/// <summary>
        /// performs a turn and sets the speed before exiting, may leave robot moving straight.
        /// exits immediately after the turn is completed (or interrupted).
        /// </summary>
        /// <param name="tamp">contains turn angle and desired speed</param>
        /// <param name="onComplete">handler to be called after the turn is completed and the speed is set</param>
        /// <returns></returns>
        protected IEnumerator<ITask> TurnAndMoveForward(TurnAndMoveParameters tamp, Handler onComplete)
        {
            LogInfo("DriveBehaviorServiceBase: TurnAndMoveForward() Started" + currentCompass);

            bool lastOpSuccess = true;   // must be true in case translate back is not needed

            _state.MovingState = MovingState.InTransition;
            // onComplete handler may set MovingState to whatever appropriate. We can set MovingState.Unknown on any error or interruption, and at the end to tamp.desiredMovingState
            lastInTransitionStarted = DateTime.Now;

            Talker.Say(2, "Turn " + (tamp.rotateAngle > 0 ? "Right " : "Left ") + Math.Abs(tamp.rotateAngle) + " And Move Forward");

            LogInfo(LogGroups.Console, "Turning " + (tamp.rotateAngle > 0 ? "right " : "left ") + Math.Abs(tamp.rotateAngle) + " degrees,  power=" + tamp.rotatePower + " - and move forward at speed=" + tamp.speed);

            tamp.success = false;
            Fault fault = null;
            int rotateAngle = tamp.rotateAngle;
            double? desiredHeading = Direction.to360(_mapperVicinity.robotDirection.heading + rotateAngle);

            CollisionState collisionState = _state.collisionState;

            if (Math.Abs(rotateAngle) > 45 && collisionState != null && collisionState.canMoveBackwards)
            {
                LogInfo("Steep Turn " + rotateAngle + " - backing up some...");
                // back up a bit to allow some front space for the turn:

                yield return Arbiter.Choice(
                    Translate(-backupDistanceSteepTurn, tamp.speed),
                    delegate(DefaultUpdateResponseType response) { lastOpSuccess = true; },
                    delegate(Fault f) {
                        lastOpSuccess = false;
                        _state.MovingState = MovingState.Unknown;
                        fault = f; }
                );

                // If the Translate was accepted, then wait for it to complete.
                // It is important not to wait if the request failed.
                if (lastOpSuccess)
                {
                    DriveStageContainer driveStage = new DriveStageContainer();
                    yield return WaitForCompletion(driveStage);
                    LogInfo("WaitForCompletion() returned: " + driveStage.DriveStage);

                    lastOpSuccess = driveStage.DriveStage == drive.DriveStage.Completed;

                    if (!lastOpSuccess)
                    {
                        _state.MovingState = MovingState.Unknown;
                    }
                }
                else
                {
                    _state.MovingState = MovingState.Unknown;
                    LogError("Error occurred in TurnAndMoveForward() on Backup Sharp Turn: " + fault);
                }

                _mapperVicinity.robotState.leftPower = 0.0d;
                _mapperVicinity.robotState.rightPower = 0.0d;
            }

            if (lastOpSuccess)   // either we didn't need to backup, or backup completed successfully.
            {
                if (Math.Abs(rotateAngle) > 3)
                {

                    int tryCount = 0;

                    while (lastOpSuccess && Math.Abs(rotateAngle) > 3 && ++tryCount < 3)
                    {
                        LogInfo(LogGroups.Console, "Turning " + (tryCount==1?"start":"retry") + ": current heading=" + _mapperVicinity.robotDirection.heading + "   desiredHeading=" + desiredHeading + "   rotateAngle=" + rotateAngle);

                        yield return Arbiter.Choice(
                            TurnByAngle(rotateAngle, tamp.rotatePower),
                            delegate(DefaultUpdateResponseType response)
                            {
                                LogInfo("success in TurnByAngle()" + currentCompass);
                                lastOpSuccess = true;
                            },
                            delegate(Fault f)
                            {
                                LogInfo("fault in TurnByAngle()");
                                lastOpSuccess = false;
                                _state.MovingState = MovingState.Unknown;
                                fault = f;
                            }
                        );

                        // If the RotateDegrees was accepted, then wait for it to complete.
                        // It is important not to wait if the request failed.
                        if (lastOpSuccess)
                        {
                            TrackRoamerBehaviorsState state = _state;
                            state.IsTurning = true;    // can't trust laser while turning
                            state.LastTurnStarted = state.LastTurnCompleted = DateTime.Now;     // reset watchdog

                            DriveStageContainer driveStage = new DriveStageContainer();
                            yield return WaitForCompletion(driveStage);
                            LogInfo("WaitForCompletion() returned: " + driveStage.DriveStage);

                            lastOpSuccess = driveStage.DriveStage == drive.DriveStage.Completed;

                            if (lastOpSuccess)
                            {
                                if (_mapperVicinity.turnState != null)
                                {
                                    _mapperVicinity.turnState.finished = DateTime.Now;
                                    _mapperVicinity.turnState.hasFinished = true;
                                }
                            }
                            else
                            {
                                if (_mapperVicinity.turnState != null)
                                {
                                    _mapperVicinity.turnState.finished = DateTime.Now;
                                    _mapperVicinity.turnState.hasFinished = true;
                                    _mapperVicinity.turnState.wasCanceled = true;
                                }
                                LogInfo("op failure");
                                state.MovingState = MovingState.Unknown;
                            }

                            state.IsTurning = false;
                            state.LastTurnCompleted = DateTime.Now;

                            if (tamp.speed != 0 && lastOpSuccess)
                            {
                                // a fire-and-forget command to move forward:
                                LogInfo("calling SetDriveSpeed()");
                                SetDriveSpeed(tamp.speed, tamp.speed);
                            }
                            else
                            {
                                // make sure we display zero power if SetDriveSpeed() was not called:
                                _mapperVicinity.robotState.leftPower = 0.0d;
                                _mapperVicinity.robotState.rightPower = 0.0d;
                            }

                            rotateAngle = (int)Direction.to180(desiredHeading - _mapperVicinity.robotDirection.heading);

                            LogInfo(LogGroups.Console, "Turning end: current heading=" + _mapperVicinity.robotDirection.heading + "   desiredHeading=" + desiredHeading + "   remains rotateAngle=" + rotateAngle);

                        }
                        else
                        {
                            lastOpSuccess = false;
                            _state.MovingState = MovingState.Unknown;
                            LogError("Error occurred in TurnAndMoveForward() on TurnByAngle: " + fault);
                        }
                    }
                }
                else
                {
                    // no need to turn (angle too small) - just set the speed:
                    _state.IsTurning = false;
                    if (tamp.speed != 0)
                    {
                        // a fire-and-forget command to move forward, if speed is specified:
                        LogInfo("calling SetDriveSpeed()");
                        SetDriveSpeed(tamp.speed, tamp.speed);
                    }
                }
            }

            // we come here with MovingState.InTransition or MovingState.Unknown
            tamp.success = lastOpSuccess;
            _state.MovingState = lastOpSuccess ? tamp.desiredMovingState : MovingState.Unknown;   // that's for now, onComplete may set it to whatever appropriate

            LogInfo("calling onComplete()");
            onComplete();   // check tamp.success, will be false if DriveStage.Cancel or other interruption occured.

            // done
            yield break;
        }
