Beispiel #1
0
		/// <summary>
		/// Get the assets and startup some timers to do random movements and other things...
		/// </summary>
		/// <param name="parameters"></param>
		public async void OnStart(object sender, IDictionary<string, object> parameters)
		{
			try
			{
				//Get the audio and image lists for use in the skill
				_audioList = (await _misty.GetAudioListAsync())?.Data;
				_imageList = (await _misty.GetImageListAsync())?.Data;
				
				_misty.Wait(2000);
				
				_misty.PlayAudio("s_Awe.wav", 100, null); 
				_misty.ChangeLED(255, 255, 255, null);
				_misty.DisplayImage("e_ContentRight.jpg", 1, null);  
				
				RegisterEvents();

				//_heartbeatTimer = new Timer(HeartbeatCallback, null, 3000, 3000);
				_moveHeadTimer = new Timer(MoveHeadCallback, null, 10000, 10000);
				_moveArmsTimer = new Timer(MoveArmCallback, null, 10000, 10000);
				_ledTimer = new Timer(ChangeLEDCallback, null, 2000, 2000);
			}
			catch (Exception ex)
			{
				_misty.SkillLogger.Log($"ForceDriving : OnStart: => Exception", ex);
			}
		}
        /// <summary>
        /// Loop through some head, arm, image and led actions
        /// </summary>
        /// <param name="parameters"></param>
        public void OnStart(object sender, IDictionary <string, object> parameters)
        {
            try
            {
                //These calls assume system assets on the robot at the time of writing,
                //update as needed for new or different assets or as an exercise to allow user to pass in asset names :)

                //This loop could also be accomplished with a timed callback
                while (_misty.Wait(5000))
                {
                    _misty.PlayAudio("s_Acceptance.wav", 100, null);
                    _misty.MoveHead(45, 25, 0, 50, AngularUnit.Degrees, null);
                    _misty.MoveArms(0, 45, 25, 60, null, AngularUnit.Degrees, null);
                    _misty.ChangeLED(0, 255, 0, null);
                    _misty.DisplayImage("e_Disoriented.jpg", 1, null);

                    //Pause for 4 seconds, if the cancellation token is set during this time, exit the pause and the method
                    if (!_misty.Wait(4000))
                    {
                        return;
                    }

                    _misty.PlayAudio("s_Awe2.wav", 100, null);
                    _misty.MoveHead(-10, 15, 30, 50, AngularUnit.Degrees, null);
                    _misty.MoveArms(-45, 0, 60, 60, null, AngularUnit.Degrees, null);
                    _misty.ChangeLED(0, 255, 255, null);
                    _misty.DisplayImage("e_ContentRight.jpg", 1, null);

                    //Pause for 3.5 seconds, if the cancellation token is set during this time, exit the pause and the method
                    if (!_misty.Wait(3500))
                    {
                        return;
                    }

                    _misty.PlayAudio("s_Joy.wav", 100, null);
                    _misty.MoveHead(75, 25, 10, 50, AngularUnit.Degrees, null);
                    _misty.MoveArms(-45, 45, 60, 60, null, AngularUnit.Degrees, null);
                    _misty.ChangeLED(255, 255, 255, null);
                    _misty.DisplayImage("e_ContentLeft.jpg", 1, null);

                    //Pause for 2.5 seconds, if the cancellation token is set during this time, exit the pause and the method
                    if (!_misty.Wait(2500))
                    {
                        return;
                    }

                    _misty.PlayAudio("s_Joy.wav", 100, null);
                    _misty.MoveHead(65, 0, -10, 50, AngularUnit.Degrees, null);
                    _misty.MoveArms(0, -45, 60, 60, null, AngularUnit.Degrees, null);
                    _misty.ChangeLED(0, 0, 255, null);
                    _misty.DisplayImage("e_Joy.jpg", 1, null);
                }
            }
            catch (Exception ex)
            {
                _misty.SkillLogger.Log($"HelloAgainWorldSkill : OnStart: => Exception", ex);
            }
        }
