My Version of CreaBotLib

Fork of CreaBotLib by CreaLab

CreaBot.cpp

Committer:
sepp_nepp
Date:
2019-04-18
Revision:
12:530772639065
Parent:
11:5a94af0afa12

File content as of revision 12:530772639065:

#include "CreaBot.h"

// *****************************************************************
//    BotCommand Structure Helper
// *****************************************************************

// Struct Helper; set structure fields to values  */
void BotCommand::set(BotCmdVerb Acommand, float Aturn_angle_deg, float Adist_cm) 
{   command  = Acommand; 
    dist_cm  = Adist_cm; 
    turn_angle_deg= Aturn_angle_deg;
}; 

// Struct Helper; set structure fields to values  */
void BotCommand::set(BotCmdVerb Acommand, float Aparam) 
{   command  = Acommand; 
    dist_cm  = Aparam; 
    turn_angle_deg= Aparam;
}; 


// *****************************************************************
//    Creabot Class
// *****************************************************************

Creabot::Creabot(CreaMot *left, CreaMot *right, float diameter_wheel_cm, float bot_diam_cm):
      wheelLeft(left), wheelRight(right) 
{
    // TODO: To catch for illegal dimensions (<= 0)
    
    // Initialize wheel geometires
    wheelLeft ->setDiamCM( diameter_wheel_cm );
    wheelRight->setDiamCM( diameter_wheel_cm );
    // setup the bot parameters:
    botDiameter     = bot_diam_cm;
    ratio_wheel_bot = bot_diam_cm/diameter_wheel_cm;
    botSpeed_cm_sec = DEFAULT_SPEED_CM_SEC;

    // Attach the Crabot functions as callbacks to the wheels
    wheelLeft ->callbackSet(this, &Creabot::wheelLeftStoppedCB);   
    wheelRight->callbackSet(this, &Creabot::wheelRightStoppedCB);
    // Initialize own callback as empty
    extCallBack = NULL;

    // setup the default Directions and State Values: 
    // used below as variable instead of always passing a direction
    wheelLeft ->defaultDirection = COUNTERCLOCKWISE;
    wheelLeft ->StateValue  = LWHEEL_RUNS;
    wheelRight->defaultDirection = CLOCKWISE;
    wheelRight->StateValue = RWHEEL_RUNS;

    // Initialize the own status fields
    wheelsState  = LRWHEELS_STOP;
    qReset();
}


// *****************************************************************
//    Queue Queue management
// *****************************************************************

void Creabot::qMove(BotCmdVerb Acommand, float Aparam)
{
    qMove(Acommand, Aparam, Aparam);
}

/* Add a new movement to the queue, if not full. 
*  ATTENTION: 
*   qExecuteNext() is called asynchronsously by the motor Callback wheelsAllStoppedCB()
*   qExecuteNext() tries to read access the next command in the queue.  
*   A simultaneous read and write access to the same resources can lead to 
*   conflict and must be carefully avoided.   
*   This conflict concerns the read-modify-write access of variable Count. 
*   It is avoided by using the qBlock variable. With qBlock set during increment
*   a simultaneous read access is blocked
*   Any changes to those write and read handlers must take into account that 
*   wheelsAllStoppedCB() and qExecuteNext() may be called any time while 
*   executing qMove command. 
*/
void Creabot::qMove(BotCmdVerb Acommand, float Aturn_angle_deg, float Adist_cm)
{   if( !qIsFull() ) 
      { cmd[writeIdx].set(Acommand, Aturn_angle_deg, Adist_cm);
        writeIdx++;
        if (writeIdx==DEPTH_Queue) writeIdx=0;
        qCollide = false; // colliding access to qCollide cannot happen while qBlock = false
        // if callback happens here, there is no collosion, the last old command gets already executed. 
        qBlock = true; 
        // if callback happens here while qBlock = true, the collision is remembered in qCollide
        Count++;
        qBlock = false;
        // if callback happens here there is no more blockage and the next (maybe the new) command is already being executed
        // in this case qCollide = false, and wheelsState!=LRWHEELS_STOP
        if ( qCollide  || (wheelsState==LRWHEELS_STOP) )
            // callback cannot happen here since the wheelState is already STOP, or Collide has happened before!
            qExecuteNext();
      };
}


