Mission segment loop

From Rsewiki
Revision as of 14:23, 18 January 2020 by Jca (talk | contribs) (→‎Global checks)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Back to Robobot

Back to Robobot mission

RunMission loop

This function in the 'UMission' class is called in its own thread, once the mission app is started.

The (example) code looks like this. Some of the code is explained below.

/**
* Thread for running the mission(s)
* All missions segments are called in turn based on mission number
* Mission number can be set at parameter when starting mission command line.
* 
* The loop also handles manual override for the gamepad, and resumes
* when manual control is released.
* */
void UMission::runMission()
{ /// current mission number
 mission = fromMission;
 int missionOld = mission;
 bool regbotStarted = false;
 bool ended = false;
 bool inManual = false;
 int loop = 0;
 missionState = 0;
 int missionStateOld = missionState;
 // fixed C-string buffer for code lines etc
 const int MSL = 120;
 char s[MSL];
 /// initialize robot mission to do nothing (wait for mission lines)
 missionInit();
 /// start (the empty) mission, ready for mission snippets.
 bridge->send("start\n"); // ask REGBOT to start controlled run (ready to execute)
 bridge->send("oled 3 waiting for REGBOT\n");
 ///
 for (int i = 0; i < 3; i++)
 {
   if (not bridge->info->isHeartbeatOK())
   { // heartbeat should come at least once a second
     sleep(1);
   }
 }
 if (not bridge->info->isHeartbeatOK())
 { // heartbeat should come at least once a second
   system("espeak \"Oops, no usable connection with robot.\" -ven+f4 -s130 -a60 2>/dev/null &"); 
   bridge->send("oled 3 Oops: Lost REGBOT!");
   printf("# ---------- error ------------\n");
   printf("# No heartbeat from robot. Bridge or REGBOT is stuck\n");
   printf("# You could try restart ROBOBOT bridge ('b' from mission console) \n");
   printf("# -----------------------------\n");
   stop();
 }
 /// loop in sequenct every mission until they report ended
 while (not finished and not th1stop)
 { // stay in this mission loop until finished
   loop++;
   // test for manuel override (joy is short for joystick or gamepad)
   if (bridge->joy->manual)
   { // just wait, do not continue mission
     usleep(20000);
     if (not inManual)
       system("espeak \"Mission paused.\" -ven+f4 -s130 -a40 2>/dev/null &"); 
     inManual = true;
     bridge->send("oled 3 GAMEPAD control\n");
   }
   else
   { // in auto mode
     if (not regbotStarted)
     { // wait for start event is received from REGBOT
       // - in response to 'bot->send("start\n")' earlier
       if (bridge->event->isEventSet(33))
       { // start mission (button pressed)
         regbotStarted = true;
       }
     }
     else
     { // mission in auto mode
       if (inManual)
       { // just entered auto mode, so tell.
         inManual = false;
         system("espeak \"Mission resuming.\" -ven+f4 -s130 -a40 2>/dev/null &");
         bridge->send("oled 3 running AUTO\n");
       }
       switch(mission)
       {
         case 1: // running auto mission
           ended = mission1(missionState);
           break;
         case 2:
           ended = mission2(missionState);
           break;
         case 3:
           ended = mission3(missionState);
           break;
         case 4:
           ended = mission4(missionState);
           break;
         default:
           // no more missions - end everything
           finished = true;
           break;
       }
       if (ended)
       { // start next mission part in state 0
         mission++;
         ended = false;
         missionState = 0;
       }
       // show current state on robot display
       if (mission != missionOld or missionState != missionStateOld)
       { // update small O-led display on robot - when there is a change
         UTime t;
         t.now();
         snprintf(s, MSL, "oled 4 mission %d state %d\n", mission, missionState);
         bridge->send(s);
         if (logMission != NULL)
         {
           fprintf(logMission, "%ld.%03ld %d %d\n", 
                   t.getSec(), t.getMilisec(),
                   missionOld, missionStateOld
           );
           fprintf(logMission, "%ld.%03ld %d %d\n", 
                   t.getSec(), t.getMilisec(),
                   mission, missionState
           );
         }
         missionOld = mission;
         missionStateOld = missionState;
       }
     }
   }
   //
   // check for general events in all modes
   // gamepad buttons 0=green, 1=red, 2=blue, 3=yellow, 4=LB, 5=RB, 6=back, 7=start, 8=Logitech, 9=A1, 10 = A2
   // gamepad axes    0=left-LR, 1=left-UD, 2=LT, 3=right-LR, 4=right-UD, 5=RT, 6=+LR, 7=+-UD
   // see also "ujoy.h"
   if (bridge->joy->button[BUTTON_RED])
   { // red button -> save image
     if (not cam->saveImage)
     {
       printf("UMission::runMission:: button 1 (red) pressed -> save image\n");
       cam->saveImage = true;
     }
   }
   if (bridge->joy->button[BUTTON_YELLOW])
   { // yellow button -> make ArUco analysis
     if (not cam->doArUcoAnalysis)
     {
       printf("UMission::runMission:: button 3 (yellow) pressed -> do ArUco\n");
       cam->doArUcoAnalysis = true;
     }
   }
   // are we finished - event 0 disables motors (e.g. green button)
   if (bridge->event->isEventSet(0))
   { // robot say stop
     finished = true;
     printf("Mission:: insist we are finished\n");
   }
   else if (mission > toMission)
   { // stop robot
     // make an event 0
     bridge->send("stop\n");
     // stop mission loop
     finished = true;
   }
   // release CPU a bit (10ms)
   usleep(10000);
 }
 bridge->send("stop\n");
 snprintf(s, MSL, "espeak \"%s finished.\"  -ven+f4 -s130 -a12  2>/dev/null &", bridge->info->robotname);
 system(s); 
 printf("Mission:: all finished\n");
 bridge->send("oled 3 finished\n");
}