Пример #2
0
        /// <summary>
        /// Stops the robot. If the robot was going forward it backs up.
        /// Keep in mind that a "hardware" stop has already been initiated in the Power Brick, so by the time we get here wheels are stopped.
        /// </summary>
        protected void Bumped(bool leftWhiskerPressed, bool rightWhiskerPressed, bumper.ContactSensorArrayState bumpersState)
        {
            LogInfo("TrackRoamerBehaviorsService: Bumped()   _state.Velocity=" + _state.Velocity + "   _state.MovingState=" + _state.MovingState);

            string whatIsBumped;
            int turnFactor; // = btRand.NextDouble() > 0.5d ? 1 : -1;

            if (leftWhiskerPressed && rightWhiskerPressed)
            {
                whatIsBumped = "Both whiskers";
                turnFactor = 0;    // straight back
            }
            if (leftWhiskerPressed)
            {
                whatIsBumped = "Left whisker";
                turnFactor = -1;    // turning a bit to the right, to avoid obstacle to the left
            }
            else if (rightWhiskerPressed)
            {
                whatIsBumped = "Right whisker";
                turnFactor = 1;    // turning a bit to the left, to avoid obstacle to the right
            }
            else
            {
                whatIsBumped = "Proximity array";
                turnFactor = btRand.NextDouble() > 0.5d ? 1 : -1;
            }

            Talker.Say(3, "bumped " + whatIsBumped);

            if (!_testBumpMode && _state.Velocity < 0)
            {
                // we are moving backwards,
                // front whiskers ignored when we move backwards. Others cause immediate stop:
                if (isSensingRearObstruction(leftWhiskerPressed, rightWhiskerPressed, bumpersState))
                {
                    LogInfo("TrackRoamerBehaviorsService: Bumped() - only Rear proximity sensors pressed while moving backwards, stopping...");

                    // either a rear bumper or both front and rear
                    // bumpers are pressed. STOP!
                    StopTurning();
                    //StopMoving();

                    // whatever it was, we didn't expect it. Let higher level decision-making take over, may be look around, do mapping:
                    _state.MovingState = MovingState.Unknown;
                    _state.Countdown = 3;
                }
                else
                {
                    LogInfo("TrackRoamerBehaviorsService: Bumped() while moving backwards, whisker press ignored (robot stopped anyway)");

                    Talker.Say(4, whatIsBumped + " ignored");
                    // well, motors are stopped and we are not getting completion by encoders.
                    _state.MovingState = MovingState.Unknown;
                    _state.Countdown = 3;
                }
            }
            else
            {
                // we are moving forward, or in test mode.
                // _testBumpMode always ends here - even if we are stationary or moving backwards
                if (_state.MovingState != MovingState.BumpedBackingUp)
                {
                    _state.MovingState = MovingState.BumpedBackingUp;
                    lastBumpedBackingUpStarted = DateTime.Now;

                    int angle = BackupAngleDegrees * turnFactor;

                    Tracer.Trace("TrackRoamerBehaviorsService: Bumped() - " + whatIsBumped + " pressed, backing up by " + (-BackupDistanceMm) + " mm  turning " + angle + " degrees");

                    Talker.Say(4, "backing up");

                    // only a front bumper is pressed.
                    // move back <BackupDistance> mm;

                    TurnAndMoveParameters tamp = new TurnAndMoveParameters()
                    {
                        distance = BackupDistanceMm,
                        speed = (int)Math.Round(ModerateBackwardVelocityMmSec),
                        rotatePower = ModerateTurnPower,
                        rotateAngle = angle
                    };

                    Port<bool> completionPort = BackUpTurnWait(tamp);

                    // start movement
                    Activate(Arbiter.Receive(false, completionPort,
                            delegate(bool b)
                            {
                                LogInfo("TrackRoamerBehaviorsService: Bumped() delegate - ++++++++++++ BackUpTurn done ++++++++++++++++++++++++++++++++++++++++++++++++++++");
                                Talker.Say(4, "done backing up");

                                // done backing up; let the decision making process take over:
                                _state.MovingState = MovingState.Unknown;
                                _state.Countdown = 3;
                            }
                        )
                    );
                    // exiting here with MovingState.BumpedBackingUp, while the delegate waits for completion.
                }
                else
                {
                    Talker.Say(4, "whisker press ignored");
                    // well, motors are stopped and we are not getting completion by encoders.
                    _state.MovingState = MovingState.Unknown;
                    _state.Countdown = 3;
                }
            }

            LogInfo("TrackRoamerBehaviorsService: Bumped()  exiting - _state.MovingState=" + _state.MovingState);
        }
        // Iterator to execute the turn
        // It is important to use an Iterator so that it can relinquish control when there is nothing to do, i.e. yield return
        protected IEnumerator<ITask> TurnByDegree(TurnAndMoveParameters tamp, Handler onComplete)
        {
            LogInfo("DriveBehaviorServiceBase: TurnByDegree() Started");

            bool lastOpSuccess = true;

            _state.MovingState = MovingState.InTransition;
            // onComplete handler may set MovingState to whatever appropriate. We can set MovingState.Unknown on any error or interruption, and at the end to tamp.desiredMovingState
            lastInTransitionStarted = DateTime.Now;

            LogInfo(LogGroups.Console, "Turning " + (tamp.rotateAngle > 0 ? "right " : "left ") + Math.Abs(tamp.rotateAngle) + " degrees,  power=" + tamp.rotatePower);

            tamp.success = false;
            Fault fault = null;
            int rotateAngle = tamp.rotateAngle;
            double? desiredHeading = Direction.to360(_mapperVicinity.robotDirection.heading + rotateAngle);

            // Turn:
            yield return Arbiter.Choice(
                            TurnByAngle(rotateAngle, tamp.rotatePower),
                            delegate(DefaultUpdateResponseType response)
                            {
                                LogInfo("success in TurnByAngle()" + currentCompass);
                                lastOpSuccess = true;
                            },
                            delegate(Fault f)
                            {
                                LogInfo("fault in TurnByAngle()");
                                lastOpSuccess = false;
                                _state.MovingState = MovingState.Unknown;
                                fault = f;
                            }
            );

            // If the RotateDegrees was accepted, then wait for it to complete.
            // It is important not to wait if the request failed.
            if (lastOpSuccess)
            {
                DriveStageContainer driveStage = new DriveStageContainer();
                yield return WaitForCompletion(driveStage);
                LogInfo("WaitForCompletion() returned: " + driveStage.DriveStage);

                if (_mapperVicinity.turnState != null)
                {
                    _mapperVicinity.turnState.finished = DateTime.Now;
                    _mapperVicinity.turnState.hasFinished = true;
                }
                lastOpSuccess = driveStage.DriveStage == drive.DriveStage.Completed;
            }
            else
            {
                if (_mapperVicinity.turnState != null)
                {
                    _mapperVicinity.turnState.finished = DateTime.Now;
                    _mapperVicinity.turnState.hasFinished = true;
                    _mapperVicinity.turnState.wasCanceled = true;
                }
                LogError("Error occurred on TurnByAngle: " + fault);
                _state.MovingState = MovingState.Unknown;
            }

            // done
            _state.IsTurning = false;
            _state.LastTurnCompleted = DateTime.Now;

            _mapperVicinity.robotState.leftPower = 0.0d;
            _mapperVicinity.robotState.rightPower = 0.0d;

            yield break;
        }