// Queue is worked through while still any elements inside
// ATTENTION read above consideration of conflicting accesses between qMove() and wheelsAllStoppedCB()
// Think careful if a case can occur that pExecuteNext is called twice immediately following each other, 
//   once by qMove() and once by  wheelsAllStoppedCB() ???
void Creabot::qExecuteNext()
{   if ( !qIsEmpty() ) //  see if something in the queue to execute
      { // Execute and then remove the oldest element at the tail of the Queue
        iExeCommand( &cmd[readIdx] );
        readIdx++;
        if (readIdx==DEPTH_Queue) readIdx=0;
        Count--;
      };
}

// Immediately end All, the queue and the motor
void Creabot::qStopAll()
{   qReset(); // empty the queue
    iStop(); // stop the motors, do not call external callback
}    


//**************************************************************************
//    Using the BotCmdVerb to encode different commands, done immediately
//**************************************************************************

void Creabot::iMoveAndWait(BotCmdVerb moveType, float Aparam)
{   iMove(moveType,Aparam,Aparam);
    iWaitEnd();
}

void Creabot::iMoveAndWait(BotCmdVerb moveType, float Aturn_angle_deg, float Adist_cm)
{   iMove(moveType,Aturn_angle_deg,Adist_cm);
    iWaitEnd();
}

void Creabot::iMove(BotCmdVerb moveType, float Aparam)
{   iMove(moveType, Aparam, Aparam);   }

void Creabot::iMove(BotCmdVerb moveType,  float Aturn_angle_deg, float Adist_cm)
{   BotCommand Anew_cmd;
    Anew_cmd.set(moveType, Aturn_angle_deg, Adist_cm);
    iExeCommand(&Anew_cmd);
}

/** High level, immediate: move bot according to prefilled command structures.
* Recommended to use iMove() methods to fill the command structure correctly. 
* Branches to the moveXXXX() methods. For details see docs for those methods. 
* Warning: Collides with queued commands if called individually. 
* @param[in] <*cmd> Pointer to type BotCommand, the prefilled command structure,*/
void Creabot::iExeCommand(BotCommand *cmd) {
          switch (cmd->command) {
            case FORWARD:
                moveForward(cmd->dist_cm);
                break;
            case BACKWARD:
                moveBackward(cmd->dist_cm);
                break;
            case LEFT:
                moveLeft(cmd->turn_angle_deg, cmd->dist_cm);
                break;
            case RIGHT:
                moveRight(cmd->turn_angle_deg, cmd->dist_cm);
                break;
            case BACKLEFT:
                moveBackLeft(cmd->turn_angle_deg, cmd->dist_cm);
                break;
            case BACKRIGHT:
                moveBackRight(cmd->turn_angle_deg, cmd->dist_cm);
                break;
            case ROTATE:
                moveRotate(cmd->turn_angle_deg);
                break;
            default:
                break;
        };
}; // executeCommand

/* spends all time with waits, returns only once wheelState = LRWHEELS_STOP  */
void Creabot::iWaitEnd()
{   while(wheelsState!=LRWHEELS_STOP) wait(0.1);   }

/*  spends all time with waits, returns only once wheelState = LRWHEELS_STOP,
  but maximum  max_wait_ms milli seconds */
void Creabot::iWaitEnd(uint32_t max_wait_ms)
{   uint32_t waited=0;
    while( (wheelsState!=LRWHEELS_STOP) &&  (waited < max_wait_ms) ) 
      { wait(0.1);  waited += 100; }
} // botWaitEnd

void Creabot::iStop()
{   // Call the StopRun method, so wheels detach their tickers
    wheelLeft->StopRun();
    wheelRight->StopRun();
    // wheelsAllStoppedCB could handle the rest of the cleanup, but would also trigger external callback
    // Turn motor power off!
    wheelLeft->MotorOFF();
    wheelRight->MotorOFF();
    wheelsState = LRWHEELS_STOP;
}

