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

asked on

[C, C++, IPC, LibSerial, fork()] How to execute two processes in C

I'm having a problem with my program flow because I'm trying to receive information from a blocking function.
I'm trying to control a mobile robot in C by using a blue-tooth joy-pad and two C program which communicates over a serial port: a program control joy-pad and servos, the other one controls the sonars. If sonars detect a collision, then servos have to stop immediately.

I try to explain better what my problem is.
At the moment, my program flow is:

1. Open Serial port
2. Send and Receive information over the serial port
3. Load a blue-tooth joy-pad with robot_move()
4. Every time there is an input from the joypad, call send_event() which is a function to move servos and in parallel trying to receive information about collision with another function check_coll() which reads these data from the serial port.

The problem is that check_coll() uses a blocking read to acquire data on the serial port, so i'm not able to send commands to my servos because every time i have to wait for check_coll().

I was thinking to use fork() to start two process: the first for the send_event() function and the second for the check_coll() function; then, when there is a collision, check_coll() sends a signal to send_event() to stop the servos.

Do you think it is possible?
It's a little bit difficult to do this for me, but i'd like to know if this can be a solution.

Or may be, is there a more simple solution just editing the while loop inside the send_event function?

This is the code (it's too long, i posted only the important parts) I'm using for the moment:

int send_event(int fd, __u16 event_type, __u16 code, __s32 value) {
	struct input_event event;
	memset(&event, 0, sizeof(event));

	gettimeofday(&event.time, NULL);
	event.type = event_type;
	event.code = code;
	event.value = value;
       
        while(!check_coll() ){ <-- this while() does not work very well because it wait for data

  	if(code == 48){
        // servo command routine
        }
        else if(code == 30){
         // servo command routine
      	} 	
        else if(code == 46){
           // servo command routine
	} 
        else if(code == 103 && (value == 0 || value == 1)){
        printf("UP\n"); 
           // servo command routine
	}
        else if(code == 108 && (value == 0 || value == 1)){
        printf("DOWN\n"); 
       // servo command routine
	} 
        else if(code == 105 && (value == 0 || value == 1)){
        printf("LEFT\n"); 
         // servo command routine
	} 
        else if(code == 106 && (value == 0 || value == 1)){
        printf("RIGHT\n"); 
        // servo command routine
	} 
}
	return 1;
}

std::map<std::string, std::string> result;
std::pair<std::string, std::string> splitAtFirst(const std::string& str, const char* delimiters)
{
	std::string::size_type i = str.find_first_of(delimiters);
	if(i != std::string::npos)
	{
		return std::pair<std::string, std::string>(
			str.substr(0, i), str.substr(i + 1)
			);
	}
	return std::pair<std::string, std::string>(str, std::string());
}

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;
   
}
while(true);

}

std::string s_read(SerialStream& serial_port)
{
     std::cout << "Test Before While Loop\n";
     std::string result;
     std::cout << "Serial available:" << serial_port.rdbuf()->in_avail() << "\n\n";
     while( serial_port.rdbuf()->in_avail() > 0 )
     {
         char next_byte;
         serial_port.get(next_byte);  
         
         result += next_byte;
     }
  
     return result;
}

std::string s_readBlocking(SerialStream& serial_port, int timeout)
{
     while( serial_port.rdbuf()->in_avail() == 0 || timeout > 0 )
     {
         timeout -= 100;
         usleep(100);
     }
     return s_read(serial_port);
}

void s_write(SerialStream& serial_port, const std::string& data)
{
    serial_port.write(data.c_str(), data.size());
}