Initialization

When the thread is started the following initialization code is executed:

 /// initialize robot mission to do nothing (wait for mission lines)
 missionInit();
 /// start (the empty) mission, ready for mission snippets.
 bridge->send("start\n"); // ask REGBOT to start controlled run (ready to execute)

This 'missionInit();' code is repeated here

/**
* Initializes the communication with the robobot_bridge and the REGBOT.
* It further initializes a (maximum) number of mission lines 
* in the REGBOT microprocessor. */
void UMission::missionInit()
{ // stop any not-finished mission
 bridge->send("robot stop\n");
 // clear old mission
 bridge->send("robot <clear\n");
 //
 // add new mission with 3 threads
 // one (100) starting at event 30 and stopping at event 31
 // one (101) starting at event 31 and stopping at event 30
 // one (  1) used for idle and initialisation of hardware
 // the mission is started, but staying in place (velocity=0, so servo action)
 //
 bridge->send("robot <add thread=1\n");
 // Irsensor should be activated a good time before use 
 // otherwise first samples will produce "false" positive (too short/negative).
 bridge->send("robot <add irsensor=1,vel=0:dist<0.2\n");
 //
 // alternating threads (100 and 101, alternating on event 30 and 31 (last 2 events)
 bridge->send("robot <add thread=100,event=30 : event=31\n");
 for (int i = 0; i < missionLineMax; i++)
   // send placeholder lines, that will never finish
   // are to be replaced with real mission
   // NB - hereafter no lines can be added to these threads, just modified
   bridge->send("robot <add vel=0 : time=0.1\n");
 //
 bridge->send("robot <add thread=101,event=31 : event=30\n");
 for (int i = 0; i < missionLineMax; i++)
   // send placeholder lines, that will never finish
   bridge->send("robot <add vel=0 : time=0.1\n");
 usleep(10000);
 //
 //
 // send subscribe to bridge
 bridge->pose->subscribe();
 bridge->edge->subscribe();
 bridge->motor->subscribe();
 bridge->event->subscribe();
 bridge->joy->subscribe();
 bridge->motor->subscribe();
 bridge->info->subscribe();
 bridge->irdist->subscribe();
 bridge->imu->subscribe();
 usleep(10000);
 // there maybe leftover events from last mission
 bridge->event->clearEvents();
}

The code makes sure that the REGBOT is not in a mission

 bridge->send("robot stop\n");
 // clear old mission
 bridge->send("robot <clear\n");

Then it prepares 3 REGBOT threads (thread 1, 100 and 101) and fills them with a number of mission lines