// *****************************************************************
//   Creabot Mid level control functions:  
//     all directly access the wheel, no Queue */ 
// *****************************************************************

// set botSpeed_cm_sec parameter for next high level commands 
void Creabot::setSpeed(float AbotSpeed_cm_sec)
{   if (AbotSpeed_cm_sec > MAX_SPEED_CM_SEC) botSpeed_cm_sec = MAX_SPEED_CM_SEC;
        else if (AbotSpeed_cm_sec<MIN_SPEED_CM_SEC) botSpeed_cm_sec = MIN_SPEED_CM_SEC;
        else botSpeed_cm_sec = AbotSpeed_cm_sec;
    // Low level setting of wheel speed is done inside SetMotsSpeed: 
}

// Mid level control function:  advance bot straight forward for a given distance
void Creabot::moveForward(float dist_cm)
{   
    wheelsSetSpeed(botSpeed_cm_sec);
    wheelRight->RunDist_cm(dist_cm );
    wheelLeft ->RunDist_cm(dist_cm ); // Direction = defaultDirection
    // negative distances are run by RunDist_cm in opposite direction!
    wheelsState = LRWHEELS_RUN;
}

// Mid level control function:  reverse bot straight backwards for a given distance
void Creabot::moveBackward(float dist_cm)
{
    wheelsSetSpeed(botSpeed_cm_sec);
    wheelRight->RunDist_cm(-dist_cm);
    wheelLeft ->RunDist_cm(-dist_cm);// Direction = defaultDirection
    // negative distance are run by RunDist_cm in opposite direction!
    wheelsState = LRWHEELS_RUN;
}

/* Mid level control function: turn bot forward right, 
   around a radius equl the bot size; i.e. turn around the right wheel */
void Creabot::moveRight(float turn_angle_deg)
{
    wheelLeft->setSpeed_cm_sec(botSpeed_cm_sec);
    wheelLeft->RunTurnAngle(turn_angle_deg,botDiameter);
    wheelsState = LWHEEL_RUNS;
}

/* Mid level control function: turn bot forward right, 
around a radius that is turn_radius_cm away from the right wheel*/
void Creabot::moveRight(float turn_angle_deg, float turn_radius_cm)
{   // If Center_cm is too small the right wheel has practically nothing to do ;)
    if (fabs(double(turn_radius_cm))<0.1) { moveRight(turn_angle_deg); } 
    else {
        wheelLeft ->setSpeed_cm_sec(botSpeed_cm_sec);
        wheelRight->setSpeed_cm_sec(botSpeed_cm_sec * turn_radius_cm/turn_radius_cm+botDiameter);
        wheelRight->RunTurnAngle(turn_angle_deg,turn_radius_cm            );
        wheelLeft ->RunTurnAngle(turn_angle_deg,turn_radius_cm+botDiameter);// Direction = defaultDirection
        // negative distance are run by RunTurnAngle in opposite direction!
        wheelsState = LRWHEELS_RUN;
      } // else
}

/* Mid level control function: turn bot backwards Right, 
   around a radius that is turn_radius_cm away from the Right wheel */
void Creabot::moveBackRight(float turn_angle_deg, float turn_radius_cm)
{ moveRight( -turn_angle_deg , turn_radius_cm); }

/* Mid level control function: turn bot backwards Right, 
   around a radius equal to the bot size; = the Right wheel stands still */
void Creabot::moveBackRight(float turn_angle_deg)
{ moveRight( - turn_angle_deg); }

/* Mid level control function: turn bot forward left, 
   around a radius equal to the bot size; = the left wheel stands still */
void Creabot::moveLeft(float turn_angle_deg)
{
    wheelRight->setSpeed_cm_sec(botSpeed_cm_sec);
    wheelRight->RunTurnAngle(turn_angle_deg,botDiameter);
    wheelsState = RWHEEL_RUNS;
}

/* Mid level control function: turn bot forward left, 
   around a radius that is turn_radius_cm away from the left wheel */