Beispiel #3
0
        private void Ewww()
        {
            int soundNumber = _random.Next(1, 5);

            switch (soundNumber)
            {
            case 1:
                _misty.PlayAudio("Fart-Common-Everyday-Fart_Mike-Koenig.wav", null, null);
                _assetWrapper.ShowSystemImage(SystemImage.Surprise);
                _misty.Wait(2000);
                _assetWrapper.PlaySystemSound(SystemSound.PhraseOopsy);
                break;

            case 2:
                _misty.PlayAudio("Quick Fart-SoundBible.com-655578646.wav", null, null);
                _assetWrapper.ShowSystemImage(SystemImage.JoyGoofy2);
                break;

            case 3:
                _misty.PlayAudio("Silly_Farts-Joe-1473367952.wav", null, null);
                _assetWrapper.ShowSystemImage(SystemImage.TerrorRight);
                _misty.Wait(3000);
                _assetWrapper.PlaySystemSound(SystemSound.PhraseUhOh);
                break;

            case 4:
                _misty.PlayAudio("Belch-Kevan-136688254.wav", null, null);
                _assetWrapper.ShowSystemImage(SystemImage.Disgust);
                _misty.Wait(3000);
                break;
            }
        }
Beispiel #4
0
        public void OnStart(object sender, IDictionary <string, object> parameters)
        {
            Task.Run(async() =>
            {
                _skillHelper = new SkillHelper(_misty);
                await Task.Delay(3000);

                // Load the path to follow.
                _followPath = new FollowPath(_misty, _skillHelper);
                List <IFollowPathCommand> commands = await _followPath.LoadCommandsAsync(PATH_FILE_NAME, MyDelegateAsync);

                // Follow the path
                await _followPath.DriveAsync(commands);

                // Dock
                await _skillHelper.DisableHazardSystemAsync();

                // Dock
#if MAP_DOCK
                bool started = await DockAlignAsync();
                if (started)
                {
                    await _skillHelper.TurnAsync(180);
                    _misty.DriveHeading(0, .6, 3000, true, OnResponse);
                    await Task.Delay(4000);
                    _misty.DriveHeading(0, .2, 1000, true, OnResponse);
                    await Task.Delay(2000);
                    _misty.PlayAudio("s_Awe.wav", 100, OnResponse);
                }
#else
                _docker = new ChargerDock(_misty, _skillHelper, HEAD_PITCH_OFFSET, HEAD_ROLL_OFFSET, HEAD_YAW_OFFSET);
                await _docker.DockAsync();
#endif
                await _skillHelper.EnableHazardSystemAsync();

                await Task.Delay(30000);

                Cleanup();
            });
        }