int
main( int    argc,
       char** argv  )
{
     //
     // Open the serial port.
     //
     std::map<std::string, std::string> Commands;
     
     char c;
     serial_port.Open( "/dev/ttyACM0" ) ;
     if ( ! serial_port.good() )
     {
         std::cerr << "[" << __FILE__ << ":" << __LINE__ << "] "
                   << "Error: Could not open serial port."
                   << std::endl ;
         exit(1) ;
     }
     //
     // Set the baud rate of the serial port.
     //
     serial_port.SetBaudRate( SerialStreamBuf::BAUD_CONFIG ) ;
     if ( ! serial_port.good() )
     {
         std::cerr << "Error: Could not set the baud rate." <<  
std::endl ;
         exit(1) ;
     }
     //
     // Set the number of data bits.
     //
     serial_port.SetCharSize( SerialStreamBuf::CHAR_SIZE_8 ) ;
     if ( ! serial_port.good() )
     {
         std::cerr << "Error: Could not set the character size." <<  
std::endl ;
         exit(1) ;
     }
     //
     // Disable parity.
     //
     serial_port.SetParity( SerialStreamBuf::PARITY_NONE ) ;
     if ( ! serial_port.good() )
     {
         std::cerr << "Error: Could not disable the parity." <<  
std::endl ;
         exit(1) ;
     }
     //
     // Set the number of stop bits.
     //
     serial_port.SetNumOfStopBits( 1 ) ;
     if ( ! serial_port.good() )
     {
         std::cerr << "Error: Could not set the number of stop bits."
                   << std::endl ;
         exit(1) ;
     }
     //
     // Turn off hardware flow control.
     //
     serial_port.SetFlowControl( SerialStreamBuf::FLOW_CONTROL_NONE ) ;
     if ( ! serial_port.good() )
     {
         std::cerr << "Error: Could not use hardware flow control."
                   << std::endl ;
         exit(1) ;
     }
     //
     // Do not skip whitespace characters while reading from the
     // serial port.
     //
     // serial_port.unsetf( std::ios_base::skipws ) ;
     //
     // Wait for some data to be available at the serial port.
     //
     //
     // Keep reading data from serial port and print it to the screen.
     //

usleep(INITIAL_DELAY);

first_start = s_readBlocking(serial_port, TIME_DELAY);

std::cout << "First start: " << first_start << "\n";

if (first_start.compare("SOL_STR")){

cout << "OK, checking sensors\n";

s_write(serial_port, "check"); // First Command

first_reply = s_readBlocking(serial_port, TIME_DELAY);

std::cout << "First reply: " << first_reply << "\n";

parseCommand(first_reply);

// check for sonar integrity
std::map<std::string,  std::string>::const_iterator sonar_front = result.find("sonar-front");
std::map<std::string,  std::string>::const_iterator sonar_left = result.find("sonar-left");
std::map<std::string,  std::string>::const_iterator sonar_right = result.find("sonar-right");

if(sonar_front != result.end() && sonar_left != result.end() && sonar_right != result.end())
{ 
  
  std::cout << "Value for sonar-front is " << sonar_front->second << "\n";
  std::cout << "Value for sonar-left is " << sonar_left->second << "\n";
  std::cout << "Value for sonar-right is " << sonar_right->second << "\n";
  
  if ( (sonar_front->second == "ok") && (sonar_left->second == "ok") && (sonar_right->second == "ok")){
  
  std::cout << "Sonar OK\n";
  s_write(serial_port, "system-start");
  robot_move(); <-- NOW IT LOADS THE BT JOYPAD
  
  }
  else {
  std::cout << "Attention: sensor error!\n";
  }
}

std::map<std::string,  std::string>::const_iterator i = result.begin();

for(;i!=result.end(); ++i)
{
  std::cout << "Key is " << i->first;
  std::cout << " Value is " << i->second << "\n";
}

}

     return EXIT_SUCCESS ;
}

void robot_move{
// call send_event() every time it receives an input from the joy-pad
}

int check_coll(){ <-- IT USES A BLOCKING READ
int collisions = 0;
while(1){
string detect_collisions = s_readBlocking(serial_port, TIME_DELAY);

parseCommand(detect_collisions);

// check for collisions
std::map<std::string,  std::string>::const_iterator sonar_collisions = result.find("sonar-collision");
if(sonar_collisions != result.end() && (sonar_collisions->second == "ok"))
{ 
  collisions++;
  std::cout << "STOP!\nCollisions " << sonar_collisions->second << " e count= " << collisions << "\n";
  return 1;
}
else return 0;
}

}

Open in new window

ASKER CERTIFIED 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
Avatar of ichigokurosaki

ASKER

Thanks for your suggestion!

It is ok for me to add the checking of the linked list in the send_event() function, but it's a little bit hard to me to figure out how to implement the thread solution.
I'll try to find documents about threading in C on the net in order to better understand how to use it.

I'll post in this topic if i'll have problems.
it is not difficult:

the thread function could be a static member function of your class. it would look like

// that member function must be declared static in yourclass
void yourclass::thread_start(void *arg)
 {
         // as it is static you don't have this pointer 
         // but you "get" a this by casting the argument 
         yourclass  * pthis = (yourclass *) arg;
         // terminated should return false when the thread should end
         while (!pthis->terminated())
         {
               // do here the check
         }
}

Open in new window


then define

pthread_t thread;
pthread_attr_t attr;
pthread_mutex_t mutex;

Open in new window



as private members in your class.

then you could start the thread in a non-member function like

pthread_mutex_init(&mutex);
pthread_attr_init(&attr);
pthread_create(&thread,  &attr, &yourclass::thread_start, this);

Open in new window


Sara
Sara, thanks for your example!
I'm not very good with classes in c++, so your code is a little bit difficult to implement.

I tried with this:
class Thread1
{
   public:
      Thread1();
      void thread_start(void * arg);
  
   private:
      pthread_t thread;
      pthread_attr_t attr;
      pthread_mutex_t mutex;
};

// that member function must be declared static in yourclass
void Thread1::thread_start(void *arg)
 {
         // as it is static you don't have this pointer 
         // but you "get" a this by casting the argument 
         Thread1  * pthis = (Thread1 *) arg;
         // terminated should return false when the thread should end
         while (!pthis->terminated())
         {
               // do here the check
         }
}              

// then in the main() after the robot_move() function:

  robot_move();
  pthread_mutex_init(&mutex);
  pthread_attr_init(&attr);
  pthread_create(&thread,  &attr, &Thread1::thread_start, this);

Open in new window