Thread 1 has one line "robot <add irsensor=1,vel=0:dist<0.2\n" to make sure the IR sensor is active, sets the velocity to 0 and wait until the robot has moved 0.2m. This keeps the REGBOT busy until one of the other threads actually moves the robot. When all threads has reached the last line, then the REGBOT terminates (stops the mission).

Thread 100 and 101 are initiated with a number of lines that are placeholders for actual mission lines. Neither of the threads are activated.

The REGBOT does not support inserting mission lines, so the only way is to override existing lines.

The line "start" is sent to the REGBOT to start control loops and interpreting the mission lines just send.

Safe start

Once the REGBOT is initialized the small line-display (O-LED display)

 bridge->send("oled 3 waiting for REGBOT\n");

Then a check to see if the REGBOT is at all responding, if not, then print a clear message on the console.

 for (int i = 0; i < 3; i++)
 {
   if (not bridge->info->isHeartbeatOK())
   { // heartbeat should come at least once a second
     sleep(1);
   }
 }
 if (not bridge->info->isHeartbeatOK())
 { // heartbeat should come at least once a second
   system("espeak \"Oops, no usable connection with robot.\" -ven+f4 -s130 -a60 2>/dev/null &"); 
   bridge->send("oled 3 Oops: Lost REGBOT!");
   printf("# ---------- error ------------\n");
   printf("# No heartbeat from robot. Bridge or REGBOT is stuck\n");
   printf("# You could try restart ROBOBOT bridge ('b' from mission console) \n");
   printf("# -----------------------------\n");
   stop();
 }

Segment loop

The first part of the loop

 while (not finished and not th1stop)
 { // stay in this mission loop until finished
   loop++;
   // test for manuel override (joy is short for joystick or gamepad)
   if (bridge->joy->manual)
   { // just wait, do not continue mission
     usleep(20000);
     if (not inManual)
       system("espeak \"Mission paused.\" -ven+f4 -s130 -a40 2>/dev/null &"); 
     inManual = true;
     bridge->send("oled 3 GAMEPAD control\n");
   }
   else
   { // in auto mode
     if (not regbotStarted)
     { // wait for start event is received from REGBOT
       // - in response to 'bot->send("start\n")' earlier
       if (bridge->event->isEventSet(33))
       { // start mission (button pressed)
         regbotStarted = true;
       }
     }

The loop runs until all mission segments has reported 'finished', or the mission is terminated with a 'quit' command from the console (setting the 'th1stop' flag).

If in manual override mode (gamepad control), then this is displayed on the line display (on line 3).

Once in automatic mode, then wait for the REGBOT to report that it has started interpreting the mission lines - by emitting an event 33.

Mission segment call

The mission segment state 'mission' controls which mission to call

       switch(mission)
       {
         case 1: // running auto mission
           ended = mission1(missionState);
           break;
         case 2:
           ended = mission2(missionState);
           break;
         case 3:
           ended = mission3(missionState);
           break;
         case 4:
           ended = mission4(missionState);
           break;
         default:
           // no more missions - end everything
           finished = true;
           break;
       }
       if (ended)
       { // start next mission part in state 0
         mission++;
         ended = false;
         missionState = 0;
       }

In this example, there are 4 mission functions (the last two is just empty).

A mission will return false all the time until it is finished.

When the actual mission segment returns true, the mission segment is finished. This will increase the mission counter and set the mission state to 0 for the next mission segment.

Global checks

The mission loop is also the place for global functions that should be active regardless of the mission segment.

In this example code, there is assigned gamepad buttons to activate a few 'test' functions.

   if (bridge->joy->button[BUTTON_RED])
   { // red button -> save image
     if (not cam->saveImage)
     {
       printf("UMission::runMission:: button 1 (red) pressed -> save image\n");
       cam->saveImage = true;
     }
   }
   if (bridge->joy->button[BUTTON_YELLOW])
   { // yellow button -> make ArUco analysis
     if (not cam->doArUcoAnalysis)
     {
       printf("UMission::runMission:: button 3 (yellow) pressed -> do ArUco\n");
       cam->doArUcoAnalysis = true;
     }
   }

The red gamepad button tells the camera thread that an image should be saved to the disk. The yellow button sets another flag to start ArUco code processing.

The flag in the camera class (cam->saveImage and cam->doArUcoAnalysis) will be reset, once the process is finished.

Note that both these processes take time and prohibit other camera functions while being processed .