Beispiel #5
0
        /// <summary>
        /// Drive straight for the specified distance in meters at a medium speed.
        /// Confirm movement with encoder values and retry if needed.
        /// If stopped due to a hazard wait a little bit for it to go away and then continue.
        /// </summary>
        /// <param name="distance"></param>
        /// <param name="slow"></param>
        public async Task <bool> DriveAsync(double distance, bool slow = false)
        {
            if (Math.Abs(distance) < 0.001)
            {
                return(true);
            }

            bool success = true;

            try
            {
                // Clear encoder values.
                int maxWait = 0;
                while (_leftEncoderValue != 0 && maxWait++ < 10)
                {
                    _misty.DriveHeading(0, 0, 100, false, OnResponse);
                    await Task.Delay(200);
                }
                if (_leftEncoderValue != 0)
                {
                    LogMessage("Failed to reset encoder values.");
                    return(false);
                }

                _leftEncoderValues  = new ConcurrentQueue <double>();
                _stopHazardLatching = false;
                bool driving     = true;
                bool sendCommand = true;

                maxWait = 0;
                while (driving)
                {
                    if (sendCommand)
                    {
                        // Arbitrary medium speed based upon distance.
                        int duration = (int)(500 + Math.Abs(distance) * 2500);
                        if (slow)
                        {
                            duration = 2 * duration;
                        }

                        LogMessage($"Sending drive command with a distance of {distance:f3} meters and a duration of {duration} ms.");
                        while (!_leftEncoderValues.IsEmpty)                         // Clearing the encoder queue
                        {
                            _leftEncoderValues.TryDequeue(out double r);
                        }
                        _misty.DriveHeading(0, Math.Abs(distance), duration, distance < 0, OnResponse);
                        sendCommand = false;
                        await Task.Delay(1000);
                    }

                    if (_abort)
                    {
                        return(false);
                    }
                    await Task.Delay(1000);

                    // Check that we really moved and if we're done.
                    // Drive command could have been dropped and/or a hazard could have stopped the robot.
                    double[] encoderValues = _leftEncoderValues.ToArray();
                    if (encoderValues.Length > 1)                     // encoder values should be arriving at 5Hz.
                    {
                        double distanceDriven = Math.Abs(encoderValues[encoderValues.Length - 1] / 1000.0);
                        //LogMessage($"Distance driven: {distanceDriven}.");
                        if (distanceDriven > Math.Abs(0.99 * distance) ||
                            Math.Abs(distanceDriven - Math.Abs(distance)) < 0.1)
                        {
                            LogMessage($"Completed drive with distance of {_leftEncoderValue / 1000.0:f3} meters.");
                            driving = false;
                        }
                        else
                        {
                            // Not there yet. Everything progressing okay?
                            if (_stopHazardLatching)
                            {
                                // We've stopped due to a hazard. Wait a bit for it to go away.
                                LogMessage("Drive command paused for hazard.");
                                _misty.PlayAudio("s_anger.wav", 100, OnResponse);
                                int maxHazardWait = 0;
                                while (_stopHazardState && maxHazardWait++ < 30)
                                {
                                    if (_abort)
                                    {
                                        return(false);
                                    }
                                    await Task.Delay(1000);
                                }
                                if (_stopHazardState)
                                {
                                    // Still in hazard state. Give up.
                                    LogMessage("Giving up on drive command due to persistent hazard condition.");
                                    success = false;
                                    driving = false;
                                }
                                else
                                {
                                    // We're out of hazard state.
                                    LogMessage("Out of hazard state.");
                                    distance           -= encoderValues[encoderValues.Length - 1] / 1000.0;
                                    _stopHazardLatching = false;
                                    sendCommand         = true;
                                }
                            }
                            if (encoderValues.Length > 2 && Math.Abs(encoderValues[encoderValues.Length - 1] - encoderValues[encoderValues.Length - 2]) < 0.0001)
                            {
                                // Encoder value not changing. Need to send drive command again.
                                LogMessage($"Encoder values not changing. Distance driven so far is {distanceDriven}.");
                                distance   -= encoderValues[encoderValues.Length - 1] / 1000.0;
                                sendCommand = true;
                            }
                        }
                    }
                    else
                    {
                        // Something is wrong if we get here.
                        // For now, just continue and hope for the best :).
                        LogMessage("Not receiving encoder messages. Can't verify drive commands.");
                    }
                }
            }
            catch (Exception ex)
            {
                LogMessage("Exception occurred within DriveAsync: " + ex.Message);
                success = false;
            }
            finally
            {
                _leftEncoderValues = null;
            }

            return(success);
        }