but g++ gives me errors:
libserial_zeemote_thread.cpp: In member function ‘void Thread1::thread_start(void*)’:
libserial_zeemote_thread.cpp:86:25: error: ‘class Thread1’ has no member named ‘terminated’
libserial_zeemote_thread.cpp: In function ‘int main(int, char**)’:
libserial_zeemote_thread.cpp:518:23: error: ‘mutex’ was not declared in this scope
libserial_zeemote_thread.cpp:519:22: error: ‘attr’ was not declared in this scope
libserial_zeemote_thread.cpp:520:19: error: ‘thread’ was not declared in this scope
libserial_zeemote_thread.cpp:520:59: error: invalid use of ‘this’ in non-member function

Open in new window


I tried to use boost libraries to do the same thing, but i realised that when the new started thread does not inherit the previous functions, so i do not know how to read the serial port since I can't call any of the previous declared functions.

With boost libraries i did this:
void check_coll(){

while(1){

std::string detect_collisions = s_readBlocking(serial_port, TIME_DELAY);

parseCommand(detect_collisions);

// check for collisions
std::map<std::string,  std::string>::const_iterator sonar_collisions = result.find("sonar-collision");
if(sonar_collisions != result.end() && (sonar_collisions->second == "ok"))
{ 
  collisions ++;
  std::cout << "STOP!\nCollisions " << sonar_collisions->second << " e count= " << collisions << "\n";
  
}

}  
// then in the main()..            
  robot_move();
  boost::thread collisions_thread1(&check_coll);

Open in new window

The problem is that in this case, g++ tells me:
libserial_zeemote_originale.cpp: In function ‘void check_coll()’:
libserial_zeemote_originale.cpp:71:71: error: ‘s_readBlocking’ was not declared in this scope
libserial_zeemote_originale.cpp:73:31: error: ‘parseCommand’ was not declared in this scope
libserial_zeemote_originale.cpp:76:72: error: ‘result’ was not declared in this scope
libserial_zeemote_originale.cpp:79:3: error: ‘collisions’ was not declared in this scope

Open in new window


Please, can you give me some further input? :(

Thanks a lot!
Finally, I was able to load the joy-pad in a new thread with boost libraries.

So, at the moment, i have a infinitive while loop in the main function and i load the joypad in the new thread by sending commands to the servos.
  boost::thread collisions_thread1(&robot_move); 
  check_coll();

Open in new window


The problem is that if i set an integer variable to 1 in the check_coll() when a collision occurs, than i'm not able to read its value in the robot_move() function which is in another thread.
In fact, when the int variable is 1 in check_coll(), it is ZERO in robot_move().

What can i do in order to use the integer variable in the new thread?

The same error if i write the collision status in the linked list.
Thanks for the tip about the thread! :)
libserial_zeemote_thread.cpp: In member function ‘void Thread1::thread_start(void*)’:
libserial_zeemote_thread.cpp:86:25: error: ‘class Thread1’ has no member named ‘terminated’

to solve that add a member 'stopped' to class YourApp (note Thread1 is a bad class name cause the object is the main thread which normally is the application object) and two public member functions:

class YourApp
{
     bool stopped;  // initialize to false in constructor YourApp()
     ...
public:
     bool terminated() { return stopped; }
     void set_terminated() { stopped = true; }
     ...

Open in new window


libserial_zeemote_thread.cpp: In function ‘int main(int, char**)’:
libserial_zeemote_thread.cpp:518:23: error: ‘mutex’ was not declared in this scope
libserial_zeemote_thread.cpp:519:22: error: ‘attr’ was not declared in this scope
libserial_zeemote_thread.cpp:520:19: error: ‘thread’ was not declared in this scope

to solve these errors you would add a function 'run' to YourApp

class YourApp
{
   public:
      YourApp();
      // a static function is a class function and needs no instance of YourApp
      // therefore you can pass it to pthread_create via function pointer
      static void thread_start(void * arg);
      int run();    
   private:
      pthread_t thread;
      pthread_attr_t attr;
      pthread_mutex_t mutex;
};

...
int main()
{
      YourApp theApp;
      int ret = theApp.run();
      return ret;
}

Open in new window


in YourApp::run you have access on private members thread, attr, mutex.


libserial_zeemote_thread.cpp:520:59: error: invalid use of ‘this’ in non-member function

in 'static void thread_start' function you have no access to non-static members like 'run()' or 'mutex' cause by keyword static thread_start is a global function. to solve that problem you would call pthread_create from run member function (which has a 'this' pointer) and pass 'this' as void* argument to pthread_create.the pthread_create would create the thread and start the start function passing the 'this' as 'arg'. in thread function you now could cast the 'arg' to a pointer of type YourApp (i named it 'pThis') which you then could use to call member functions of the original YourApp object.  

if you prefer the boost thread approach you would need to use a shared (global) variable for communication between the threads.

class Thread1
{
  public: 
        static int shared;
  ....

Open in new window


here the variable 'shared' would be accessable from everywhere by Thread1::shared, for example

// main thread

    while (Thread1::shared == 0)
    {
           ....

// worker thread
    if (collission_detected())
        Thread1::shared = 1;

Open in new window


Sara