void Creabot::moveLeft(float turn_angle_deg, float turn_radius_cm)
{   // If Center_cm is too small the left wheel has practically nothing to do ;)
    if (fabs(double(turn_radius_cm))<0.1) { moveLeft(turn_angle_deg); } 
    else {
        wheelLeft -> setSpeed_cm_sec(botSpeed_cm_sec * turn_radius_cm/(turn_radius_cm+botDiameter));
        wheelRight-> setSpeed_cm_sec(botSpeed_cm_sec);
        wheelLeft -> RunTurnAngle(turn_angle_deg,turn_radius_cm);
        wheelRight-> RunTurnAngle(turn_angle_deg,turn_radius_cm+botDiameter);// Direction = defaultDirection
        // negative distance are run by RunTurnAngle in opposite direction!
        wheelsState = LRWHEELS_RUN;
        }
}

/* Mid level control function: turn bot backwards left, 
   around a radius that is turn_radius_cm away from the left wheel */
void Creabot::moveBackLeft(float turn_angle_deg, float turn_radius_cm)
{ moveLeft( - turn_angle_deg, turn_radius_cm); }

/* Mid level control function: turn bot backwards left, 
   around a radius equal to the bot size; = the left wheel stands still */
void Creabot::moveBackLeft(float turn_angle_deg)
{ moveLeft( - turn_angle_deg); }

/* Mid level control function: turn bot on its place for a given angle.
positive angles turn right, negative angles turn left*/
void Creabot::moveRotate(float angleBot)
{
    float angleWheel = angleBot*ratio_wheel_bot;
    setSpeed(botSpeed_cm_sec);
    if(angleBot<0) {
        wheelRight->RunDegrees( angleWheel);
        wheelLeft ->RunDegrees(-angleWheel);// Direction = defaultDirection
        // negative distance are run by RunDegrees in opposite direction!
    } else {
        wheelLeft ->RunDegrees( angleWheel);
        wheelRight->RunDegrees(-angleWheel);// Direction = defaultDirection
        // negative distance are run by RunDegrees in opposite direction!
    }
    wheelsState = LRWHEELS_RUN;
}

//******************************************************************************
// low level wheel functions, only filters for rot_angle_deg>0, then passes 
// to wheel Class; and remembers that a wheel moves
//******************************************************************************

/* Low level access functions: sets both wheel speeds immediately  */ 
void Creabot::wheelsSetSpeed(float mot_speed_cm_sec)
{
    wheelLeft ->setSpeed_cm_sec(mot_speed_cm_sec);
    wheelRight->setSpeed_cm_sec(mot_speed_cm_sec);
}

//******************************************************************************
// low level wheel functions, handles callbacks from wheels
// *****************************************************************************

void Creabot::wheelLeftStoppedCB()
{   wheelsState = TWheelsState( wheelsState & LWHEEL_RUNS );  // mask out Left Wheel status bit
    if (wheelsState == LRWHEELS_STOP) // all wheels are now known to have stopped
        wheelsAllStoppedCB();
}

void Creabot::wheelRightStoppedCB()
{   wheelsState = TWheelsState( wheelsState & RWHEEL_RUNS );  // mask out Left Wheel status bit
    if (wheelsState == LRWHEELS_STOP) // all wheels are now known to have stopped
        wheelsAllStoppedCB();
}

void Creabot::wheelsAllStoppedCB()
{   // assume that already at entry wheelsState == LRWHEELS_STOP
    // Try to execute next entry in queue, if one is present
    if ( !qBlock ) 
      { qExecuteNext();// not blocked, so can execute now
        // if wheels are still STOPed, that means no command to execute was left in the queue 
        if (wheelsState==LRWHEELS_STOP) 
          { // Turn motor power off!
            wheelLeft->MotorOFF();
            wheelRight->MotorOFF();
            // Call the externall callback function to inform about the end of all movements
            if(extCallBack!=NULL) extCallBack( wheelsState );
          } // if wheelsState==LRWHEELS_STOP
      } // if ( !qBlock ) 
      else qCollide = true; // Remember the collision to execute the next command. 
}