Beispiel #6
0
 /// <summary>
 /// Simple method wrapper to help with playing system audio
 /// </summary>
 public void PlaySystemSound(SystemSound sound)
 {
     _misty.PlayAudio($"s_{sound.ToString()}.wav", null, null);
 }
        /// <summary>
        /// Get the assets and startup some timers to do random movements and other things...
        /// </summary>
        /// <param name="parameters"></param>
        public async void OnStart(object sender, IDictionary <string, object> parameters)
        {
            try
            {
                var deviceInfo = await _misty.GetDeviceInformationAsync();

                if (!_misty.Wait(0))
                {
                    return;
                }

                //Get the audio and image lists for use in the skill
                _audioList = (await _misty.GetAudioListAsync())?.Data;

                if (!_misty.Wait(0))
                {
                    return;
                }
                _imageList = (await _misty.GetImageListAsync())?.Data;


                if (!_misty.Wait(0))
                {
                    return;
                }

                if (_audioList != null && _audioList.Count > 0)
                {
                    AudioDetails randomAudio = _audioList[_randomGenerator.Next(0, _audioList.Count - 1)];
                    _misty.PlayAudio(randomAudio.Name, 100, null);
                }

                if (_imageList != null && _imageList.Count > 0)
                {
                    ImageDetails randomImage = _imageList[_randomGenerator.Next(0, _imageList.Count - 1)];
                    _misty.DisplayImage(randomImage.Name, 1, null);
                }


                if (!_misty.Wait(2000))
                {
                    return;
                }

                _misty.ChangeLED(255, 255, 255, null);

                //Register a number of events
                _misty.RegisterAudioPlayCompleteEvent(AudioPlayCallback, 0, true, null, null);
                _misty.RegisterCapTouchEvent(CapTouchCallback, 0, true, null, null, null);
                _misty.RegisterKeyPhraseRecognizedEvent(KeyPhraseRecognizedCallback, 250, true, null, null);
                _misty.StartKeyPhraseRecognition(null);
                _misty.StartFaceRecognition(null);

                //Create an event with a specific name so we can unregister it when needed using that name
                _misty.RegisterBumpSensorEvent(BumpCallback, 0, true, null, "MyBumpSensorName", null);

                //Register face rec with keepAlive = false, it will need to be reregistered after triggering if the user wants it to run again
                _misty.RegisterFaceRecognitionEvent(FaceRecCallback, 0, false, null, null, null);

                //Play audio indicator that the event registration state has changed
                if (_audioList != null && _audioList.Count > 0)
                {
                    AudioDetails randomAudio = _audioList[_randomGenerator.Next(0, _audioList.Count - 1)];
                    _misty.PlayAudio(randomAudio.Name, 100, null);
                    await Task.Delay(1000);

                    _misty.PlayAudio(randomAudio.Name, 100, null);
                }

                if (!_misty.Wait(30000))
                {
                    return;
                }


                //Unregister the bump sensor
                _misty.UnregisterEvent("MyBumpSensorName", UnregisterCallback);

                //Play audio indicator that the event registration state has changed
                if (_audioList != null && _audioList.Count > 0)
                {
                    AudioDetails randomAudio = _audioList[_randomGenerator.Next(0, _audioList.Count - 1)];
                    _misty.PlayAudio(randomAudio.Name, 100, null);
                    await Task.Delay(1000);

                    _misty.PlayAudio(randomAudio.Name, 100, null);
                }

                if (!_misty.Wait(20000))
                {
                    return;
                }

                //Unregister ALL events
                _misty.UnregisterAllEvents(UnregisterCallback);

                //Play audio indicator that the event registration state has changed
                if (_audioList != null && _audioList.Count > 0)
                {
                    AudioDetails randomAudio = _audioList[_randomGenerator.Next(0, _audioList.Count - 1)];
                    _misty.PlayAudio(randomAudio.Name, 100, null);
                    await Task.Delay(1000);

                    _misty.PlayAudio(randomAudio.Name, 100, null);
                }


                if (!_misty.Wait(20000))
                {
                    return;
                }

                //Play audio indicator that the event registration state has changed
                if (_audioList != null && _audioList.Count > 0)
                {
                    AudioDetails randomAudio = _audioList[_randomGenerator.Next(0, _audioList.Count - 1)];
                    _misty.PlayAudio(randomAudio.Name, 100, null);
                    await Task.Delay(1000);

                    _misty.PlayAudio(randomAudio.Name, 100, null);
                }

                //Re-register events
                _misty.RegisterAudioPlayCompleteEvent(AudioPlayCallback, 0, true, null, null);
                _misty.RegisterFaceRecognitionEvent(FaceRecCallback, 0, false, null, null, null);
                _misty.RegisterCapTouchEvent(CapTouchCallback, 0, true, null, null, null);
                _misty.RegisterKeyPhraseRecognizedEvent(KeyPhraseRecognizedCallback, 0, true, null, null);

                //You can also register events without callbacks, it requires a user to subscribe to that event as follows...
                //Note that this re-registers bump events to play a sound on release, not contact as they were previously handled
                _misty.RegisterBumpSensorEvent(0, true, null, null, null);
                _misty.BumpSensorEventReceived += ProcessBumpEvent;

                //Will continue to process events until timeout of cancel
            }
            catch (Exception ex)
            {
                _misty.SkillLogger.Log($"InteractiveMistySkill : OnStart: => Exception", ex);
            }
        }
        private void ProcessQueryResult(object sender, string stringResult)
        {
            JObject dynamicResponse   = JObject.Parse(stringResult);
            string  color             = ((string)dynamicResponse["queryResult"]?["parameters"]?["Color"])?.ToLower();
            string  position          = ((string)dynamicResponse["queryResult"]?["parameters"]?["Direction"])?.ToLower();
            string  outputAudioString = (string)dynamicResponse["outputAudio"];

            byte[] outputAudio = Convert.FromBase64String(outputAudioString);

            try
            {
                JsonConverter[] converters = new JsonConverter[2] {
                    new ProtoByteStringConverter(), new OutputAudioEncodingConverter()
                };
                DetectIntentResponse response = JsonConvert.DeserializeObject <DetectIntentResponse>(stringResult, converters);
                string intent = response?.QueryResult?.Intent?.DisplayName;

                string defaultResponse = response?.QueryResult?.FulfillmentText;

                switch (intent)
                {
                case "ChangeLED":
                    if (color != null)
                    {
                        Color argbColor = Color.FromName(color);
                        _misty.ChangeLED(argbColor.R, argbColor.G, argbColor.B, null);
                    }
                    break;

                case "Joke":
                    _misty.PlayAudio(defaultResponse, 30, null);
                    break;

                case "Move your arms":
                    if (position == "up")
                    {
                        _misty.MoveArms(-90, -90, 50, 50, null, AngularUnit.Degrees, null);
                    }

                    if (position == "down")
                    {
                        _misty.MoveArms(90, 90, 50, 50, null, AngularUnit.Degrees, null);
                    }
                    _misty.Halt(null, null);

                    break;

                case "MoveHeadPosition":
                    if (position == "up")
                    {
                        _misty.MoveHead(-100, 0, 0, 50, AngularUnit.Degrees, null);
                    }

                    if (position == "down")
                    {
                        _misty.MoveHead(100, 0, 0, 50, AngularUnit.Degrees, null);
                    }

                    if (position == "right")
                    {
                        _misty.MoveHead(0, 0, -100, 50, AngularUnit.Degrees, null);
                    }

                    if (position == "left")
                    {
                        _misty.MoveHead(0, 0, 100, 50, AngularUnit.Degrees, null);
                    }

                    break;

                default:
                    _misty.SaveAudio("tts.wav", outputAudio, true, true, null);
                    break;
                }
            }
            catch (Exception ex)
            {
            }
        }
Beispiel #9
0
        private async Task <bool> ExecuteDockAsync()
        {
            _charging = false;

            // Set head position.
            await _skillHelper.MoveHeadAsync(_headPitchOffset, _headRollOffset, _headYawOffset);

            if (_abort)
            {
                return(false);
            }

            // Find the charger.
            if (!await FindChargerAsync())
            {
                return(false);
            }

            // Get Misty aligned with charger based upon charger pose X and Euler angle.
            int retries = 0;

            while (Math.Abs(_chargerPose.X) > ALIGNED_X || Math.Abs(_chargerPose.EulerYaw) > ALIGNED_EULER_YAW)
            {
                if (retries++ > ALIGN_MAX_RETRIES)
                {
                    _skillHelper.LogMessage("Failed to align with charger.");
                    return(false);
                }

                _skillHelper.LogMessage($"Charger position is [{_chargerPose.X:f3}, {_chargerPose.Y:f3}, {_chargerPose.Z:f3}] meters. Euler yaw is {_chargerPose.EulerYaw:f3} degrees.");
                await FaceChargerAsync(ALIGNED_X);

                if (_abort)
                {
                    return(false);
                }

                if (Math.Abs(_chargerPose.X) > ALIGNED_X || Math.Abs(_chargerPose.EulerYaw) > ALIGNED_EULER_YAW)
                {
                    await AlignWithChargerAsync();

                    if (Math.Abs(_chargerPose.Z - IDEAL_ALIGNMENT_DISTANCE) > .1)
                    {
                        await _skillHelper.DriveAsync(_chargerPose.Z - IDEAL_ALIGNMENT_DISTANCE);
                    }
                }
            }

            _skillHelper.LogMessage($"Charger position is [{_chargerPose.X:f3}, {_chargerPose.Y:f3}, {_chargerPose.Z:f3}] meters. Euler yaw is {_chargerPose.EulerYaw:f3} degrees.");

            // Backup and drive straight forward to check alignment.
            double chargerDistance = _chargerPose.Z;
            var    offsets         = new List <double>();
            await _skillHelper.DriveAsync(-1.0, true);

            await Task.Delay(1000);

            offsets.Add(_chargerPose.X);
            for (int i = 0; i < 6; i++)
            {
                await _skillHelper.DriveAsync(.25, true);

                await Task.Delay(1000);

                offsets.Add(_chargerPose.X);
            }
            foreach (var o in offsets)
            {
                _skillHelper.LogMessage(o.ToString("f3"));
            }
            double slope = (offsets[0] - offsets.Last()) / 1.5;
            double estimateFinalOffset = offsets.Last() - slope * 0.5;
            double offsetAngle         = -Math.Asin(estimateFinalOffset / 0.4) * 180.0 / Math.PI;

            _skillHelper.LogMessage($"Estimate final offset {estimateFinalOffset:f3} meters and {offsetAngle} degrees.");

            if (Math.Abs(offsetAngle) > FINAL_OFFSET_ANGLE_MAX)
            {
                _skillHelper.LogMessage("Offset angle is too large. Backing up and retrying dock process.");
                await _skillHelper.DriveAsync(-IDEAL_ALIGNMENT_DISTANCE + 0.25);

                return(false);
            }
            await _skillHelper.TurnAsync(offsetAngle);

            // Turn around.
            _skillHelper.LogMessage("Turning 180 degrees to face away from charger.");
            await _skillHelper.TurnAsync(-180);

            if (_abort)
            {
                return(false);
            }

            // Back on to charger.
            _skillHelper.LogMessage($"Driving {chargerDistance - 0.5 + CHARGER_DOCK_OVERSHOOT:f3} meters to back on to charger.");
            await _skillHelper.DriveAsync(-chargerDistance + 0.5 - CHARGER_DOCK_OVERSHOOT, true);

            if (_abort)
            {
                return(false);
            }

            // Check if we've ended up on top of the wedge.
            await Task.Delay(1000);

            _skillHelper.LogMessage($"Roll = {_skillHelper.ImuRoll:f3}. Pitch = {_skillHelper.ImuPitch:f3}.");
            if (Math.Abs(_skillHelper.ImuRoll) > MISTY_ON_WEDGE_ROLL || Math.Abs(AxisRotation(_initialPitch, _skillHelper.ImuPitch)) > MISTY_ON_WEDGE_PITCH)
            {
                // We're on the wedge. Drive away from the charger and try again.
                _skillHelper.LogMessage($"We appear to have driven on top of the alignment wedge. IMU roll is {_skillHelper.ImuRoll:f3}. IMU pitch is {_skillHelper.ImuPitch:f3}.");
                await _skillHelper.DriveAsync(IDEAL_ALIGNMENT_DISTANCE - 0.1);

                if (_abort)
                {
                    return(false);
                }
                await _skillHelper.TurnAsync(180);

                _chargerPose = null;
                return(false);
            }

            // Check that we're fully docked: back up a little bit to get more aligned.
            _misty.DriveHeading(0, 0.2, 500, true, OnResponse);

            // It can take several seconds for the charging indicator to update...
            await Task.Delay(7000);

            // Check that we're charging.
            if (!_charging)
            {
                _misty.PlayAudio("s_Anger3.wav", 100, OnResponse);
                await _skillHelper.DriveAsync(IDEAL_ALIGNMENT_DISTANCE - 0.1);

                if (_abort)
                {
                    return(false);
                }
                await _skillHelper.TurnAsync(180);

                _chargerPose = null;
                return(false);
            }

            _misty.PlayAudio("s_Ecstacy.wav", 100, OnResponse);

            return(true);
        }
Beispiel #10
0
        /// <summary>
        /// Drive straight for the specified distance in meters at a medium speed.
        /// Confirm movement with encoder values and retry if needed.
        /// If stopped due to a hazard wait a little bit for it to go away and then continue.
        /// </summary>
        /// <param name="distance"></param>
        /// <param name="slow"></param>
        public async Task <bool> DriveAsync(double distance, bool slow = false)
        {
            if (Math.Abs(distance) < 0.003)
            {
                // Can't drive this short a distance
                return(true);
            }

            if (DateTime.Now.Subtract(LastEncoderMessageReceived).TotalSeconds > 1)
            {
                LogMessage($"Cannot carry out a drive command because encoder messages are not being received. Last encoder message received at {LastEncoderMessageReceived}.");
                MistySpeak("Encoder messages are not being received. Path following aborted.");
                return(false);
            }

            bool success = true;

            try
            {
                // Clear encoder values by sending a drive command with a distance of 0.
                int encoderResets = 0;
                while (_leftEncoderValue != 0 && encoderResets++ < 10)
                {
                    _misty.DriveHeading(0, 0, 100, false, OnResponse);
                    await Task.Delay(500);
                }
                if (_leftEncoderValue != 0)
                {
                    LogMessage($"Failed to reset encoder values. Last encoder value received at {LastEncoderMessageReceived} with a value of {_leftEncoderValue}.");
                    return(false);
                }

                _leftEncoderValues  = new ConcurrentQueue <double>();
                _stopHazardLatching = false;
                bool     driving     = true;
                bool     sendCommand = true;
                int      duration    = 0;
                int      retries     = 0;
                DateTime start       = DateTime.Now;

                while (driving)
                {
                    if (sendCommand)
                    {
                        // Arbitrary medium speed based upon distance.
                        duration = (int)(500 + Math.Abs(distance) * 3000);
                        if (slow)
                        {
                            duration = 3 * duration;
                        }

                        while (!_leftEncoderValues.IsEmpty)                         // Clearing the encoder queue
                        {
                            _leftEncoderValues.TryDequeue(out double r);
                        }
                        LogMessage($"Sending drive command with a distance of {distance:f3} meters and a duration of {duration} ms.");
                        _misty.DriveHeading(0, Math.Abs(distance), duration, distance < 0, OnResponse);
                        start       = DateTime.Now;
                        sendCommand = false;
                        await Task.Delay(1000);
                    }

                    if (_abort)
                    {
                        return(false);
                    }
                    await Task.Delay(1500);

                    // Check that we really moved and if we're done.
                    // Drive command could have been dropped and/or a hazard could have stopped the robot.
                    double[] encoderValues = _leftEncoderValues.ToArray();
                    if (encoderValues.Length > 1)                     // encoder values should be arriving at 5Hz.
                    {
                        double distanceDriven = Math.Abs(encoderValues[encoderValues.Length - 1] / 1000.0);
                        if (distanceDriven > Math.Abs(0.99 * distance) || Math.Abs(distanceDriven - Math.Abs(distance)) < 0.1)
                        {
                            LogMessage($"Completed drive with distance of {_leftEncoderValue / 1000.0:f3} meters.");
                            driving = false;
                        }
                        else
                        {
                            // THE FOLLOWING CODE AROUND HAZARDS IS CURRENTLY INACTIVE AS THE HAZARD SYSTEM IS DISABLED BY THE SKILL
                            // Not there yet. Everything progressing okay?
                            if (_stopHazardLatching)
                            {
                                // We've stopped due to a hazard. Wait a bit for it to go away.
                                LogMessage("Drive command paused for hazard.");
                                _misty.PlayAudio("s_Anger.wav", 100, OnResponse);
                                int maxHazardWait = 0;
                                while (_stopHazardState && maxHazardWait++ < 30)
                                {
                                    if (_abort)
                                    {
                                        return(false);
                                    }
                                    await Task.Delay(1000);
                                }
                                if (_stopHazardState)
                                {
                                    // Still in hazard state. Give up.
                                    LogMessage("Giving up on drive command due to persistent hazard condition.");
                                    success = false;
                                    driving = false;
                                }
                                else
                                {
                                    // We're out of hazard state.
                                    LogMessage("Out of hazard state.");
                                    distance           -= encoderValues[encoderValues.Length - 1] / 1000.0;
                                    _stopHazardLatching = false;
                                    sendCommand         = true;
                                }
                            }
                            if (encoderValues[encoderValues.Length - 1] == 0)
                            {
                                // Encoder values are not changing. Try sending drive command again.
                                if (retries++ > 5)
                                {
                                    // Don't try forever.
                                    LogMessage("Unable to complete drive command successfully.");
                                    success = false;
                                    driving = false;
                                }
                                else
                                {
                                    sendCommand = true;
                                }
                            }
                            // Don't wait forever.
                            if (DateTime.Now.Subtract(start).TotalMilliseconds > (5 + 2 * duration))
                            {
                                LogMessage($"Time out waiting for completion of drive. Completed distance of {_leftEncoderValue / 1000.0:f3} meters.");
                                success = false;
                                driving = false;
                            }
                        }
                    }
                    else
                    {
                        LogMessage($"Not receiving encoder messages. Last encoder value recieved at {_lastEncoderValue}.");
                        success = false;
                    }
                }
            }
            catch (Exception ex)
            {
                LogMessage("Exception occurred within DriveAsync: " + ex.Message);
                success = false;
            }
            finally
            {
                _leftEncoderValues = null;
            }

            return(success);
        }
        /// <summary>
        /// This event handler is called when the robot/user sends a start message
        /// The parameters can be set in the Skill Runner (or as json) and used in the skill if desired
        /// </summary>
        /// <param name="parameters"></param>
        public async void OnStart(object sender, IDictionary <string, object> parameters)
        {
            //TODO Put your code here and update the summary above
            int task_id = 0;

            _timeToTellJokes = true;
            try
            {
                //Get the audio and image lists for use in the skill
                _audioList = (await _misty.GetAudioListAsync())?.Data;
                _imageList = (await _misty.GetImageListAsync())?.Data;
                _misty.Wait(2000);

                task_id++;
                _misty.PlayAudio("Misty_Hi.wav", 80, null);
                _misty.Wait(2000);
                _misty.PlayAudio("Misty_I_am_Annie.wav", 80, null);
                _misty.Wait(4000);
                _misty.ChangeLED(255, 255, 255, null);
                _misty.DisplayImage("e_DefaultContent.jpg", 1, null);
                _misty.MoveHead(10, 0, 0, 60, AngularUnit.Degrees, null);

                task_id++;
                _misty.RegisterAudioPlayCompleteEvent(AudioPlayCallback, 0, true, null, null);
                // Temporarily disable this voice recognition of the keyphrase wake up words due to SDK software issue.
                // It will be used the the fix is done.
                // The skill will be initiated by the skill sequencer.
//				_misty.StartKeyPhraseRecognition(null);
//				_misty.RegisterKeyPhraseRecognizedEvent(10, false, "KeyPhrase", null);
//				_misty.KeyPhraseRecognizedEventReceived += ProcessKeyPhraseEvent;
                _misty.StartFaceRecognition(null);
                RegisterEvents();

                task_id++;
                _heartbeatTimer = new Timer(HeartbeatCallback, null, 5000, 3000);
                _moveHeadTimer  = new Timer(MoveHeadCallback, null, 5000, 7000);
                _moveArmsTimer  = new Timer(MoveArmCallback, null, 5000, 4000);
                _ledTimer       = new Timer(ChangeLEDCallback, null, 1000, 1000);
            }
            catch (Exception ex)
            {
                if (task_id == 0)
                {
                    _misty.SkillLogger.LogVerbose($"TellingJokeSkill : Failed to Load audio and image files");
                }
                else if (task_id == 1)
                {
                    _misty.SkillLogger.LogVerbose($"TellingJokeSkill : Failed to play audio and display image files");
                }
                else if (task_id == 2)
                {
                    _misty.SkillLogger.LogVerbose($"TellingJokeSkill : Failed to register events");
                }
                else
                {
                    _misty.SkillLogger.LogVerbose($"TellingJokeSkill : Failed to setup timers");
                }
                _misty.SkillLogger.Log($"TellingJokeSkill : OnStart: => Exception", ex);
            }
        }