<|start_filename|>Anki.Vector/Events/CameraSettingsUpdateEventArgs.cs<|end_filename|>
﻿// <copyright file="CameraSettingsUpdateEventArgs.cs" company="<NAME>">
//     Copyright (c) 2020 <NAME>. All rights reserved.
// </copyright>

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;

namespace Anki.Vector.Events
{
    /// <summary>
    /// Camera settings update event args
    /// </summary>
    /// <seealso cref="Anki.Vector.Events.RobotEventArgs" />
    [Serializable]
    public class CameraSettingsUpdateEventArgs : RobotEventArgs
    {
        /// <summary>
        /// Gets a value indicating whether automatic exposure enabled.
        /// </summary>
        public bool AutoExposureEnabled { get; }

        /// <summary>
        /// Gets the exposure in milliseconds.
        /// </summary>
        public uint ExposureMs { get; }

        /// <summary>
        /// Gets the gain.
        /// </summary>
        public float Gain { get; }

        /// <summary>
        /// Initializes a new instance of the <see cref="CameraSettingsUpdateEventArgs"/> class.
        /// </summary>
        /// <param name="e">The e.</param>
        internal CameraSettingsUpdateEventArgs(ExternalInterface.Event e) : base(e)
        {
            AutoExposureEnabled = e.CameraSettingsUpdate.AutoExposureEnabled;
            ExposureMs = e.CameraSettingsUpdate.ExposureMs;
            Gain = e.CameraSettingsUpdate.Gain;
        }
    }
}


<|start_filename|>Anki.Vector/Types/CloudConnection.cs<|end_filename|>
﻿// <copyright file="CloudConnection.cs" company="<NAME>">
//     Copyright (c) 2020 <NAME>. All rights reserved.
// </copyright>

using System;
using System.Collections.Generic;
using System.Text;
using Anki.Vector.ExternalInterface;

namespace Anki.Vector.Types
{
    /// <summary>
    /// Cloud connection code
    /// </summary>
    public enum CloudConnectionCode
    {
        /// <summary>Unknown</summary>
        Unknown = 0,
        /// <summary>Available</summary>
        Available = 1,
        /// <summary>Bad connectivity</summary>
        BadConnectivity = 2,
        /// <summary>Failed TLS</summary>
        FailedTls = 3,
        /// <summary>Failed Authorization</summary>
        FailedAuth = 4,
        /// <summary>Insufficient bandwidth</summary>
        InsufficientBandwidth = 5,
    }

    /// <summary>
    /// Cloud connection information
    /// </summary>
    public class CloudConnection
    {
        /// <summary>
        /// Gets the cloud connection code.
        /// </summary>
        public CloudConnectionCode Code { get; }

        /// <summary>
        /// Gets the expected packets.
        /// </summary>
        public int ExpectedPackets { get; }

        /// <summary>
        /// Gets the number packets.
        /// </summary>
        public int NumPackets { get; }

        /// <summary>
        /// Initializes a new instance of the <see cref="CloudConnection"/> class.
        /// </summary>
        /// <param name="response">The response.</param>
        internal CloudConnection(CheckCloudResponse response)
        {
            Code = (CloudConnectionCode)response.Code;
            ExpectedPackets = response.ExpectedPackets;
            NumPackets = response.NumPackets;
        }
    }
}


<|start_filename|>Anki.Vector/CameraComponent.cs<|end_filename|>
﻿// <copyright file="CameraComponent.cs" company="<NAME>">
//     Copyright (c) 2020 <NAME>. All rights reserved.
// </copyright>

namespace Anki.Vector
{
    using System;
    using System.ComponentModel;
    using System.Runtime.InteropServices;
    using System.Threading.Tasks;
    using Anki.Vector.Events;
    using Anki.Vector.ExternalInterface;
    using Anki.Vector.GrpcUtil;
    using Anki.Vector.Types;

    /// <summary>
    /// Support for Vector’s camera.
    /// <para>Vector has a built-in camera which he uses to observe the world around him.  You can start receiving camera images from Vector by calling the <see cref="StartFeed"/> method.  
    /// The <see cref="ImageReceived"/> event will be raised for each frame received.  Although there is an <see cref="ImageEncoding"/> property, the data received from Vector is always in
    /// the color JPEG format.</para>
    /// <para>The camera resolution is 1280 x 720 with a field of view of 90 deg (H) x 50 deg (V).</para>
    /// </summary>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1001:Types that own disposable fields should be disposable", Justification = "Component is disposed by Teardown method.")]
    public class CameraComponent : Component
    {
        /// <summary>
        /// The camera event loop
        /// </summary>
        private readonly IAsyncEventLoop cameraFeed;

        /// <summary>
        /// Gets the latest image from the camera feed.
        /// </summary>
        public Image LatestImage { get => _latestImage; private set => SetProperty(ref _latestImage, value); }
        private Image _latestImage = null;

        /// <summary>
        /// Gets the configuration and calibration of the camera.  
        /// </summary>
        /// <remarks>Camera configuration is only populated with firmware version 1.7 or greater.  See <see cref="Capabilities.CameraSettings"/>.</remarks>
        public CameraConfig Config { get; private set; }

        /// <summary>
        /// Gets a value indicating whether automatic exposure is enabled.
        /// <para>If auto exposure is enabled the <see cref="Gain"/> and <see cref="ExposureMs"/> values will constantly be updated by Vector.</para>
        /// </summary>
        public bool AutoExposureEnabled { get; private set; } = true;

        /// <summary>
        /// Gets the current camera gain setting.
        /// </summary>
        /// <remarks>Gain is only populated with firmware version 1.7 or greater.  See <see cref="Capabilities.CameraSettings"/>.</remarks>
        public float Gain { get; private set; }

        /// <summary>
        /// Gets the current camera exposure setting in milliseconds.
        /// </summary>
        /// <remarks>Exposure is only populated with firmware version 1.7 or greater.  See <see cref="Capabilities.CameraSettings"/>.</remarks>
        public uint ExposureMs { get; private set; }

        /// <summary>
        /// Occurs when camera feed event.
        /// </summary>
        public event EventHandler<ImageReceivedEventArgs> ImageReceived;

        /// <summary>
        /// Initializes a new instance of the <see cref="CameraComponent" /> class.
        /// </summary>
        /// <param name="robot">The robot.</param>
        internal CameraComponent(Robot robot) : base(robot)
        {
            this.cameraFeed = new AsyncEventLoop<CameraFeedResponse>(
                (token) => robot.StartStream(client => client.CameraFeed(new CameraFeedRequest(), cancellationToken: token)),
                (response) =>
                {
                    var imageReceivedEventArgs = new ImageReceivedEventArgs(response);
                    LatestImage = imageReceivedEventArgs.Image;
                    ImageReceived?.Invoke(this, imageReceivedEventArgs);
                },
                () => OnPropertyChanged(nameof(IsFeedActive)),
                robot.PropagateException
            );

            robot.Connected += Robot_Connected;
            robot.Disconnected += Robot_Disconnected;
            robot.Events.CameraSettingsUpdate += Events_CameraSettingsUpdate;
        }

        /// <summary>
        /// Handles the Connected event of the Robot control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="ConnectedEventArgs"/> instance containing the event data.</param>
        private async void Robot_Connected(object sender, ConnectedEventArgs e)
        {
            if (Robot.Capabilities.CameraSettings)
            {
                Config = await ReadCameraConfig().ConfigureAwait(false);
                OnPropertyChanged(nameof(Config));
            }
        }

        /// <summary>
        /// Handles the Disconnected event of the Robot control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="DisconnectedEventArgs"/> instance containing the event data.</param>
        private void Robot_Disconnected(object sender, DisconnectedEventArgs e)
        {
            Config = null;
            OnPropertyChanged(nameof(Config));
        }

        /// <summary>
        /// Handles the CameraSettingsUpdate event of the Events control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="CameraSettingsUpdateEventArgs"/> instance containing the event data.</param>
        private void Events_CameraSettingsUpdate(object sender, CameraSettingsUpdateEventArgs e)
        {
            AutoExposureEnabled = e.AutoExposureEnabled;
            ExposureMs = e.ExposureMs;
            Gain = e.Gain;
            OnPropertyChanged(nameof(AutoExposureEnabled));
            OnPropertyChanged(nameof(ExposureMs));
            OnPropertyChanged(nameof(Gain));
        }

        /// <summary>
        /// Gets a value indicating whether the camera feed is active.
        /// </summary>
        /// <value>
        ///   <c>true</c> if the camera feed is active; otherwise, <c>false</c>.
        /// </value>
        public bool IsFeedActive => cameraFeed.IsActive;

        /// <summary>
        /// The needs stream enabled
        /// </summary>
        private bool needsStreamEnabled = false;

        /// <summary>
        /// Starts the camera feed.  The feed will run in a background thread and raise the <see cref="ImageReceived" /> event for each received image.  It will
        /// also update the <see cref="LatestImage"/> property.
        /// </summary>
        /// <returns>A task that represents the asynchronous operation.</returns>
        public async Task StartFeed()
        {
            if (!await IsImageStreamingEnabled().ConfigureAwait(false)) needsStreamEnabled = true;
            await cameraFeed.Start().ConfigureAwait(false);
            OnPropertyChanged(nameof(IsFeedActive));
        }

        /// <summary>
        /// Stops the camera feed.
        /// </summary>
        /// <returns>A task that represents the asynchronous operation.</returns>
        public async Task StopFeed()
        {
            needsStreamEnabled = false;
            await cameraFeed.End().ConfigureAwait(false);
        }

        /// <summary>
        /// Request to capture a single image from the robot's camera.
        /// <para>This call requests the robot to capture an image and returns it.  If the camera feed is active, this call returns the contents of the <see cref="LatestImage"/> property and does
        /// not request a new image from the robot.</para>
        /// </summary>
        /// <returns>A task that represents the asynchronous operation; the task result contains the image captured from the robot.</returns>
        public async Task<Image> CaptureSingleImage()
        {
            if (IsFeedActive) return LatestImage;
            var response = await Robot.RunMethod(client => client.CaptureSingleImageAsync(new CaptureSingleImageRequest())).ConfigureAwait(false);
            response.Status.Code.EnsureSuccess();
            return new Image(response);
        }

        /// <summary>
        /// Captures a high resolution image from the robot's camera and returns it.
        /// </summary>
        /// <returns>A task that represents the asynchronous operation; the task result contains the image captured from the robot.</returns>
        /// <remarks>This metohd is only available with firmware version 1.7 or greater.  See <see cref="Capabilities.HighResolutionImageCapture"/>.</remarks>
        public async Task<Image> CaptureHighResolutionImage()
        {
            Robot.Capabilities.Assert(Capabilities.Version1_7);
            var response = await Robot.RunMethod(client => client.CaptureSingleImageAsync(new CaptureSingleImageRequest() { EnableHighResolution = true })).ConfigureAwait(false);
            response.Status.Code.EnsureSuccess();
            if (IsFeedActive) needsStreamEnabled = true;
            return new Image(response);
        }

        /// <summary>
        /// Enable auto exposure on Vector's Camera.
        /// <para>Enable auto exposure on Vector's camera to constantly update the exposure time and gain values based on the recent images. This is the default mode when any SDK program starts.</para>
        /// </summary>
        /// <returns>A task that represents the asynchronous operation; the task result contains the result from the robot.</returns>
        /// <remarks>This method is only available with firmware version 1.7 or greater.  See <see cref="Capabilities.CameraSettings"/>.</remarks>
        public async Task<StatusCode> EnableAutoExposure()
        {
            Robot.Capabilities.Assert(Capabilities.Version1_7);
            var response = await Robot.RunMethod(client => client.SetCameraSettingsAsync(new SetCameraSettingsRequest() { EnableAutoExposure = true })).ConfigureAwait(false);
            return response.Status.Code.Convert();
        }

        /// <summary>
        /// Set manual exposure values for Vector's Camera.
        /// <para>This will disable auto exposure on Vector's camera and force the specified exposure time and gain values.</para>
        /// <para>See the camera <see cref="Config"/> for valid ranges for the exposure and gain parameters</para>
        /// </summary>
        /// <param name="exposureMs">The exposure in milliseconds.</param>
        /// <param name="gain">The gain.</param>
        /// <returns>A task that represents the asynchronous operation; the task result contains the result from the robot.</returns>
        /// <remarks>Requires behavior control.  This method is only available with firmware version 1.7 or greater.  See <see cref="Capabilities.CameraSettings"/>.</remarks>
        public async Task<StatusCode> SetManualExposure(uint exposureMs, float gain)
        {
            Robot.Capabilities.Assert(Capabilities.Version1_7);
            if (exposureMs < Config.MinCameraExposureTimeMs || exposureMs > Config.MaxCameraExposureTimeMs) throw new ArgumentOutOfRangeException(nameof(exposureMs), "Exposure value of range");
            if (gain < Config.MinCameraGain || gain > Config.MaxCameraGain) throw new ArgumentOutOfRangeException(nameof(gain), "Gain value is out of range");

            var response = await Robot.RunControlMethod(client => client.SetCameraSettingsAsync(new SetCameraSettingsRequest()
            {
                Gain = gain,
                ExposureMs = exposureMs,
                EnableAutoExposure = false
            })).ConfigureAwait(false);

            Gain = gain;
            ExposureMs = exposureMs;
            AutoExposureEnabled = false;

            OnPropertyChanged(nameof(AutoExposureEnabled));
            OnPropertyChanged(nameof(ExposureMs));
            OnPropertyChanged(nameof(Gain));

            return response.Status.Code.Convert();
        }

        /// <summary>
        /// Retrieves the calibrated camera settings from the robot.
        /// </summary>
        /// <returns>A task that represents the asynchronous operation; the task result contains the camera configuration.</returns>
        /// <remarks>This method is only available with firmware version 1.7 or greater.  See <see cref="Capabilities.CameraSettings"/>.</remarks>
        private async Task<CameraConfig> ReadCameraConfig()
        {
            Robot.Capabilities.Assert(Capabilities.Version1_7);
            var response = await Robot.RunMethod(client => client.GetCameraConfigAsync(new CameraConfigRequest())).ConfigureAwait(false);
            return new CameraConfig(response);
        }

        /// <summary>
        /// Enables the high resolution image streaming.  This method does not appear to work.
        /// </summary>
        /// <returns>A task that represents the asynchronous operation; the task result contains the result from the robot.</returns>
        /// <remarks>This method will hang if <see cref="RobotStatus.IsInCalmPowerMode"/> is true</remarks>
        [EditorBrowsable(EditorBrowsableState.Never)]
        [Obsolete("This method is not yet work.")]
        private async Task<StatusCode> EnableHighResolutionImageStreaming()
        {
            Robot.Capabilities.Assert(Capabilities.Version1_7);
            var response = await Robot.RunMethod(client => client.EnableImageStreamingAsync(new EnableImageStreamingRequest() { Enable = true, EnableHighResolution = true })).ConfigureAwait(false);
            return response.Status.Code.Convert();
        }

        /// <summary>
        /// Enables the image streaming.  Image streaming needs to be enabled for the robot to receive images from the feed.
        /// </summary>
        /// <returns>A task that represents the asynchronous operation; the task result contains the result from the robot.</returns>
        /// <remarks>This method will hang if <see cref="RobotStatus.IsInCalmPowerMode"/> is true</remarks>
        private async Task<StatusCode> EnableImageStreaming()
        {
            var response = await Robot.RunMethod(client => client.EnableImageStreamingAsync(new EnableImageStreamingRequest() { Enable = true, EnableHighResolution = false })).ConfigureAwait(false);
            return response.Status.Code.Convert();
        }

        /// <summary>
        /// Disables the image streaming.  
        /// </summary>
        /// <returns>A task that represents the asynchronous operation; the task result contains the result from the robot.</returns>
        private async Task<StatusCode> DisableImageStreaming()
        {
            var response = await Robot.RunMethod(client => client.EnableImageStreamingAsync(new EnableImageStreamingRequest() { Enable = false, EnableHighResolution = false })).ConfigureAwait(false);
            return response.Status.Code.Convert();
        }

        /// <summary>
        /// Determines whether image streaming is enabled.
        /// </summary>
        /// <returns>A task that represents the asynchronous operation; the task result contains whether not streaming is enabled.</returns>
        private async Task<bool> IsImageStreamingEnabled()
        {
            var response = await Robot.RunMethod(client => client.IsImageStreamingEnabledAsync(new IsImageStreamingEnabledRequest())).ConfigureAwait(false);
            return response.IsImageStreamingEnabled;
        }

        /// <summary>
        /// Ensures the stream is enabled.  This is called from the robot status method to ensure that the robot is not in calm power mode.
        /// </summary>
        internal async Task EnsureStreamEnabled()
        {
            if (!needsStreamEnabled) return;
            if (!IsFeedActive) return;
            if (Robot.Status.IsInCalmPowerMode) return;
            needsStreamEnabled = false;
            // Wait for the robot to be availabe after calm power mode
            await Task.Delay(1000).ConfigureAwait(false);
            await EnableImageStreaming().ConfigureAwait(false);
        }

        /// <summary>
        /// Called when disconnecting
        /// </summary>
        /// <param name="forced">if set to <c>true</c> the shutdown is forced due to lost connection.</param>
        /// <returns>
        /// A task that represents the asynchronous operation.
        /// </returns>
        internal override Task Teardown(bool forced)
        {
            return StopFeed();
        }
    }
}


<|start_filename|>Anki.Vector/Robot.cs<|end_filename|>
﻿// <copyright file="Robot.cs" company="<NAME>">
//     Copyright (c) 2020 <NAME>. All rights reserved.
// </copyright>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using Anki.Vector.Events;
using Anki.Vector.Exceptions;
using Anki.Vector.ExternalInterface;
using Anki.Vector.Types;
using Grpc.Core;
using Newtonsoft.Json;
using static Anki.Vector.ExternalInterface.ExternalInterface;

namespace Anki.Vector
{
    /// <summary>
    /// Vector control priority level
    /// </summary>
    public enum ControlPriority
    {
        /// <summary>
        /// Highest priority level. Suppresses most automatic physical reactions, use with caution. 
        /// </summary>
        OverrideBehaviors = 10,

        /// <summary>
        /// Normal priority level. Directly under mandatory physical reactions.
        /// </summary>
        Default = 20,

        /// <summary>
        /// Enable long-running SDK control between script execution.  Not to be used for regular behavior control.
        /// </summary>
        ReserveControl = 30
    }

    /// <summary>
    /// The main robot class for managing Vector.
    /// <para>The Robot object is responsible for managing the state and connections to a Vector, and is typically the entry-point to running the SDK.</para>
    /// </summary>
    /// <seealso cref="System.IDisposable" />
    public class Robot : RobotObject, IDisposable
    {
        /// <summary>
        /// Occurs when there is a background exception.
        /// </summary>
        public event EventHandler<ErrorEventArgs> Error;

        /// <summary>
        /// Occurs when the robot is fully connected.
        /// </summary>
        public event EventHandler<ConnectedEventArgs> Connected;

        /// <summary>
        /// Occurs when robot is disconnected.
        /// </summary>
        public event EventHandler<DisconnectedEventArgs> Disconnected;

        /// <summary>
        /// Occurs when robot is delocalized  (i.e. whenever Vector no longer knows where he is - e.g.when he's picked up).
        /// </summary>
        public event EventHandler<DelocalizedEventArgs> Delocalized;

        /// <summary>
        /// Gets the control component
        /// </summary>
        public ControlComponent Control { get; }

        /// <summary>
        /// Gets the camera component
        /// </summary>
        public CameraComponent Camera { get; }

        /// <summary>
        /// Gets the behavior component
        /// </summary>
        public BehaviorComponent Behavior { get; }

        /// <summary>
        /// Gets the motors component
        /// </summary>
        public MotorComponent Motors { get; }

        /// <summary>
        /// Gets the photo component.
        /// </summary>
        public PhotoComponent Photos { get; }

        /// <summary>
        /// Gets the events component 
        /// </summary>
        public EventComponent Events { get; }

        /// <summary>
        /// Gets the animation component.
        /// </summary>
        public AnimationComponent Animation { get; }

        /// <summary>
        /// Gets the face component
        /// </summary>
        public FaceComponent Faces { get; }

        /// <summary>
        /// Gets the audio component.
        /// </summary>
        public AudioComponent Audio { get; }

        /// <summary>
        /// Gets the screen component.
        /// </summary>
        public ScreenComponent Screen { get; }

        /// <summary>
        /// Gets the vision component.
        /// </summary>
        public VisionComponent Vision { get; }

        /// <summary>
        /// Gets the world component.
        /// </summary>
        public WorldComponent World { get; }

        /// <summary>
        /// Gets the navmap component.
        /// </summary>
        public NavMapComponent NavMap { get; }

        /// <summary>
        /// Gets the capabilities support by Vector's current firmware
        /// </summary>
        public Capabilities Capabilities { get; }

        /// <summary>
        /// Gets a value indicating whether the robot is connected.
        /// </summary>
        public bool IsConnected => Events.IsProccessingEvents;

        /// <summary>
        /// Gets the current accelerometer reading
        /// </summary>
        public Acceleration Acceleration { get => _acceleration; private set => SetProperty(ref _acceleration, value); }
        private Acceleration _acceleration;

        /// <summary>
        /// Gets the ID of the object currently being carried (-1 if none)
        /// </summary>
        public int CarryingObjectId { get => _carryingObjectId; private set => SetProperty(ref _carryingObjectId, value); }
        private int _carryingObjectId = -1;

        /// <summary>
        /// Gets the current gyroscope reading (x, y, z) 
        /// </summary>
        public AngularVelocity Gyro { get => _gyro; private set => SetProperty(ref _gyro, value); }
        private AngularVelocity _gyro;

        /// <summary>
        /// Gets Vector's head angle (up/down) in radians
        /// </summary>
        public float HeadAngleRad { get => _headAngleRad; private set => SetProperty(ref _headAngleRad, value); }
        private float _headAngleRad;

        /// <summary>
        /// Gets the ID of the object the head is tracking to (-1 if none)
        /// </summary>
        public int HeadTrackingObjectId { get => _headTrackingObjectId; private set => SetProperty(ref _headTrackingObjectId, value); }
        private int _headTrackingObjectId = -1;

        /// <summary>
        /// Gets the robot's timestamp for the last image seen.
        /// </summary>
        public uint LastImageTimestamp { get => _lastImageTimestamp; private set => SetProperty(ref _lastImageTimestamp, value); }
        private uint _lastImageTimestamp;

        /// <summary>
        /// Gets Vector’s left wheel speed in mm/sec
        /// </summary>
        public float LeftWheelSpeedMmps { get => _leftWheelSpeedMmps; private set => SetProperty(ref _leftWheelSpeedMmps, value); }
        private float _leftWheelSpeedMmps;

        /// <summary>
        /// Gets the height of Vector’s lift from the ground.
        /// </summary>
        public float LiftHeightMm { get => _liftHeightMm; private set => SetProperty(ref _liftHeightMm, value); }
        private float _liftHeightMm;

        /// <summary>
        /// Gets the ID of the object that the robot is localized to (-1 if none)
        /// </summary>
        public int LocalizedToObjectId { get => _localizedToObjectId; private set => SetProperty(ref _localizedToObjectId, value); }
        private int _localizedToObjectId = -1;

        /// <summary>
        /// Gets the current pose (position and orientation) of Vector.
        /// </summary>
        public Pose Pose { get => _pose; private set => SetProperty(ref _pose, value); }
        private Pose _pose = new Pose();

        /// <summary>
        /// Gets Vector's pose angle (heading in X-Y plane) in radians
        /// </summary>
        public float PoseAngleRad { get => _poseAngleRad; private set => SetProperty(ref _poseAngleRad, value); }
        private float _poseAngleRad;

        /// <summary>
        /// Gets Vector’s pose pitch (angle up/down) in radians
        /// </summary>
        public float PosePitchRad { get => _posePitchRad; private set => SetProperty(ref _posePitchRad, value); }
        private float _posePitchRad;

        /// <summary>
        /// Gets the proximity sensor data.
        /// </summary>
        public ProximitySensorData Proximity { get => _proximity; private set => SetProperty(ref _proximity, value); }
        private ProximitySensorData _proximity;

        /// <summary>
        /// Gets Vector's right wheel speed in mm/sec
        /// </summary>
        public float RightWheelSpeedMmps { get => _rightWheelSpeedMmps; private set => SetProperty(ref _rightWheelSpeedMmps, value); }
        private float _rightWheelSpeedMmps;

        /// <summary>
        /// Gets the various status properties of the robot.
        /// </summary>
        public RobotStatus Status { get => _status; private set => SetProperty(ref _status, value); }
        private RobotStatus _status;

        /// <summary>
        /// Gets state related to touch detection.
        /// </summary>
        public TouchSensorData Touch { get => _touch; private set => SetProperty(ref _touch, value); }
        private TouchSensorData _touch;

        /// <summary>
        /// Gets the current IP address of the robot.  Can be null if remote Vector connection is used.
        /// </summary>
        public IPAddress IPAddress => client?.IPAddress;

        /// <summary>
        /// Gets the firmware version.  Populated when connected
        /// </summary>
        public Version FirmwareVersion { get; private set; }

        /// <summary>
        /// The timeout for calls and connections in milliseconds
        /// </summary>
        internal const int DefaultConnectionTimeout = 5_000;

        /// <summary>
        /// The mDNS search timeout
        /// </summary>
        internal const int IPAddressSearchTimeout = 10_000;

        /// <summary>
        /// The GRPC and REST client
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2213", Justification = "Disposed in Disconnect method")]
        private Client client = null;

        /// <summary>
        /// Has the disconnect method been called
        /// </summary>
        private volatile bool disconnecting = false;

        /// <summary>
        /// Propagates the exception by raising the error event.
        /// </summary>
        /// <param name="ex">The ex.</param>
        internal void PropagateException(Exception ex)
        {
            Error?.Invoke(this, new ErrorEventArgs(ex));
        }

        /// <summary>
        /// Connects to Vector and returns a robot instance.
        /// </summary>
        /// <param name="robotConfiguration">The robot configuration.</param>
        /// <param name="timeout">The connection timeout in milliseconds.</param>
        /// <returns>A task that represents the asynchronous operation; the task result contains the connected robot instance</returns>
        public static async Task<Robot> NewConnection(IRobotConfiguration robotConfiguration, int timeout = DefaultConnectionTimeout)
        {
            // Create a robot proxy object used to communicate with Vector
            var robot = new Robot();
            try
            {
                // Attempt to connect the robot
                await robot.Connect(robotConfiguration, timeout).ConfigureAwait(false);
            }
            catch (Exception)
            {
                // There was a problem connecting with the robot, clean up the connection
                robot.Dispose();
                throw;
            }
            return robot;
        }

        /// <summary>
        /// Connects to Vector using the first robot found in the configuration file and returns a robot instance.
        /// </summary>
        /// <param name="timeout">The timeout.</param>
        /// <returns>A task that represents the asynchronous operation.  The task result contains the connected robot instance.</returns>
        /// <exception cref="VectorConfigurationException">No Robot Configuration found; please run the configuration tool to setup the robot connection.</exception>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Ignore VectorNotFoundException until all Vectors have been tested.")]
        public static async Task<Robot> NewConnection(int timeout = DefaultConnectionTimeout)
        {
            // Load the configuration file
            var configurations = RobotConfiguration.Load().ToArray();
            if (configurations.Length == 0)
            {
                throw new VectorConfigurationException("No Robot Configuration found; please run the configuration tool to setup the robot connection.");
            }
            
            // try each robot in order until the one is able to connect
            foreach (var configuration in configurations)
            {
                try
                {
                    // Try connecting to the robot.  Wait for it connect so that we can capture any
                    // failed connection exceptions.  If we succeed, we'll return the connection;
                    // if get an exception, we'll try the next configuration
                    return await NewConnection(configuration, timeout).ConfigureAwait(false);
                }
                catch (VectorNotFoundException)
                {
                    // If only one robot, rethrow the original exception
                    if (configurations.Length == 1) throw;
                }
            }

            // More than one robot, throw exception if none found
            throw new VectorNotFoundException("Could not connect to any of the configured Vectors.");
        }

        /// <summary>
        /// Connects to Vector using the robot configuration that matches the name or serial number provided and returns a robot instance.
        /// </summary>
        /// <param name="nameOrSerialNumber">The name or serial number.</param>
        /// <param name="timeout">The timeout.</param>
        /// <returns>A task that represents the asynchronous operation.  The task result contains the connected robot instance.</returns>
        /// <exception cref="VectorConfigurationException">No Robot Configuration with the name or serial number matching '{nameOrSerialNumber}' found.</exception>
        public static Task<Robot> NewConnection(string nameOrSerialNumber, int timeout = DefaultConnectionTimeout)
        {
            var configuration = RobotConfiguration.Load().FirstOrDefault(c => c.RobotName == nameOrSerialNumber || c.SerialNumber == nameOrSerialNumber);
            if (configuration == null)
            {
                throw new VectorConfigurationException($"No Robot Configuration with the name or serial number matching '{nameOrSerialNumber}' found.");
            }
            return NewConnection(configuration, timeout);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Robot"/> class.
        /// </summary>
        public Robot() 
        {
            // Components
            this.Control = new ControlComponent(this);
            this.Events = new EventComponent(this);
            this.Camera = new CameraComponent(this);
            this.Behavior = new BehaviorComponent(this);
            this.Motors = new MotorComponent(this);
            this.Animation = new AnimationComponent(this);
            this.Audio = new AudioComponent(this);
            this.Screen = new ScreenComponent(this);
            this.Vision = new VisionComponent(this);
            this.World = new WorldComponent(this);
            this.Faces = new FaceComponent(this);
            this.Photos = new PhotoComponent(this);
            this.NavMap = new NavMapComponent(this);
            this.Capabilities = new Capabilities(this);

            // Handle the robot state event
            this.Events.RobotState += Events_RobotState;

            // Update the connected state if processing event changes
            this.Events.PropertyChanged += (sender, e) =>
            {
                if (e.PropertyName == nameof(Events.IsProccessingEvents))
                {
                    OnPropertyChanged(nameof(IsConnected));
                    if (IsConnected) Connected?.Invoke(this, new ConnectedEventArgs());
                }
            };
        }

        /// <summary>
        /// Handles the RobotState event of the Events control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="RobotStateEventArgs"/> instance containing the event data.</param>
        private async void Events_RobotState(object sender, RobotStateEventArgs e)
        {
            // Vector is delocalized if his pose is no longer comparable.
            bool delocalized = !Pose.IsComparable(e.Pose);

            Acceleration = e.Acceleration;
            CarryingObjectId = e.CarryingObjectId;
            Gyro = e.Gyro;
            HeadAngleRad = e.HeadAngleRad;
            HeadTrackingObjectId = e.HeadTrackingObjectId;
            LastImageTimestamp = e.LastImageTimestamp;
            LeftWheelSpeedMmps = e.LeftWheelSpeedMmps;
            LiftHeightMm = e.LiftHeightMm;
            LocalizedToObjectId = e.LocalizedToObjectId;
            Pose = e.Pose;
            PoseAngleRad = e.PoseAngleRad;
            PosePitchRad = e.PosePitchRad;
            Proximity = e.Proximity;
            RightWheelSpeedMmps = e.RightWheelSpeedMmps;
            Status = e.Status;
            Touch = e.Touch;

            // Trigger the delocalized event
            if (delocalized) Delocalized?.Invoke(this, new DelocalizedEventArgs());

            // Ensures the image stream is enabled
            await Camera.EnsureStreamEnabled().ConfigureAwait(false);
        }

        /// <summary>
        /// Connect to Vector on the local network.  This will attempt to connect using the configured IP address (if provided) otherwise it will
        /// trigger an mDNS query to find Vector's IP address.  For connections to Vector over the Internet, use the <see cref="RemoteConnect(IRemoteRobotConfiguration, int)"/> method instead.
        /// </summary>
        /// <param name="robotConfiguration">The robot configuration.</param>
        /// <param name="timeout">Timeout in milliseconds</param>
        /// <param name="useZeroConfig">if set to <c>true</c> use zero configuration (mDNS).</param>
        /// <returns>A task that represents the asynchronous operation.</returns>
        /// <exception cref="ArgumentNullException">robotConfiguration</exception>
        /// <exception cref="VectorNotFoundException">Unable to establish a connection to Vector.</exception>
        /// <exception cref="VectorInvalidVersionException">Your SDK version is not compatible with Vector’s version.</exception>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "Client is disposed in disconnect method")]
        public async Task Connect(IRobotConfiguration robotConfiguration, int timeout = DefaultConnectionTimeout, bool useZeroConfig = true)
        {
            if (robotConfiguration == null) throw new ArgumentNullException(nameof(robotConfiguration));

            // If already connected, do nothing.
            if (IsConnected) return;

            // Reset disconnecting flag
            disconnecting = false;

            // The robot IP address   
            Task<Client> connectionTask = null;
            var ipAddress = robotConfiguration.IPAddress;

            // If using mDNS then lookup IP address 
            if (useZeroConfig)
            {
                // If an IP address is specified, try and connect to that IP while looking up additional IP
                if (ipAddress != null)
                {
                    // Create connection to default IP address
                    connectionTask = Client.Connect(robotConfiguration, ipAddress, timeout);
                    // Search for updated IP address
                    var ipAddressTask = robotConfiguration.FindRobotAddress();

                    // Run both connection and IP address lookup at the same time
                    var completedTask = await Task.WhenAny(connectionTask, ipAddressTask).ConfigureAwait(false);

                    if (completedTask == ipAddressTask && !ipAddressTask.IsFaulted && !ipAddressTask.Result.Equals(ipAddress))
                    {
                        // If IP address lookup completed first and IP doesn't match then try again below with new IP
                        ipAddress = ipAddressTask.Result;
                        // Ensure ignored connection is disposed 
                        _ = connectionTask.ContinueWith(task =>
                        {
                            if (task.IsFaulted)
                            {
                                if (task.Exception.InnerException is VectorConnectionException) return;
                                throw task.Exception;
                            }
                            if (task.IsCompleted)
                            {
                                task.Result.Dispose();
                            }
                        }, TaskScheduler.Default).ConfigureAwait(false);
                        connectionTask = null;
                    }
                    else if (completedTask == connectionTask && connectionTask.IsFaulted)
                    {
                        // If connection is faulted then wait for IP address and try again below
                        ipAddress = await ipAddressTask.ConfigureAwait(false);
                        connectionTask = null;
                    }
                }
                else
                {
                    // If no IP address specified look it up
                    ipAddress = await robotConfiguration.FindRobotAddress().ConfigureAwait(false);
                    connectionTask = null;
                }
            }

            // If cannot find IP address then error out
            if (ipAddress == null)
            {
                throw new VectorNotFoundException("Could not find Vector and no IP address specified.");
            }

            // If connection task not created, create one here.
            if (connectionTask == null)
            {
                connectionTask = Client.Connect(robotConfiguration, ipAddress, timeout);
            }

            // Start the event loop
            await StartConnection(await connectionTask.ConfigureAwait(false)).ConfigureAwait(false);
        }

        /// <summary>
        /// Connects to the robot using the remote connection information.  This is used for connecting to Vector when he's not on the LAN.  This requires port forwarding on your 
        /// router to setup an external connection to Vector.  For connecting to Vector on the same network as this application, use the <see cref="Connect(IRobotConfiguration, int, bool)"/> method instead.
        /// </summary>
        /// <param name="robotConfiguration">The remote robot configuration.</param>
        /// <param name="timeout">The timeout.</param>
        /// <returns>A task that represents the asynchronous operation.</returns>
        /// <exception cref="ArgumentNullException">robotConfiguration</exception>
        /// <exception cref="VectorConfigurationException">Remote host not specified for remote connection.</exception>
        public async Task RemoteConnect(IRemoteRobotConfiguration robotConfiguration, int timeout = DefaultConnectionTimeout)
        {
            if (robotConfiguration == null) throw new ArgumentNullException(nameof(robotConfiguration));
            if (string.IsNullOrWhiteSpace(robotConfiguration.RemoteHost)) throw new VectorConfigurationException("Remote host not specified for remote connection.");

            // If already connected, do nothing.
            if (IsConnected) return;

            // Reset disconnecting flag
            disconnecting = false;

            // Start the event loop
            await StartConnection(await Client.RemoteConnect(robotConfiguration, timeout).ConfigureAwait(false)).ConfigureAwait(false);
        }

        /// <summary>
        /// Starts the connection by saving the client and initiating the event loop.  After this method return the robot is fully connected.
        /// </summary>
        /// <param name="client">The client.</param>
        /// <returns>A task that represents the asynchronous operation.</returns>
        private async Task StartConnection(Client client)
        {
            try
            {
                // Save the client instance
                this.client = client;
                // Gets the firmware version from the robot and updates the firmware version property
                var versionState = await this.ReadVersionState().ConfigureAwait(false);
                Version version = new Version();
                if (Version.TryParse(versionState.OsVersion, out version)) FirmwareVersion = version;
                OnPropertyChanged(nameof(FirmwareVersion));
                OnPropertyChanged(nameof(Capabilities));
                // Start the event loop
                await this.Events.Start().ConfigureAwait(false);
            }
            catch (Exception)
            {
                client.Dispose();
                this.client = null;
                throw;
            }
        }

        /// <summary>
        /// Reads the state of the battery from the robot.
        /// </summary>
        /// <returns>A task that represents the asynchronous operation; the task result contains the battery state.</returns>
        public async Task<BatteryState> ReadBatteryState()
        {
            var response = await RunMethod(r => r.BatteryStateAsync(new BatteryStateRequest())).ConfigureAwait(false);
            response.Status.EnsureSuccess();
            return new BatteryState(response);
        }

        /// <summary>
        /// Reads the the versioning information for Vector, including Vector’s OS version and engine build id.
        /// </summary>
        /// <returns>A task that represents the asynchronous operation; The task result contains robot version information.</returns>
        public async Task<VersionState> ReadVersionState()
        {
            var response = await RunMethod(r => r.VersionStateAsync(new VersionStateRequest())).ConfigureAwait(false);
            response.Status.EnsureSuccess();
            return new VersionState(response);
        }

        /// <summary>
        /// Request the list of the current feature flags.
        /// </summary>
        /// <returns>A task that represents the asynchronous operation; the task result contains the robot setting.</returns>
        /// <remarks>To see which flags are enabled, use the Get Feature Flag command.</remarks>
        public async Task<IReadOnlyList<string>> GetFeatureFlagList()
        {
            var response = await RunMethod(client => client.GetFeatureFlagListAsync(new FeatureFlagListRequest())).ConfigureAwait(false);
            response.Status.EnsureSuccess();
            return response.List.ToList().AsReadOnly();
        }
        
        /// <summary>
        /// Request the current setting of a feature flag.
        /// </summary>
        /// <param name="name">The name of the feature to retrieve information about</param>
        /// <returns>A task that represents the asynchronous operation; the task result contains the robot setting.</returns>
        public async Task<bool> GetFeatureFlag(string name)
        {
            var response = await RunMethod(client => client.GetFeatureFlagAsync(new FeatureFlagRequest()
            {
                FeatureName = name
            })).ConfigureAwait(false);
            response.Status.EnsureSuccess();

            // Is the feature name valid?
            if (!response.ValidFeature) return false;
            return response.FeatureEnabled;
        }

        /// <summary>
        /// Gets the settings from the robot.
        /// </summary>
        /// <returns>A task that represents the asynchronous operation; the task result contains the robot setting.</returns>
        public async Task<RobotSettings> ReadRobotSettings()
        {
            var request = new PullJdocsRequest();
            request.JdocTypes.Add(ExternalInterface.JdocType.RobotSettings);
            var response = await RunMethod(r => r.PullJdocsAsync(request)).ConfigureAwait(false);
            response.Status.EnsureSuccess();
            return RobotSettings.FromNamedJdoc(response.NamedJdocs.FirstOrDefault());
        }

        /// <summary>
        /// Updates the robot settings.
        /// </summary>
        /// <param name="settings">The settings to update.</param>
        /// <returns>A task that represents the asynchronous operation; the task result contains the updated settings.</returns>
        public async Task<RobotSettings> UpdateRobotSettings(RobotSettings settings)
        {
            if (settings == null) throw new ArgumentNullException(nameof(settings));
            var response = await client.RunHttpCommand<UpdateSettingsResponse, UpdateSettingsRequest>("/v1/update_settings", new UpdateSettingsRequest()
            {
                Settings = settings.ToRobotSettingsConfig()
            }).ConfigureAwait(false);
            if (response.Code != ResultCode.SettingsAccepted) return null;
            return RobotSettings.FromJdoc(response.Doc);
        }

        /// <summary>
        /// Gets the robot lifetime stats.
        /// </summary>
        /// <returns>A task that represents the asynchronous operation; the task result contains the robot lifetime stats.</returns>
        public async Task<RobotLifetimeStats> ReadRobotLifetimeStats()
        {
            var request = new PullJdocsRequest();
            request.JdocTypes.Add(ExternalInterface.JdocType.RobotLifetimeStats);
            var response = await RunMethod(r => r.PullJdocsAsync(request)).ConfigureAwait(false);
            response.Status.EnsureSuccess();
            return RobotLifetimeStats.FromNamedJdoc(response.NamedJdocs.FirstOrDefault());
        }

        /// <summary>
        /// Requests information about the most recent attention transfer
        /// </summary>
        /// <returns>A task that represents the asynchronous operation; the task result contains the latest attention transfer.</returns>
        public async Task<Types.LatestAttentionTransfer> GetLatestAttentionTransfer()
        {
            var response = await RunMethod(client => client.GetLatestAttentionTransferAsync(new LatestAttentionTransferRequest())).ConfigureAwait(false);
            response.Status.EnsureSuccess();
            // There wasn't any reason given 
            if (response.LatestAttentionTransfer.AttentionTransfer == null) return null;
            return new Types.LatestAttentionTransfer(response.LatestAttentionTransfer);
        }

        /// <summary>
        /// Cycles the update-engine service (to start a new check for an update) and sets up a stream of <see cref="Events.CheckUpdateStatusEventArgs"/> Events.
        /// </summary>
        /// <returns>A task that represents the asynchronous operation; the task result contains the update status.</returns>
        public async Task<CheckUpdateStatus> StartUpdateEngine()
        {
            var response = await RunMethod(client => client.StartUpdateEngineAsync(new CheckUpdateStatusRequest())).ConfigureAwait(false);
            response.Status.EnsureSuccess();
            return new CheckUpdateStatus(response);
        }

        /// <summary>
        /// Check if the robot is ready to reboot and update.
        /// </summary>
        /// <returns>A task that represents the asynchronous operation; the task result contains the update status.</returns>
        public async Task<CheckUpdateStatus> CheckUpdateStatus()
        {
            var response = await RunMethod(client => client.CheckUpdateStatusAsync(new CheckUpdateStatusRequest())).ConfigureAwait(false);
            response.Status.EnsureSuccess();
            return new CheckUpdateStatus(response);
        }

        /// <summary>
        /// Updates and restart the robot.
        /// </summary>
        /// <returns>A task that represents the asynchronous operation; the task result contains the result from the robot.</returns>
        public async Task<Types.StatusCode> UpdateAndRestart()
        {
            var response = await RunMethod(client => client.UpdateAndRestartAsync(new UpdateAndRestartRequest())).ConfigureAwait(false);
            return response.Status.Code.Convert();
        }

        /// <summary>
        /// Checks the cloud connection to Vector
        /// </summary>
        /// <returns>A task that represents the asynchronous operation; the task result contains the cloud connection information.</returns>
        public async Task<CloudConnection> CheckCloudConnection()
        {
            var response = await RunMethod(client => client.CheckCloudConnectionAsync(new CheckCloudRequest())).ConfigureAwait(false);
            response.Status.EnsureSuccess();
            return new CloudConnection(response);
        }

        /// <summary>
        /// Disconnects from the Robot
        /// </summary>
        /// <returns>A task that represents the asynchronous operation.</returns>
        public Task Disconnect()
        {
            return Disconnect(false);
        }

        /// <summary>
        /// Disconnects from the Robot.
        /// </summary>
        /// <param name="forced">if set to <c>true</c> the shutdown is forced due to disconnect from Vector.</param>
        /// <returns>A task that represents the asynchronous operation.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Ignoring exceptions on shutdown")]
        internal async Task Disconnect(bool forced)
        {
            // If we are already disconnecting do not allow re-entry
            if (disconnecting) return;

            // If no client, we've already disconnected
            if (client == null) return;

            // Mark as disconnected to the disconnect event doesn't cause a loop here
            disconnecting = true;

            // Exception triggered during teardown
            Exception exception = null;

            try
            {
                // End all component processing
                await Task.WhenAll(
                    Vision.Teardown(forced),
                    Audio.Teardown(forced),
                    Camera.Teardown(forced),
                    Animation.Teardown(forced),
                    Behavior.Teardown(forced),
                    Control.Teardown(forced),
                    Events.Teardown(forced),
                    Screen.Teardown(forced),
                    World.Teardown(forced),
                    Faces.Teardown(forced),
                    Photos.Teardown(forced),
                    NavMap.Teardown(forced)
                ).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                // Save exception for later
                exception = ex;
            }

            // Clear the status;
            Status = new RobotStatus(0);
            Acceleration = new Acceleration();
            CarryingObjectId = -1;
            Gyro = new AngularVelocity();
            HeadAngleRad = 0;
            HeadTrackingObjectId = -1;
            LastImageTimestamp = 0;
            LeftWheelSpeedMmps = 0;
            LiftHeightMm = 0;
            LocalizedToObjectId = -1;
            Pose = new Pose();
            PoseAngleRad = 0;
            PosePitchRad = 0;
            Proximity = new ProximitySensorData();
            RightWheelSpeedMmps = 0;
            Touch = new TouchSensorData();

            client.Dispose();
            client = null;

            // No longer disconnecting
            disconnecting = false;

            // Raise disconnected event
            Disconnected?.Invoke(this, new DisconnectedEventArgs());

            // Rethrow exception if one is triggered during teardown
            if (exception != null) System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(exception).Throw();
        }

        /// <summary>
        /// Runs the client behavior method.  If control was lost, this method will attempt to gain control.  If control was not requested
        /// this method will throw an exception.
        /// </summary>
        /// <typeparam name="T">The result type of the command</typeparam>
        /// <param name="command">The command.</param>
        /// <param name="methodName">Name of the method.</param>
        /// <returns>A task that represents the asynchronous operation; the task result contains the result of the command.</returns>
        internal Task<T> RunControlMethod<T>(Func<ExternalInterfaceClient, AsyncUnaryCall<T>> command, [CallerMemberName]string methodName = null)
        {
            return RunControlMethod(robot => command(robot).ResponseAsync, methodName);
        }

        /// <summary>
        /// Runs the client behavior method.  If control was lost, this method will attempt to gain control.  If control was not requested
        /// this method will throw an exception.
        /// </summary>
        /// <typeparam name="T">The result type of the command</typeparam>
        /// <param name="command">The command.</param>
        /// <param name="methodName">Name of the method.</param>
        /// <returns>A task that represents the asynchronous operation; the task result contains the result of the command.</returns>
        /// <exception cref="VectorNotConnectedException">Vector is not connected.</exception>
        /// <exception cref="VectorControlException">Unable to acquire control of Vector.</exception>
        internal async Task<T> RunControlMethod<T>(Func<ExternalInterfaceClient, Task<T>> command, [CallerMemberName]string methodName = null)
        {
            // Cannot run method if not connected
            if (!IsConnected) throw new VectorNotConnectedException("Vector is not connected.");

            // If we don't have control and we aren't maintaining control then throw exception
            if (!this.Control.HasControl && !Control.MaintainBehaviorControl)
            {
                throw new VectorControlException($"Method {methodName ?? "unknown"} requires behavior control.");
            }

            // If we don't have control, request it.
            if (!this.Control.HasControl) await Control.RequestControl().ConfigureAwait(false);

            // If we still don't have control, throw exception
            if (!this.Control.HasControl) throw new VectorControlException("Unable to acquire control of Vector.");

            return await client.RunCommand(command).ConfigureAwait(false);
        }

        /// <summary>
        /// Runs the client method.
        /// </summary>
        /// <typeparam name="T">The result type of the command</typeparam>
        /// <param name="command">The command.</param>
        /// <returns>A task that represents the asynchronous operation; the task result contains the result of the command.</returns>
        internal Task<T> RunMethod<T>(Func<ExternalInterfaceClient, AsyncUnaryCall<T>> command)
        {
            return RunMethod(robot => command(robot).ResponseAsync);
        }

        /// <summary>
        /// Runs the client method.
        /// </summary>
        /// <typeparam name="T">The result type of the command</typeparam>
        /// <param name="command">The command.</param>
        /// <returns>A task that represents the asynchronous operation; the task result contains the result of the command.</returns>
        internal async Task<T> RunMethod<T>(Func<ExternalInterfaceClient, Task<T>> command)
        {
            // Cannot run method if not connected
            // We don't use IsConnected here because these methods might run during startup or shutdown
            if (client == null) throw new VectorNotConnectedException("Vector is not connected.");
            return await client.RunCommand(command).ConfigureAwait(false);
        }

        /// <summary>
        /// Starts a client stream.
        /// </summary>
        /// <typeparam name="TResponse">The type of the response.</typeparam>
        /// <param name="command">The command to run.</param>
        /// <returns>The async GRPC stream</returns>
        internal AsyncServerStreamingCall<TResponse> StartStream<TResponse>(Func<ExternalInterfaceClient, AsyncServerStreamingCall<TResponse>> command)
        {
            // Cannot run method if not connected
            if (!IsConnected) throw new VectorNotConnectedException("Vector is not connected.");
            return client.RunCommand(command);
        }

        /// <summary>
        /// Starts a duplex client stream.
        /// </summary>
        /// <typeparam name="TRequest">The type of the request.</typeparam>
        /// <typeparam name="TResponse">The type of the response.</typeparam>
        /// <param name="command">The command to run.</param>
        /// <returns>The duplex async GRPC stream</returns>
        internal AsyncDuplexStreamingCall<TRequest, TResponse> StartStream<TRequest, TResponse>(Func<ExternalInterfaceClient, AsyncDuplexStreamingCall<TRequest, TResponse>> command)
        {
            // Cannot run method if not connected
            if (!IsConnected) throw new VectorNotConnectedException("Vector is not connected.");
            return client.RunCommand(command);
        }
            
        #region Dispose Pattern

        /// <summary>
        /// To detect redundant calls
        /// </summary>
        private bool disposedValue = false;

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources.
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing) Task.Run(async () => await Disconnect().ConfigureAwait(false)).Wait();
                disposedValue = true;
            }
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}

<|start_filename|>Anki.Vector/Types/CheckUpdateStatus.cs<|end_filename|>
﻿// <copyright file="CheckUpdateStatus.cs" company="<NAME>">
//     Copyright (c) 2020 <NAME>. All rights reserved.
// </copyright>

using System;
using System.Collections.Generic;
using System.Text;
using Anki.Vector.ExternalInterface;

namespace Anki.Vector.Types
{
    /// <summary>
    /// Update status enum
    /// </summary>
    public enum UpdateStatus
    {
        /// <summary>The no update to the current firmware</summary>
        NoUpdate = 0,
        /// <summary>New firmware is ready to install</summary>
        ReadyToInstall = 1,
        /// <summary>The download is in progress</summary>
        InProgressDownload = 2,
    }

    /// <summary>
    /// The result of check update status methods.
    /// </summary>
    public class CheckUpdateStatus
    {
        /// <summary>Gets the expected number of bytes to download</summary>
        public long Expected { get; }

        /// <summary>Gets the current number of bytes downloaded</summary>
        public long Progress { get; }

        /// <summary>Gets the update status.</summary>
        public UpdateStatus UpdateStatus { get; }

        /// <summary>
        /// Gets the update version.
        /// </summary>
        public string UpdateVersion { get; }

        /// <summary>
        /// Initializes a new instance of the <see cref="CheckUpdateStatus"/> class.
        /// </summary>
        /// <param name="response">The response.</param>
        internal CheckUpdateStatus(CheckUpdateStatusResponse response)
        {
            Expected = response.Expected;
            Progress = response.Progress;
            UpdateStatus = (UpdateStatus)response.UpdateStatus;
            UpdateVersion = response.UpdateVersion;
        }
    }
}


<|start_filename|>Anki.Vector/Events/ImageReceivedEventArgs.cs<|end_filename|>
﻿// <copyright file="ImageReceivedEventArgs.cs" company="<NAME>">
//     Copyright (c) 2020 <NAME>. All rights reserved.
// </copyright>

using System;
using Anki.Vector.Types;

namespace Anki.Vector.Events
{
    /// <summary>
    /// Image received event args
    /// </summary>
    /// <seealso cref="System.EventArgs" />
    public class ImageReceivedEventArgs : EventArgs
    {
        /// <summary>
        /// Gets the image.
        /// </summary>
        public Image Image { get; }

        /// <summary>
        /// Initializes a new instance of the <see cref="ImageReceivedEventArgs" /> class.
        /// </summary>
        /// <param name="response">The response.</param>
        internal ImageReceivedEventArgs(ExternalInterface.CameraFeedResponse response)
        {
            Image = new Image(response);
        }
    }
}


<|start_filename|>docs/api/Anki.Vector.Types.html<|end_filename|>
﻿<!DOCTYPE html>
<!--[if IE]><![endif]-->
<html>
  
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Namespace Anki.Vector.Types
   </title>
    <meta name="viewport" content="width=device-width">
    <meta name="title" content="Namespace Anki.Vector.Types
   ">
    <meta name="generator" content="docfx 2.48.1.0">
    
    <link rel="shortcut icon" href="../favicon.ico">
    <link rel="stylesheet" href="../styles/docfx.vendor.css">
    <link rel="stylesheet" href="../styles/docfx.css">
    <link rel="stylesheet" href="../styles/main.css">
    <meta property="docfx:navrel" content="../toc.html">
    <meta property="docfx:tocrel" content="toc.html">
    
    <meta property="docfx:rel" content="../">
    
  </head>
  <body data-spy="scroll" data-target="#affix" data-offset="120">
    <div id="wrapper">
      <header>
        
        <nav id="autocollapse" class="navbar navbar-inverse ng-scope" role="navigation">
          <div class="container-fluid">
            <div class="navbar-header">
              <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
              </button>
              
              <a class="navbar-brand" href="../index.html">
                <img id="logo" class="svg" src="../logo.svg" alt="">
                  .NET SDK Documentation
              </a>
            </div>
            <div class="collapse navbar-collapse" id="navbar">
              <form class="navbar-form navbar-right" role="search" id="search">
                <div class="form-group">
                  <input type="text" class="form-control" id="search-query" placeholder="Search" autocomplete="off">
                </div>
              </form>
            </div>
          </div>
        </nav>
        
        <div class="subnav navbar navbar-default">
          <div class="container-fluid hide-when-search" id="breadcrumb">
            <ul class="breadcrumb">
              <li></li>
            </ul>
          </div>
        </div>
      </header>
      <div class="container-fluid body-content">
        
        <div id="search-results">
          <div class="search-list"></div>
          <div class="sr-items">
            <p><i class="glyphicon glyphicon-refresh index-loading"></i></p>
          </div>
          <ul id="pagination"></ul>
        </div>
      </div>
      <div role="main" class="container-fluid body-content hide-when-search">
        
        <div class="sidenav hide-when-search" style="margin-left: -15px">
          <a class="btn toc-toggle collapse" data-toggle="collapse" href="#sidetoggle" aria-expanded="false" aria-controls="sidetoggle">Show / Hide Table of Contents</a>
          <div class="sidetoggle collapse" id="sidetoggle">
            <div id="sidetoc"></div>
          </div>
        </div>
        <div class="article row grid-right">
          <div class="col-md-10">
            <article class="content wrap" id="_content" data-uid="Anki.Vector.Types" style="max-width: 992px">
  
  <h1 id="Anki_Vector_Types" data-uid="Anki.Vector.Types" class="text-break">Namespace Anki.Vector.Types
  </h1>
  <div class="markdown level0 summary"></div>
  <div class="markdown level0 conceptual"></div>
  <div class="markdown level0 remarks"></div>
    <h3 id="classes">Classes
  </h3>
      <h4><a class="xref" href="Anki.Vector.Types.ActionResult.html">ActionResult</a></h4>
      <section><p>The result of the action method</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.Angle.html">Angle</a></h4>
      <section><p>Angle extensions</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.Animation.html">Animation</a></h4>
      <section><p>Animation that can be played by the robot.</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.AnimationTrigger.html">AnimationTrigger</a></h4>
      <section><p>Animation trigger that can be played by the robot.</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.BatteryState.html">BatteryState</a></h4>
      <section><p>The state of the robot battery.</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.BehaviorResult.html">BehaviorResult</a></h4>
      <section><p>Result from the behavior methods</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.CameraConfig.html">CameraConfig</a></h4>
      <section><p>The fixed properties for Vector's camera.</p>
<p>
A full 3x3 calibration matrix for doing 3D reasoning based on the camera images would look like:
<p>+--------------+--------------+---------------+
|focal_length.x|      0       |    center.x   |
+--------------+--------------+---------------+
|       0      |focal_length.y|    center.y   |
+--------------+--------------+---------------+
|       0      |       0      |        1      |
+--------------+--------------+---------------+</p>
</p>
<remarks></remarks>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.CheckUpdateStatus.html">CheckUpdateStatus</a></h4>
      <section><p>The result of check update status methods.</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.CloudConnection.html">CloudConnection</a></h4>
      <section><p>Cloud connection information</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.CubeBatteryState.html">CubeBatteryState</a></h4>
      <section><p>The state of the cube battery.</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.Image.html">Image</a></h4>
      <section><p>Represents a single image captured from the robot.</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.ImageRect.html">ImageRect</a></h4>
      <section><p>Image coordinates and size</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.KnownFace.html">KnownFace</a></h4>
      <section><p>A face that Vector has detected.</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.LatestAttentionTransfer.html">LatestAttentionTransfer</a></h4>
      <section><p>Vector attention transfer info event args</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.Light.html">Light</a></h4>
      <section><p>Lights are used with Vector's Cube.</p>
<p>Lights may either be &quot;on&quot; or &quot;off&quot;, though in practice any colors may be assigned to either state (including no color/light).</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.MotionProfile.html">MotionProfile</a></h4>
      <section><p>Tells Vector how to drive when receiving navigation and movement actions such as GoToPose and DockWithCube.</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.NavMapGrid.html">NavMapGrid</a></h4>
      <section><p>A navigation memory map, stored as a quad-tree.</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.NavMapGridNode.html">NavMapGridNode</a></h4>
      <section><p>A node in the NavMap</p>
<p>Leaf nodes contain content, all other nodes are split into 4 equally sized children.</p>
<p>Child node indices are stored in the following X,Y orientation:
<p>+---+----+---+
| ^ | 2  | 0 |
+---+----+---+
| Y | 3  | 1 |
+---+----+---+
|   | X-&gt;|   |
+---+----+---+</p>
</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.PhotoInfo.html">PhotoInfo</a></h4>
      <section><p>Store photo information</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.Pose.html">Pose</a></h4>
      <section><p>Represents where an object is in the world.</p>
<p>Whenever Vector is delocalized (i.e.whenever Vector no longer knows where he is - e.g.when he&apos;s picked up), Vector creates a new pose starting at
(0,0,0) with no rotation, with origin_id incremented to show that these poses cannot be compared with earlier ones. As Vector drives around, his 
pose (and the pose of other objects he observes - e.g.faces, his LightCube, charger, etc.) is relative to this initial position and orientation.</p>
<p>The coordinate space is relative to Vector, where Vector&apos;s origin is the  point on the ground between Vector&apos;s two front wheels. The X axis is 
Vector&apos;s forward direction, the Y axis is to Vector&apos;s left, and the Z axis is up.</p>
<p>Only poses of the same origin can safely be compared or operated on.</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.RobotLifetimeStats.html">RobotLifetimeStats</a></h4>
      <section><p>Vector lifetime statistics</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.RobotSettings.html">RobotSettings</a></h4>
      <section><p>THe permanently stored Vector robot settings</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.StatusCodeExtensions.html">StatusCodeExtensions</a></h4>
      <section><p>Extensions for status codes</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.VersionState.html">VersionState</a></h4>
      <section><p>The versioning information for Vector.</p>
</section>
    <h3 id="structs">Structs
  </h3>
      <h4><a class="xref" href="Anki.Vector.Types.Acceleration.html">Acceleration</a></h4>
      <section><p>Represents acceleration values</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.AngularVelocity.html">AngularVelocity</a></h4>
      <section><p>Represents the angular velocity</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.Color.html">Color</a></h4>
      <section><p>Extensions for working with robot colors</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.ColorProfile.html">ColorProfile</a></h4>
      <section><p>Applies transforms to make Vector’s lights and colors appear as intended, by limiting maximum channel intensity.</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.Matrix.html">Matrix</a></h4>
      <section><p>Simplified matrix struct</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.Point.html">Point</a></h4>
      <section><p>Represents a point.</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.Position.html">Position</a></h4>
      <section><p>Represents the position of an object in the world.  A position consists of its x, y and z values in millimeters.</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.Quaternion.html">Quaternion</a></h4>
      <section><p>Represents the rotation of an object in the world.</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.Vector2.html">Vector2</a></h4>
      <section><p>Represents a 2D Vector (type/units aren't specified)</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.Vector3.html">Vector3</a></h4>
      <section><p>Represents a 3D Vector (type/units aren't specified)</p>
</section>
    <h3 id="interfaces">Interfaces
  </h3>
      <h4><a class="xref" href="Anki.Vector.Types.IAnimation.html">IAnimation</a></h4>
      <section><p>Interface for animation and animation triggers</p>
</section>
    <h3 id="enums">Enums
  </h3>
      <h4><a class="xref" href="Anki.Vector.Types.ActionResultCode.html">ActionResultCode</a></h4>
      <section><p>The result of an action</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.AttentionTransferReason.html">AttentionTransferReason</a></h4>
      <section><p>The reason for the last attention transfer</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.BatteryLevel.html">BatteryLevel</a></h4>
      <section><p>Battery level</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.BehaviorResultCode.html">BehaviorResultCode</a></h4>
      <section><p>The result of a behavior</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.ButtonWakeWord.html">ButtonWakeWord</a></h4>
      <section><p>The service that responds when clicking Vector's back button</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.CloudConnectionCode.html">CloudConnectionCode</a></h4>
      <section><p>Cloud connection code</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.CubeBatteryLevel.html">CubeBatteryLevel</a></h4>
      <section><p>Cube battery level</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.CustomObjectMarker.html">CustomObjectMarker</a></h4>
      <section><p>Defines all available custom object markers.</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.CustomObjectType.html">CustomObjectType</a></h4>
      <section><p>Custom object type.</p>
<p>Vector has 20 slots for custom object types.  You can define a <span class="xref">Anki.Vector.ExternalInterface.CustomBoxDefinition</span>, <span class="xref">Anki.Vector.ExternalInterface.CustomCubeDefinition</span>, or <span class="xref">Anki.Vector.ExternalInterface.CustomWallDefinition</span> 
in these slots and Vector will recognize objects of those types.</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.EyeColor.html">EyeColor</a></h4>
      <section><p>Vector's eye color setting</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.ImageEncoding.html">ImageEncoding</a></h4>
      <section><p>The image encoding</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.NavNodeContentType.html">NavNodeContentType</a></h4>
      <section><p>The content types for a <a class="xref" href="Anki.Vector.Types.NavMapGridNode.html">NavMapGridNode</a>.</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.RobotVolume.html">RobotVolume</a></h4>
      <section><p>The permanent settings volume level</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.StatusCode.html">StatusCode</a></h4>
      <section><p>Status result</p>
</section>
      <h4><a class="xref" href="Anki.Vector.Types.UpdateStatus.html">UpdateStatus</a></h4>
      <section><p>Update status enum</p>
</section>
</article>
          </div>
          
          <div class="hidden-sm col-md-2" role="complementary">
            <div class="sideaffix">
              <div class="contribution">
                <ul class="nav">
                </ul>
              </div>
              <nav class="bs-docs-sidebar hidden-print hidden-xs hidden-sm affix" id="affix">
              <!-- <p><a class="back-to-top" href="#top">Back to top</a><p> -->
              </nav>
            </div>
          </div>
        </div>
      </div>
      
      <footer>
        <div class="grad-bottom"></div>
        <div class="footer">
          <div class="container-fluid">
            <span class="pull-right">
              <a href="#top">Back to top</a>
            </span>
            
            <span>Copyright (c) 2019 by <NAME></span>
          </div>
        </div>
      </footer>
    </div>
    
    <script type="text/javascript" src="../styles/docfx.vendor.js"></script>
    <script type="text/javascript" src="../styles/docfx.js"></script>
    <script type="text/javascript" src="../styles/main.js"></script>
  </body>
</html>


<|start_filename|>Anki.Vector/Types/Image.cs<|end_filename|>
﻿// <copyright file="Image.cs" company="<NAME>">
//     Copyright (c) 2020 <NAME>. All rights reserved.
// </copyright>

using System;
using System.Collections.Generic;
using System.Text;
using Anki.Vector.ExternalInterface;

namespace Anki.Vector.Types
{
    /// <summary>
    /// Represents a single image captured from the robot.
    /// </summary>
    public class Image
    {
        /// <summary>
        /// Gets the image identifier.
        /// </summary>
        public uint ImageId { get; }

        /// <summary>
        /// Gets the image data.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "Image data")]
        public byte[] Data { get; }

        /// <summary>
        /// Gets the frame robot timestamp.
        /// </summary>
        public uint FrameTimestamp { get; }

        /// <summary>
        /// Gets the image encoding.
        /// </summary>
        public ImageEncoding Encoding { get; }

        /// <summary>
        /// Initializes a new instance of the <see cref="Image" /> class from a camera feed response.
        /// </summary>
        /// <param name="response">The response.</param>
        internal Image(ExternalInterface.CameraFeedResponse response)
        {
            ImageId = response.ImageId;
            Data = response.Data.ToByteArray();
            Encoding = MapImageEncoding(response.ImageEncoding);
            FrameTimestamp = response.FrameTimeStamp;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Image"/> class from a single image response.
        /// </summary>
        /// <param name="response">The response.</param>
        internal Image(ExternalInterface.CaptureSingleImageResponse response)
        {
            ImageId = response.ImageId;
            Data = response.Data.ToByteArray();
            Encoding = MapImageEncoding(response.ImageEncoding);
            FrameTimestamp = response.FrameTimeStamp;
        }

        /// <summary>
        /// Maps the image encoding.
        /// </summary>
        /// <param name="imageEncoding">The robot image encoding.</param>
        /// <returns>The SDK image encoding</returns>
        internal static ImageEncoding MapImageEncoding(ImageChunk.Types.ImageEncoding imageEncoding)
        {
            switch (imageEncoding)
            {
                case ImageChunk.Types.ImageEncoding.NoneImageEncoding: return ImageEncoding.NoneImageEncoding;
                case ImageChunk.Types.ImageEncoding.RawGray: return ImageEncoding.RawGray;
                case ImageChunk.Types.ImageEncoding.RawRgb: return ImageEncoding.RawRgb;
                case ImageChunk.Types.ImageEncoding.Yuyv: return ImageEncoding.Yuyv;
                case ImageChunk.Types.ImageEncoding.Yuv420Sp: return ImageEncoding.Yuv420Sp;
                case ImageChunk.Types.ImageEncoding.Bayer: return ImageEncoding.Bayer;
                case ImageChunk.Types.ImageEncoding.JpegGray: return ImageEncoding.JpegGray;
                case ImageChunk.Types.ImageEncoding.JpegColor: return ImageEncoding.JpegColor;
                case ImageChunk.Types.ImageEncoding.JpegColorHalfWidth: return ImageEncoding.JpegColorHalfWidth;
                case ImageChunk.Types.ImageEncoding.JpegMinimizedGray: return ImageEncoding.JpegMinimizedGray;
                case ImageChunk.Types.ImageEncoding.JpegMinimizedColor: return ImageEncoding.JpegMinimizedColor;
                default:
                    throw new NotSupportedException($"ImageEncoding {imageEncoding} is not supported");
            }
        }
    }
}


<|start_filename|>Anki.Vector/Exceptions/VectorNotFoundException.cs<|end_filename|>
﻿// <copyright file="VectorNotFoundException.cs" company="<NAME>">
//     Copyright (c) 2020 <NAME>. All rights reserved.
// </copyright>

using System;

namespace Anki.Vector.Exceptions
{
    /// <summary>
    /// Unable to establish a connection to Vector.
    /// <para>Make sure you’re on the same network, and Vector is connected to the Internet.</para>
    /// </summary>
    /// <seealso cref="Anki.Vector.Exceptions.VectorException" />
    public class VectorNotFoundException : VectorConnectionException
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="VectorNotFoundException"/> class.
        /// </summary>
        internal VectorNotFoundException() : base()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="VectorNotFoundException"/> class.
        /// </summary>
        /// <param name="message">The message that describes the error.</param>
        internal VectorNotFoundException(string message) : base(message)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="VectorNotFoundException"/> class.
        /// </summary>
        /// <param name="message">The error message that explains the reason for the exception.</param>
        /// <param name="innerException">The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.</param>
        internal VectorNotFoundException(string message, Exception innerException) : base(message, innerException)
        {
        }
    }
}


<|start_filename|>Anki.Vector/ControlComponent.cs<|end_filename|>
﻿// <copyright file="ControlComponent.cs" company="<NAME>">
//     Copyright (c) 2020 <NAME>. All rights reserved.
// </copyright>

using System;
using System.Diagnostics.Contracts;
using System.Threading;
using System.Threading.Tasks;
using Anki.Vector.Events;
using Anki.Vector.Exceptions;
using Anki.Vector.ExternalInterface;
using Anki.Vector.GrpcUtil;

namespace Anki.Vector
{
    /// <summary>
    /// Management of the control of Vector's behaviors.
    /// <para>When you connect to Vector with this SDK he will behave normally.  Taking control of Vector will disable
    /// his internal behaviors (driving around autonomously, responding to voice commands, etc).  You are required to
    /// take control of Vector in order to execute commands that move Vector or provide other output.</para>
    /// <para>Call the <see cref="RequestControl(int)"/> method to request control of Vector and <see cref="ReleaseControl"/> to 
    /// release control.  Unless you use priority level <see cref="ControlPriority.OverrideBehaviors"/> Vector will regain
    /// control automatically if he senses an edge or runs low on battery.  You can subscribe to the <see cref="ControlGranted"/> and 
    /// <see cref="ControlLost"/> events to get feedback on when you have acquired or lost control.</para>
    /// </summary>
    /// <seealso cref="Anki.Vector.Component" />
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1001:Types that own disposable fields should be disposable", Justification = "Component is disposed by Teardown method.")]
    public class ControlComponent : Component
    {
        /// <summary>
        /// Occurs when control of Vector is granted.
        /// </summary>
        public event EventHandler<ControlGrantedEventArgs> ControlGranted;

        /// <summary>
        /// Occurs when control of Vector is lost.
        /// </summary>
        public event EventHandler<ControlLostEventArgs> ControlLost;

        /// <summary>
        /// Gets a value indicating whether SDK has control over Vector
        /// </summary>
        public bool HasControl { get => _hasControl; private set => SetProperty(ref _hasControl, value); }
        private bool _hasControl;

        /// <summary>
        /// Gets a value indicating whether SDK will attempt to acquire control automatically when a behavior method is called after
        /// losing control to higher priority behaviors such as returning home to charge a low battery.  
        /// </summary>
        public bool MaintainBehaviorControl { get => _maintainBehaviorControl; private set => SetProperty(ref _maintainBehaviorControl, value); }
        private bool _maintainBehaviorControl;

        /// <summary>
        /// Gets the last requested control priority.
        /// </summary>
        public ControlPriority ControlPriority { get => _controlPriority; private set => SetProperty(ref _controlPriority, value); }
        private ControlPriority _controlPriority = ControlPriority.Default;

        /// <summary>
        /// The behavior feed
        /// </summary>
        private readonly IAsyncDuplexEventLoop<BehaviorControlRequest> behaviorFeed;

        /// <summary>
        /// The behavior result
        /// </summary>
        private TaskCompletionSource<bool> behaviorResult = null;

        /// <summary>
        /// The connection timeout in milliseconds
        /// </summary>
        private const int DefaultControlTimeout = 10_000;

        /// <summary>
        /// Initializes a new instance of the <see cref="ControlComponent"/> class.
        /// </summary>
        /// <param name="robot">The robot.</param>
        internal ControlComponent(Robot robot) : base(robot)
        {
            // Create the behavior feed event loop
            this.behaviorFeed = new AsyncDuplexEventLoop<BehaviorControlRequest, BehaviorControlResponse>(
                (token) => robot.StartStream(client => client.BehaviorControl(cancellationToken: token)),
                ProcessControlResponse,
                () => {
                    behaviorResult?.TrySetResult(false);
                    HasControl = false;
                },
                robot.PropagateException
            );
        }

        /// <summary>
        /// Requests the control of Vector.
        /// </summary>
        /// <param name="timeout">The timeout in milliseconds.</param>
        /// <returns>A task that represents the asynchronous operation.  The task result indicated whether control was granted.</returns>
        public Task<bool> RequestControl(int timeout = DefaultControlTimeout)
        {
            return RequestControl(ControlPriority.Default, timeout);
        }

        /// <summary>
        /// Requests the control.
        /// </summary>
        /// <param name="priority">The priority.</param>
        /// <param name="timeout">The timeout.</param>
        /// <returns>A task that represents the asynchronous operation.  The task result indicated whether control was granted.</returns>
        /// <exception cref="VectorControlTimeoutException">Surpassed control timeout</exception>
        public async Task<bool> RequestControl(ControlPriority priority, int timeout = DefaultControlTimeout)
        {
            // If a behavior result already exists, set it.
            MaintainBehaviorControl = priority != ControlPriority.ReserveControl;
            if (HasControl && ControlPriority == priority) return true;
            ControlPriority = priority;
            behaviorResult?.TrySetCanceled();
            behaviorResult = new TaskCompletionSource<bool>();
            await behaviorFeed.Call(new BehaviorControlRequest()
            {
                ControlRequest = new ControlRequest()
                {
                    Priority = (ControlRequest.Types.Priority)priority
                }
            }).ConfigureAwait(false);

            // If timeout, wait for timeout
            if (timeout != Timeout.Infinite)
            {
                // Otherwise wait for result and timeout
                var delay = Task.Delay(timeout);
                if (await Task.WhenAny(behaviorResult.Task, delay).ConfigureAwait(false) == delay)
                {
                    throw new VectorControlTimeoutException($"Surpassed control timeout of {timeout}ms");
                }
            }
            return await behaviorResult.Task.ConfigureAwait(false);
        }

        /// <summary>
        /// Releases the control of Vector
        /// </summary>
        /// <returns>A task that represents the asynchronous operation.  The task result indicated whether control was released.</returns>
        public Task<bool> ReleaseControl()
        {
            MaintainBehaviorControl = false;
            return InternalReleaseControl();
        }

        /// <summary>
        /// Releases the control of Vector without setting the MaintainBehaviorControl flag.  Used by internal methods to release
        /// control.
        /// </summary>
        /// <returns>A task that represents the asynchronous operation.  The task result indicated whether control was released.</returns>
        internal async Task<bool> InternalReleaseControl()
        {
            if (!behaviorFeed.IsActive) return true;
            if (!HasControl) return true;
            await behaviorFeed.Call(new BehaviorControlRequest()
            {
                ControlRelease = new ControlRelease()
            }).ConfigureAwait(false);
            HasControl = false;
            return true;
        }

        /// <summary>
        /// Waits for control to change.
        /// <para>The task will complete when control has changed and the result is true if SDK has control over Vector.</para>
        /// </summary>
        /// <returns>A task that represents the asynchronous operation; the task result contains whether not SDK has control.</returns>
        public Task<bool> WaitForControlChange()
        {
            if (behaviorResult == null) behaviorResult = new TaskCompletionSource<bool>();
            return behaviorResult.Task;
        }

        /// <summary>
        /// Processes the control response.
        /// </summary>
        /// <param name="response">The response.</param>
        private void ProcessControlResponse(BehaviorControlResponse response)
        {
            var type = response.ResponseTypeCase;
            switch (type)
            {
                case BehaviorControlResponse.ResponseTypeOneofCase.ControlGrantedResponse:
                case BehaviorControlResponse.ResponseTypeOneofCase.ControlLostEvent:
                    HasControl = type == BehaviorControlResponse.ResponseTypeOneofCase.ControlGrantedResponse;
                    behaviorResult?.TrySetResult(HasControl);
                    if (type == BehaviorControlResponse.ResponseTypeOneofCase.ControlGrantedResponse) ControlGranted?.Invoke(this, new ControlGrantedEventArgs());
                    if (type == BehaviorControlResponse.ResponseTypeOneofCase.ControlLostEvent) ControlLost?.Invoke(this, new ControlLostEventArgs());
                    break;
                case BehaviorControlResponse.ResponseTypeOneofCase.ReservedControlLostEvent:
                    HasControl = false;
                    behaviorResult?.TrySetResult(false);
                    ControlLost?.Invoke(this, new ControlLostEventArgs());
                    break;
            }
        }

        /// <summary>
        /// Called when disconnecting Robot
        /// </summary>
        /// <param name="forced">if set to <c>true</c> the shutdown is forced due to lost connection.</param>
        /// <returns>A task that represents the asynchronous operation.</returns>
        internal override async Task Teardown(bool forced)
        {
            await behaviorFeed.End().ConfigureAwait(false);
        }
    }
}


<|start_filename|>Anki.Vector/ExternalInterface/ExternalInterface.cs<|end_filename|>
// <auto-generated>
//     Generated by the protocol buffer compiler.  DO NOT EDIT!
//     source: anki_vector/messaging/external_interface.proto
// </auto-generated>
#pragma warning disable 1591, 0612, 3021
#region Designer generated code

using pb = global::Google.Protobuf;
using pbc = global::Google.Protobuf.Collections;
using pbr = global::Google.Protobuf.Reflection;
using scg = global::System.Collections.Generic;
namespace Anki.Vector.ExternalInterface {

  /// <summary>Holder for reflection information generated from anki_vector/messaging/external_interface.proto</summary>
  public static partial class ExternalInterfaceReflection {

    #region Descriptor
    /// <summary>File descriptor for anki_vector/messaging/external_interface.proto</summary>
    public static pbr::FileDescriptor Descriptor {
      get { return descriptor; }
    }
    private static pbr::FileDescriptor descriptor;

    static ExternalInterfaceReflection() {
      byte[] descriptorData = global::System.Convert.FromBase64String(
          string.Concat(
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>t",
            "ZXNzYWdpbmcvbmF2X21hcC5wcm90bxoiYW5raV92ZWN0b3IvbWVzc2FnaW5n",
            "L3NoYXJlZC5wcm90bxokYW5raV92ZWN0b3IvbWVzc2FnaW5nL3NldHRpbmdz",
            "LnByb3RvKm8KD1Byb3RvY29sVmVyc2lvbhIcChhQUk9UT0NPTF9WRVJTSU9O",
            "X1VOS05PV04QABIcChhQUk9UT0NPTF9WRVJTSU9OX01JTklNVU0QABIcChhQ",
            "Uk9UT0NPTF9WRVJTSU9OX0NVUlJFTlQQBRoCEAE<KEY>0",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "b25zZSIfgtPkkwIZIhQvdjEvcHJvdG9jb2xfdmVyc2lvbjoBKhKrAQoRU0RL",
            "SW5pdGlhbGl6YXRpb24SOC5BbmtpLlZlY3Rvci5leHRlcm5hbF9pbnRlcmZh",
            "Y2UuU0RLSW5pdGlhbGl6YXRpb25SZXF1ZXN0GjkuQW5raS5WZWN0b3IuZXh0",
            "ZXJuYWxfaW50ZXJmYWNlLlNES0luaXRpYWxpemF0aW9uUmVzcG9uc2UiIYLT",
            "5JMCGyIWL3YxL3Nka19pbml0aWFsaXphdGlvbjoBKhJ4CgtEcml2ZVdoZWVs",
            "cxIyLkFua2kuVmVjdG9yL<KEY>ZS<KEY>",
            "c1JlcXVlc3QaMy5BbmtpLlZlY3Rvci5leHRlcm5hbF9pbnRlcmZhY2UuRHJp",
            "dmVXaGVlbHNSZXNwb25zZSIAEowBChRQbGF5QW5pbWF0aW9uVHJpZ2dlchI7",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "UmVxdWVzdBo1LkFua2kuVmVjdG9yLmV4dGVybmFsX2ludGVyZmFjZS5QbGF5",
            "QW5pbWF0aW9uUmVzcG9uc2UiABKfAQoOTGlzdEFuaW<KEY>MSNS5Bbmtp",
            "LlZlY3Rvci5leHRlcm5hbF9pbnRlcmZhY2UuTGlzdEFuaW1hdGlvbnNSZXF1",
            "ZXN0GjYuQW5raS5WZWN0b3IuZXh0ZXJuYWxfaW50ZXJmYWNlLkxpc3RBbmlt",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "aS5WZWN0b3IuZXh0ZXJuYWxfaW50ZXJmYWNlLlN0b3BBbGxNb3RvcnNSZXNw",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "bmFsQXVkaW9TdHJlYW1QbGF5YmFjaxI6LkFua2kuVmVjdG9yLmV4dGVybmFs",
            "<KEY>",
            "VmVjdG9yLmV4dGVybmFsX<KEY>",
            "UmVzcG9uc2UiAC<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "ZE5hbWVzEjsuQW5raS5WZWN0b3IuZXh0ZXJuYWxfaW50ZXJmYWNlLlJlcXVl",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "b2xsZWRGYWNlQnlJRFJlc3BvbnNlIiiC0+STAiIiHS92MS9lcmFzZV9lbnJv",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>l",
            "ckR<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "YWxfaW50ZXJmYWNlLkVuYWJsZU1vdGlvbkRldGVjdGlvblJlcXVlc3QaPS5B",
            "<KEY>",
            "ZWN0aW9uUmVzcG9uc2UiJoLT5JMCICIbL3YxL2VuYWJsZV<KEY>fZGV0",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "ZWRSZXF1ZXN0Gj8uQW5raS5WZWN0b3IuZXh0ZXJuYWxfaW50ZXJmYWNlLklz",
            "SW1hZ2VTdHJlYW1pbmdFbmFibGVkUmVzcG9uc2UiKYLT5JMCIyIeL3YxL2lz",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "Di92MS9nb190b19wb3NlOgEqEpgBCgxEb2NrV2l0aEN1YmUSMy5BbmtpLlZl",
            "Y3Rvci5leHRlcm5hbF9pbnRlcmZhY2UuRG9ja1dpdGhDdWJlUmVxdWVzdBo0",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "aXZlT25DaGFyZ2VyUmVzcG9uc2UiH4LT<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "ZSIagtPkkwIUIg8vdjEvcGhvdG9zX2luZm86ASoSegoFUGhvdG8SLC5Bbmtp",
            "<KEY>",
            "<KEY>mV<KEY>FILT",
            "5JMCDiIJL3YxL3Bob3RvOgEqEooBCglUaHVtYm5haWwSMC5BbmtpLlZlY3Rv",
            "<KEY>VG<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "aXZlU3RyYWlnaHQSNC5BbmtpLlZlY3Rvci5leHRlcm5hbF9pbnRlcmZhY2Uu",
            "<KEY>dHJhaWdodFJlcXVlc3QaNS5BbmtpLlZlY3Rvci5leHRlcm5hbF9p",
            "bnRlcmZhY2UuRHJpdmVTdHJhaWdodFJlc3BvbnNlIgASeAoLVHVybkluUGxh",
            "Y2USMi5BbmtpLlZlY3Rvci5leHRlcm5hbF9pbnRlcmZhY2UuVHVybkluUGxh",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "ZmFjZS5Hb1RvT2JqZWN0UmVxdWVzdBoyLkFua2kuVmVjdG9yLmV4dGVybmFs",
            "X2ludGVyZmFjZS5Hb1RvT2JqZWN0UmVzcG9uc2UiABJ1CgpSb2xsT2JqZWN0",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "cm5hbF9pbnRlcmZhY2UuUGlja3VwT2JqZWN0UmVzcG9uc2UiABKcAQoXUGxh",
            "Y2VPYmplY3RPbkdyb3VuZEhlcmUSPi5BbmtpLlZlY3Rvci5leHRlcm5hbF9p",
            "<KEY>UG<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "5JMCFiIRL3YxL2JhdHRlcnlfc3RhdGU6ASoSlwEKDFZlcnNpb25TdGF0ZRIz",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "EoMB<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "b25uZWN0X2N1YmU6ASoSnwEKDkRpc2Nvbm5lY3RDdWJlEjUuQW5raS5WZWN0",
            "b3IuZXh0ZXJuYWxfaW50ZXJmYWNlLkRpc2Nvbm5lY3RDdWJlUmVxdWVzdBo2",
            "LkFua2kuVmVjdG9yLmV4dGVybmFsX<KEY>ZS5EaXNjb25uZWN0Q3Vi",
            "ZVJlc3BvbnNlIh6C0+STAhgiEy92MS9kaXNjb25uZWN0X2N1YmU6ASoSnwEK",
            "<KEY>",
            "<KEY>",
            "bmFsX2ludGVyZmFjZS5DdWJlc0F2YWlsYWJsZVJlc3BvbnNlIh6C0+STAhgi",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "dG9yLmV4dGVybmFsX2ludGVyZmFjZS5Gb3JnZXRQcmVmZXJyZWRDdWJlUmVx",
            "dWVzdBo7LkFua2kuVmVjdG9yL<KEY>5Gb3JnZXRQ",
            "cmVmZXJyZWRDdWJlUmVzcG9uc2UiJILT5JMCHiIZL3YxL2ZvcmdldF9wcmVm",
            "ZXJyZWRfY3ViZToBKhKoAQoQU2V0UHJlZmVycmVkQ3ViZRI3LkFua2kuVmVj",
            "<KEY>",
            "<KEY>",
            "<KEY>WL3YxL3NldF9wcmVmZXJyZWRfY3Vi",
            "Z<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "Y3Rvci5leHRlcm5hbF9pbnRlcmZhY2UuQ3JlYXRlRml4ZWRDdXN0b21PYmpl",
            "Y3RSZXF1ZXN0Gj8uQW5raS5WZ<KEY>YW<KEY>",
            "ZWF0ZUZpeGVkQ3VzdG9tT2JqZWN0UmVzcG9uc2UiKYLT5JMCIyIeL3YxL2Ny",
            "<KEY>P",
            "YmplY3QSOS5BbmtpLlZlY3Rvci5leHRlcm5hbF9pbnRlcmZhY2UuRGVmaW5l",
            "<KEY>",
            "dGVyZmFjZS5EZWZpbmVDdXN0b21PYmplY3RSZXNwb25zZSIjgtPkkwIdIhgv",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>QXVkaW9GZWVkUmVzcG9uc2Ui",
            "<KEY>ARK<KEY>",
            "<KEY>",
            "<KEY>",
            "ZWVkUmVzcG9uc2UiGoLT5JMCFCIPL3YxL2NhbWVyYV9mZWVkOgEqMAESsAEK",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "L2dldF9jYW1lcmFfY29uZmlnOgEqEpQBCgtTZXRFeWVDb2xvchIyLkFua2ku",
            "VmVjdG9yL<KEY>5TZXRFeWVDb2xvclJlcXVlc3Qa",
            "<KEY>RX<KEY>",
            "<KEY>I<KEY>",
            "<KEY>",
            "<KEY>",
            "ZS5OYXZNYXBGZWVkUmVzcG9uc2UiG4LT5JMCFSIQL3YxL25hdl9tYXBfZmVl",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>ZXJmYWN<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>uQW5raS5WZWN0b3IuZXh0ZXJuYWxf",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>LT",
            "5JMCEyIOL3YxL3B1bGxfamRvY3M6ASoSvAEKFVVwZGF0ZUFjY291bnRTZXR0",
            "aW5ncxI8LkFua2kuVmVjdG9yLmV4dGVybmFsX2ludGVyZmFjZS5VcGRhdGVB",
            "Y2NvdW50U2V0dGluZ3NSZXF1ZXN0Gj0uQW5raS5WZWN0b3IuZXh0ZXJuYWxf",
            "<KEY>ST",
            "<KEY>",
            "<KEY>USOC5BbmtpLlZlY3Rvci5leHRlcm5hbF9pbnRlcmZhY2Uu",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>KC0+STAhwiFy92",
            "MS9jaGVja191cGRhdGVfc3RhdHVzOgEqEqgBChBVcGRhdGVBbmRSZXN0YXJ0",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "a2kuVmVjdG9yL<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "<KEY>",
            "YXR1cmVGbGFnTGlzdFJlc3BvbnNlIiCC0+STAhoiFS92MS9mZWF0dXJlX2Zs",
            "YWdfbGlzdDoBKhKjAQoRR2V0QWxleGFBdXRoU3RhdGUSNS5BbmtpLlZlY3Rv",
            "ci5leHRlcm5hbF9pbnRlcmZhY2UuQWxleGFBdXRoU3RhdGVSZXF1ZXN0GjYu",
            "QW5raS5WZWN0b3IuZXh0ZXJuYWxfaW50ZXJmYWNlLkFsZXhhQXV0aFN0YXRl",
            "UmVzcG9uc2UiH4LT5JMCGSIUL3YxL2FsZXhhX2F1dGhfc3RhdGU6ASoSkAEK",
            "CkFsZXhhT3B0SW4SMS5BbmtpLlZlY3Rvci5leHRlcm5hbF9pbnRlcmZhY2Uu",
            "Q<KEY>HRJblJlcXVlc3QaMi5BbmtpLlZlY3Rvci5leHRlcm5hbF9pbnRl",
            "cmZhY2UuQWxleGFPcHRJblJlc3BvbnNlIhuC0+STAhUiEC92MS9hbGV4YV9v",
            "cHRfaW46ASpiBnByb3RvMw=="));
      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
          new pbr::FileDescriptor[] { global::Google.Api.AnnotationsReflection.Descriptor, global::Anki.Vector.ExternalInterface.AlexaReflection.Descriptor, global::Anki.Vector.ExternalInterface.BehaviorReflection.Descriptor, global::Anki.Vector.ExternalInterface.CubeReflection.Descriptor, global::Anki.Vector.ExternalInterface.MessagesReflection.Descriptor, global::Anki.Vector.ExternalInterface.NavMapReflection.Descriptor, global::Anki.Vector.ExternalInterface.SharedReflection.Descriptor, global::Anki.Vector.ExternalInterface.SettingsReflection.Descriptor, },
          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Anki.Vector.ExternalInterface.ProtocolVersion), }, null));
    }
    #endregion

  }
  #region Enums
  /// <summary>
  /// &lt;p>The valid versions of the protocol.&lt;/p>
  /// &lt;p>Protocol versions are updated when messages change significantly:
  /// new ones are added and removed, fields deprecated, etc.&lt;/p>
  /// &lt;p>The goal is to support as many old versions as possible, only
  /// bumping the minimum when there is no way to handle a prior version.&lt;/p>
  /// </summary>
  public enum ProtocolVersion {
    /// <summary>
    /// Required zero value for enums
    /// </summary>
    [pbr::OriginalName("PROTOCOL_VERSION_UNKNOWN")] Unknown = 0,
    /// <summary>
    /// Minimum supported version
    /// </summary>
    [pbr::OriginalName("PROTOCOL_VERSION_MINIMUM", PreferredAlias = false)] Minimum = 0,
    /// <summary>
    /// Current version (incremented on 2019.03.12 for streaming update status support.)
    /// </summary>
    [pbr::OriginalName("PROTOCOL_VERSION_CURRENT")] Current = 5,
  }

  #endregion

}

#endregion Designer generated code


<|start_filename|>Anki.Vector/EventComponent.cs<|end_filename|>
﻿// <copyright file="EventComponent.cs" company="<NAME>">
//     Copyright (c) 2020 <NAME>. All rights reserved.
// </copyright>

using System;
using System.Threading;
using System.Threading.Tasks;
using Anki.Vector.Events;
using Anki.Vector.ExternalInterface;
using Anki.Vector.GrpcUtil;

namespace Anki.Vector
{
    /// <summary>
    /// Event handlers to subscribe to robot events.
    /// </summary>
    /// <seealso cref="System.IDisposable" />
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1001:Types that own disposable fields should be disposable", Justification = "Component is disposed by Teardown method.")]
    public class EventComponent : Component
    {
        /// <summary>
        /// The event stream timeout
        /// </summary>
        private const int EventStreamTimeout = 5_000;

        /// <summary>
        /// The event feed loop
        /// </summary>
        private readonly IAsyncEventLoop eventFeed;

        /// <summary>
        /// Gets the cancellation token source for timing out the event loop
        /// </summary>
        private CancellationTokenSource timeoutCancellationTokenSource = null;

        /// <summary>Lost connection to Vector</summary>
        private bool lostConnection = false;

        /// <summary>
        /// Initializes a new instance of the <see cref="EventComponent" /> class.
        /// </summary>
        /// <param name="robot">The robot.</param>
        internal EventComponent(Robot robot) : base(robot)
        {
            this.eventFeed = new AsyncEventLoop<EventResponse>(
                (token) => robot.StartStream(client => client.EventStream(new EventRequest(), cancellationToken: token)),
                ProcessEvent,
                () =>
                {
                    OnPropertyChanged(nameof(IsProccessingEvents));
                    Robot.Disconnect(lostConnection).ConfigureAwait(false);
                },
                robot.PropagateException
            );
        }

        /// <summary>
        /// Gets a value indicating whether this instance is processing events.
        /// </summary>
        public bool IsProccessingEvents => eventFeed.IsActive;

        /// <summary>
        /// Starts the event processing
        /// </summary>
        /// <returns>A task that represents the asynchronous operation.</returns>
        /// <exception cref="InvalidOperationException">The camera feed has already been started.</exception>
        internal async Task Start()
        {
            await eventFeed.Start().ConfigureAwait(false); 
            OnPropertyChanged(nameof(IsProccessingEvents));
        }

        /// <summary>
        /// Ends the event processing
        /// </summary>
        /// <returns>A task that represents the asynchronous operation.</returns>
        internal async Task End()
        {
            await eventFeed.End().ConfigureAwait(false);
        }

        /// <summary>
        /// Called when disconnecting
        /// </summary>
        /// <param name="forced">if set to <c>true</c> the shutdown is forced due to lost connection.</param>
        /// <returns>
        /// A task that represents the asynchronous operation.
        /// </returns>
        internal override Task Teardown(bool forced)
        {
            return End();
        }

        /// <summary>
        /// Occurs for every robot event
        /// </summary>
        public event EventHandler<RobotEventArgs> RobotEvent;

        /// <summary>
        /// Occurs when connection response received.
        /// </summary>
        public event EventHandler<ConnectionResponseEventArgs> ConnectionResponse;

        /// <summary>
        /// Occurs when cube batter status received.
        /// </summary>
        public event EventHandler<CubeBatteryEventArgs> CubeBattery;

        /// <summary>
        /// Robot event triggered when Vector hears "Hey Vector"
        /// </summary>
        public event EventHandler<WakeWordBeginEventArgs> WakeWordBegin;

        /// <summary>
        /// Robot event triggered after Vector finishes listening to "Hey Vector"
        /// </summary>
        public event EventHandler<WakeWordEndEventArgs> WakeWordEnd;

        /// <summary>
        /// Robot event triggered after Vector finishes RCM TBD
        /// </summary>
        public event EventHandler<AttentionTransferEventArgs> AttentionTransfer;

        /// <summary>
        /// Keep alive event from robot
        /// </summary>
        public event EventHandler<KeepAliveEventArgs> KeepAlive;

        /// <summary>
        /// Occurs when mirror mode is disabled.
        /// </summary>
        public event EventHandler<MirrorModeDisabledEventArgs> MirrorModeDisabled;

        /// <summary>
        /// Occurs when the cube connection is lost.
        /// </summary>
        public event EventHandler<CubeConnectionLostEventArgs> CubeConnectionLost;

        /// <summary>
        /// After the ConnectCube process is started, all available light cubes in range will broadcast an availability message through the Robot.
        /// </summary>
        public event EventHandler<RobotObjectAvailableEventArgs> ObjectAvailable;

        /// <summary>
        /// Connection state change of the cube
        /// </summary>
        public event EventHandler<RobotObjectConnectionStateEventArgs> ObjectConnectionState;

        /// <summary>
        /// Robot event triggered when an object starts moving.
        /// </summary>
        public event EventHandler<RobotObjectMovedEventArgs> ObjectMoved;

        /// <summary>
        /// Robot event triggered when an object stops moving.
        /// </summary>
        public event EventHandler<RobotObjectStoppedMovingEventArgs> ObjectStoppedMoving;

        /// <summary>
        /// Robot event triggered when an object is tapped.
        /// </summary>
        public event EventHandler<RobotObjectTappedEventArgs> ObjectTapped;

        /// <summary>
        /// Robot event triggered when an object’s orientation changed.
        /// </summary>
        public event EventHandler<RobotObjectUpAxisChangedEventArgs> ObjectUpAxisChanged;

        /// <summary>
        /// Robot event triggered when an object is observed by the robot.
        /// </summary>
        public event EventHandler<RobotObservedObjectEventArgs> ObservedObject;

        /// <summary>
        /// Robot event when photo is taken
        /// </summary>
        public event EventHandler<PhotoTakenEventArgs> PhotoTaken;

        /// <summary>
        /// Robot event for when observed face changes
        /// </summary>
        public event EventHandler<RobotChangedObservedFaceIdEventArgs> ChangedObservedFaceId;

        /// <summary>
        /// Robot event for when a face is observed by the robot.
        /// </summary>
        public event EventHandler<RobotObservedFaceEventArgs> ObservedFace;

        /// <summary>
        /// Robot event containing changes to the robot’s state.
        /// </summary>
        public event EventHandler<RobotStateEventArgs> RobotState;

        /// <summary>
        /// Robot event containing stimulation information.
        /// </summary>
        public event EventHandler<StimulationInfoEventArgs> StimulationInfo;

        /// <summary>
        /// Occurs when a feature is invoked.
        /// </summary>
        public event EventHandler<FeatureStatusEventArgs> FeatureStatus;

        /// <summary>
        /// Robot event when face enrollment completed.
        /// </summary>
        public event EventHandler<FaceEnrollmentCompletedEventArgs> FaceEnrollmentCompleted;

        /// <summary>
        /// Robot event when face scan completed.
        /// </summary>
        public event EventHandler<FaceScanCompleteEventArgs> FaceScanComplete;

        /// <summary>
        /// Robot event when face scan started.
        /// </summary>
        public event EventHandler<FaceScanStartedEventArgs> FaceScanStarted;

        /// <summary>
        /// Robot event vision modes are automatically disabled.
        /// </summary>
        public event EventHandler<VisionModesAutoDisabledEventArgs> VisionModesAutoDisabled;

        /// <summary>
        /// Robot event when jdocs have been changed.
        /// </summary>
        public event EventHandler<JdocsChangedEventArgs> JdocsChanged;

        /// <summary>
        /// Robot event when alexa authentication occurs.
        /// </summary>
        public event EventHandler<AlexaAuthEventArgs> AlexaAuth;

        /// <summary>
        /// Root event triggered after processing voice commands.
        /// <para>Note: This event is only sent if the application has reserved
        /// control -- and thus Vector will not carry it out.</para>
        /// <para>See also WakeWordEnd (to receive the intent when we haven't reserved control), and AppIntent to send an intent</para>
        /// </summary>
        public event EventHandler<UserIntentEventArgs> UserIntent;

        /// <summary>
        /// Robot event that occurs when checking for updates.
        /// </summary>
        public event EventHandler<CheckUpdateStatusEventArgs> CheckUpdateStatus;

        /// <summary>
        /// Robot event triggered when the robot observed motion.
        /// </summary>
        public event EventHandler<RobotObservedMotionEventArgs> RobotObservedMotion;

        /// <summary>
        /// Robot event triggered when robot erased an enrolled face.
        /// </summary>
        public event EventHandler<RobotErasedEnrolledFaceEventArgs> RobotErasedEnrolledFace;

        /// <summary>
        /// Robot event triggered when robot renamed enrolled face.
        /// </summary>
        public event EventHandler<RobotRenamedEnrolledFaceEventArgs> RobotRenamedEnrolledFace;

        /// <summary>
        /// Robot event triggered when camera settings are updated.
        /// </summary>
        public event EventHandler<CameraSettingsUpdateEventArgs> CameraSettingsUpdate;

        /// <summary>
        /// Robot event triggered when Vector does not move as expected.
        /// </summary>
        public event EventHandler<UnexpectedMovementEventArgs> UnexpectedMovement;

        /// <summary>
        /// Called when cube is explicitly connected.
        /// </summary>
        /// <param name="response">The response.</param>
        internal void OnCubeConnected(ExternalInterface.ConnectCubeResponse response)
        {
            RaiseRobotEvents(ObjectConnectionState, new RobotObjectConnectionStateEventArgs(response));
        }

        /// <summary>
        /// Processes the event.  This is called directly from event feed loop
        /// </summary>
        /// <param name="eventResponse">The event response.</param>
        private void ProcessEvent(EventResponse eventResponse)
        {
            // Must receive en event every 5 seconds or the feed will be shut down
            _ = ResetTimeout(EventStreamTimeout).ConfigureAwait(false);

            var e = eventResponse.Event;
            switch (e.EventTypeCase)
            {
                case Event.EventTypeOneofCase.ConnectionResponse:
                    RaiseRobotEvents(ConnectionResponse, new ConnectionResponseEventArgs(e));
                    break;
                case Event.EventTypeOneofCase.CubeBattery:
                    RaiseRobotEvents(CubeBattery, new CubeBatteryEventArgs(e));
                    break;
                case Event.EventTypeOneofCase.WakeWord:
                    ProcessWakeWord(e);
                    break;
                case Event.EventTypeOneofCase.AttentionTransfer:
                    RaiseRobotEvents(AttentionTransfer, new AttentionTransferEventArgs(e));
                    break;
                case Event.EventTypeOneofCase.KeepAlive:
                    RaiseRobotEvents(KeepAlive, new KeepAliveEventArgs(e));
                    break;
                case Event.EventTypeOneofCase.JdocsChanged:
                    RaiseRobotEvents(JdocsChanged, new JdocsChangedEventArgs(e));
                    break;
                case Event.EventTypeOneofCase.AlexaAuthEvent:
                    RaiseRobotEvents(AlexaAuth, new AlexaAuthEventArgs(e));
                    break;
                case Event.EventTypeOneofCase.MirrorModeDisabled:
                    RaiseRobotEvents(MirrorModeDisabled, new MirrorModeDisabledEventArgs(e));
                    break;
                case Event.EventTypeOneofCase.ObjectEvent:
                    ProcessObjectEvent(e);
                    break;
                case Event.EventTypeOneofCase.PhotoTaken:
                    RaiseRobotEvents(PhotoTaken, new PhotoTakenEventArgs(e));
                    break;
                case Event.EventTypeOneofCase.RobotChangedObservedFaceId:
                    RaiseRobotEvents(ChangedObservedFaceId, new RobotChangedObservedFaceIdEventArgs(e));
                    break;
                case Event.EventTypeOneofCase.RobotObservedFace:
                    RaiseRobotEvents(ObservedFace, new RobotObservedFaceEventArgs(e));
                    break;
                case Event.EventTypeOneofCase.RobotState:
                    RaiseRobotEvents(RobotState, new RobotStateEventArgs(e));
                    break;
                case Event.EventTypeOneofCase.StimulationInfo:
                    RaiseRobotEvents(StimulationInfo, new StimulationInfoEventArgs(e));
                    break;
                case Event.EventTypeOneofCase.TimeStampedStatus:
                    ProcessTimestampedStatus(e);
                    break;
                case Event.EventTypeOneofCase.VisionModesAutoDisabled:
                    RaiseRobotEvents(VisionModesAutoDisabled, new VisionModesAutoDisabledEventArgs(e));
                    break;
                case Event.EventTypeOneofCase.CheckUpdateStatusResponse:
                    RaiseRobotEvents(CheckUpdateStatus, new CheckUpdateStatusEventArgs(e));
                    break;
                case Event.EventTypeOneofCase.UserIntent:
                    RaiseRobotEvents(UserIntent, new UserIntentEventArgs(e));
                    break;
                case Event.EventTypeOneofCase.RobotObservedMotion:
                    RaiseRobotEvents(RobotObservedMotion, new RobotObservedMotionEventArgs(e));
                    break;
                case Event.EventTypeOneofCase.RobotErasedEnrolledFace:
                    RaiseRobotEvents(RobotErasedEnrolledFace, new RobotErasedEnrolledFaceEventArgs(e));
                    break;
                case Event.EventTypeOneofCase.RobotRenamedEnrolledFace:
                    RaiseRobotEvents(RobotRenamedEnrolledFace, new RobotRenamedEnrolledFaceEventArgs(e));
                    break;
                case Event.EventTypeOneofCase.CameraSettingsUpdate:
                    RaiseRobotEvents(CameraSettingsUpdate, new CameraSettingsUpdateEventArgs(e));
                    break;
                case Event.EventTypeOneofCase.UnexpectedMovement:
                    RaiseRobotEvents(UnexpectedMovement, new UnexpectedMovementEventArgs(e));
                    break;
            }
        }

        /// <summary>
        /// Processes the wake word event 
        /// </summary>
        /// <param name="e">The wake word data.</param>
        private void ProcessWakeWord(Event e)
        {
            switch (e.WakeWord.WakeWordTypeCase)
            {
                case WakeWord.WakeWordTypeOneofCase.WakeWordBegin:
                    RaiseRobotEvents(WakeWordBegin, new WakeWordBeginEventArgs(e));
                    break;
                case WakeWord.WakeWordTypeOneofCase.WakeWordEnd:
                    RaiseRobotEvents(WakeWordEnd, new WakeWordEndEventArgs(e));
                    break;
            }
        }

        /// <summary>
        /// Processes the object event.
        /// </summary>
        /// <param name="e">The object event.</param>
        private void ProcessObjectEvent(Event e)
        {
            switch (e.ObjectEvent.ObjectEventTypeCase)
            {
                case ObjectEvent.ObjectEventTypeOneofCase.CubeConnectionLost:
                    RaiseRobotEvents(CubeConnectionLost, new CubeConnectionLostEventArgs(e));
                    break;
                case ObjectEvent.ObjectEventTypeOneofCase.ObjectAvailable:
                    RaiseRobotEvents(ObjectAvailable, new RobotObjectAvailableEventArgs(e));
                    break;
                case ObjectEvent.ObjectEventTypeOneofCase.ObjectConnectionState:
                    RaiseRobotEvents(ObjectConnectionState, new RobotObjectConnectionStateEventArgs(e));
                    break;
                case ObjectEvent.ObjectEventTypeOneofCase.ObjectMoved:
                    RaiseRobotEvents(ObjectMoved, new RobotObjectMovedEventArgs(e));
                    break;
                case ObjectEvent.ObjectEventTypeOneofCase.ObjectStoppedMoving:
                    RaiseRobotEvents(ObjectStoppedMoving, new RobotObjectStoppedMovingEventArgs(e));
                    break;
                case ObjectEvent.ObjectEventTypeOneofCase.ObjectTapped:
                    RaiseRobotEvents(ObjectTapped, new RobotObjectTappedEventArgs(e));
                    break;
                case ObjectEvent.ObjectEventTypeOneofCase.ObjectUpAxisChanged:
                    RaiseRobotEvents(ObjectUpAxisChanged, new RobotObjectUpAxisChangedEventArgs(e));
                    break;
                case ObjectEvent.ObjectEventTypeOneofCase.RobotObservedObject:
                    RaiseRobotEvents(ObservedObject, new RobotObservedObjectEventArgs(e));
                    break;
            }
        }

        /// <summary>
        /// Processes the timestamped status events.
        /// </summary>
        /// <param name="e">The event details.</param>
        private void ProcessTimestampedStatus(Event e)
        {
            switch (e.TimeStampedStatus.Status.StatusTypeCase)
            {
                case Status.StatusTypeOneofCase.FeatureStatus:
                    RaiseRobotEvents(FeatureStatus, new FeatureStatusEventArgs(e));
                    break;
                case Status.StatusTypeOneofCase.FaceEnrollmentCompleted:
                    RaiseRobotEvents(FaceEnrollmentCompleted, new FaceEnrollmentCompletedEventArgs(e));
                    break;
                case Status.StatusTypeOneofCase.MeetVictorFaceScanComplete:
                    RaiseRobotEvents(FaceScanComplete, new FaceScanCompleteEventArgs(e));
                    break;
                case Status.StatusTypeOneofCase.MeetVictorFaceScanStarted:
                    RaiseRobotEvents(FaceScanStarted, new FaceScanStartedEventArgs(e));
                    break;
            }
        }

        /// <summary>
        /// Raises the <see cref="RobotEvent"/> event and then the specified event handler
        /// </summary>
        /// <typeparam name="T">Event args type</typeparam>
        /// <param name="eventHandler">The event handler.</param>
        /// <param name="eventArgs">The event arguments.</param>
        private void RaiseRobotEvents<T>(EventHandler<T> eventHandler, T eventArgs) where T : RobotEventArgs
        {
            RobotEvent?.Invoke(this, eventArgs);
            eventHandler?.Invoke(this, eventArgs);
        }

        /// <summary>
        /// Resets the timeout every time an event is received.
        /// </summary>
        /// <param name="timeout">The timeout.</param>
        /// <returns>A task that represents the asynchronous operation.</returns>
        private async Task ResetTimeout(int timeout)
        {
            if (timeoutCancellationTokenSource != null)
            {
                timeoutCancellationTokenSource.Cancel();
                timeoutCancellationTokenSource.Dispose();
                timeoutCancellationTokenSource = null;
            }

            timeoutCancellationTokenSource = new CancellationTokenSource();
            var token = timeoutCancellationTokenSource.Token;
            if (token.IsCancellationRequested) return;
            await Task.Delay(timeout, token).ConfigureAwait(false);
            if (token.IsCancellationRequested) return;
            lostConnection = true;
            await End().ConfigureAwait(false);
        }
    }
}


<|start_filename|>Anki.Vector/Events/RobotObservedMotionEventArgs.cs<|end_filename|>
﻿// <copyright file="RobotObservedMotionEventArgs.cs" company="<NAME>">
//     Copyright (c) 2020 <NAME>. All rights reserved.
// </copyright>

using System;
using System.Collections.Generic;
using System.Text;

namespace Anki.Vector.Events
{
    /// <summary>
    /// Robot observed motion event args
    /// </summary>
    /// <seealso cref="System.EventArgs" />
    [Serializable]
    public class RobotObservedMotionEventArgs : RobotEventArgs
    {
        /// <summary>
        /// Gets the area of the supporting region for the point, as a fraction of the bottom region
        /// </summary>
        public float BottomImgArea { get; }

        /// <summary>
        /// Gets pixel coordinate of the point in the image, relative to top-left corner.
        /// </summary>
        public int BottomImgX { get; }

        /// <summary>
        /// Gets pixel coordinate of the point in the image, relative to top-left corner.
        /// </summary>
        public int BottomImgY { get; }

        /// <summary>
        /// Gets the area of the supporting region for the point, as a fraction of the ground ROI.  If unable to map to the ground, area = 0
        /// </summary>
        public float GroundArea { get; }

        /// <summary>
        /// Gets the coordinates of the point on the ground, relative to robot, in mm
        /// </summary>
        public int GroundX { get; }

        /// <summary>
        /// Gets the coordinates of the point on the ground, relative to robot, in mm
        /// </summary>
        public int GroundY { get; }

        /// <summary>
        /// Gets the area of the supporting region for the point, as a fraction of the image
        /// </summary>
        public float ImgArea { get; }

        /// <summary>
        /// Gets the pixel coordinate of the point in the image, relative to top-left corner.
        /// </summary>
        public int ImgX { get; }

        /// <summary>
        /// Gets the pixel coordinate of the point in the image, relative to top-left corner.
        /// </summary>
        public int ImgY { get; }

        /// <summary>
        /// Gets the area of the supporting region for the point, as a fraction of the left region
        /// </summary>
        public float LeftImgArea { get; }

        /// <summary>
        /// Gets the pixel coordinate of the point in the image, relative to top-left corner.
        /// </summary>
        public int LeftImgX { get; }

        /// <summary>
        /// Gets the pixel coordinate of the point in the image, relative to top-left corner.
        /// </summary>
        public int LeftImgY { get; }

        /// <summary>
        /// Gets the area of the supporting region for the point, as a fraction of the right region
        /// </summary>
        public float RightImgArea { get; }

        /// <summary>
        /// Gets the pixel coordinate of the point in the image, relative to top-left corner.
        /// </summary>
        public int RightImgX { get; }

        /// <summary>
        /// Gets the pixel coordinate of the point in the image, relative to top-left corner.
        /// </summary>
        public int RightImgY { get; }

        /// <summary>
        /// Gets the area of the supporting region for the point, as a fraction of the top region
        /// </summary>
        public float TopImgArea { get; }

        /// <summary>
        /// Gets the pixel coordinate of the point in the image, relative to top-left corner.
        /// </summary>
        public int TopImgX { get; }

        /// <summary>
        /// Gets the pixel coordinate of the point in the image, relative to top-left corner.
        /// </summary>
        public int TopImgY { get; }

        /// <summary>
        /// Gets the timestamp of the corresponding image
        /// </summary>
        public uint Timestamp { get; }

        /// <summary>
        /// Initializes a new instance of the <see cref="RobotObservedMotionEventArgs"/> class.
        /// </summary>
        /// <param name="e">The e.</param>
        internal RobotObservedMotionEventArgs(ExternalInterface.Event e) : base(e)
        {
            BottomImgArea = e.RobotObservedMotion.BottomImgArea;
            BottomImgX = e.RobotObservedMotion.BottomImgX;
            BottomImgY = e.RobotObservedMotion.BottomImgY;
            GroundArea = e.RobotObservedMotion.GroundArea;
            GroundX = e.RobotObservedMotion.GroundX;
            GroundY = e.RobotObservedMotion.GroundY;
            ImgArea = e.RobotObservedMotion.ImgArea;
            ImgX = e.RobotObservedMotion.ImgX;
            ImgY = e.RobotObservedMotion.ImgY;
            LeftImgArea = e.RobotObservedMotion.LeftImgArea;
            LeftImgX = e.RobotObservedMotion.LeftImgX;
            LeftImgY = e.RobotObservedMotion.LeftImgY;
            RightImgArea = e.RobotObservedMotion.RightImgArea;
            RightImgX = e.RobotObservedMotion.RightImgX;
            RightImgY = e.RobotObservedMotion.RightImgY;
            TopImgArea = e.RobotObservedMotion.TopImgArea;
            TopImgX = e.RobotObservedMotion.TopImgX;
            TopImgY = e.RobotObservedMotion.TopImgY;
            Timestamp = e.RobotObservedMotion.Timestamp;
        }
    }
}


<|start_filename|>Anki.Vector/Client.cs<|end_filename|>
﻿// <copyright file="Client.cs" company="<NAME>">
//     Copyright (c) 2020 <NAME>. All rights reserved.
// </copyright>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using Anki.Vector.Exceptions;
using Anki.Vector.ExternalInterface;
using Grpc.Core;
using Newtonsoft.Json;
using static Anki.Vector.ExternalInterface.ExternalInterface;

namespace Anki.Vector
{
    /// <summary>
    /// This class abstracts all the gRPC connection login and provides access to the underlying Vector interface.
    /// </summary>
    /// <seealso cref="System.IDisposable" />
    internal class Client : IDisposable
    {
        /// <summary>
        /// Gets the current IP address of the robot.  Can be null if remote Vector connection is used.
        /// </summary>
        public IPAddress IPAddress { get; }

        /// <summary>
        /// The GRPC channel
        /// </summary>
        private Channel channel = null;

        /// <summary>
        /// The gRPC client
        /// </summary>
        private ExternalInterfaceClient grpcClient = null;

        /// <summary>
        /// The HTTP client for REST calls to Vector
        /// </summary>
        private HttpClient httpClient = null;

        /// <summary>
        /// Initializes a new instance of the <see cref="Client"/> class.
        /// </summary>
        /// <param name="channel">The channel.</param>
        /// <param name="certificate">The certificate.</param>
        /// <param name="guid">The unique identifier.</param>
        /// <param name="ipAddress">The ip address.</param>
        private Client(Channel channel, string certificate, string guid, IPAddress ipAddress) 
        {
            this.channel = channel;
            this.httpClient = CreateHttpClient(certificate, guid);
            this.grpcClient = new ExternalInterfaceClient(channel);
            this.IPAddress = ipAddress;
        }

        /// <summary>
        /// Connect to Vector on the local network using the specified robotConfiguration and IP address
        /// </summary>
        /// <param name="robotConfiguration">The robot configuration.</param>
        /// <param name="ipAddress">The IP address.</param>
        /// <param name="timeout">The timeout.</param>
        /// <returns>A task that represents the asynchronous operation; the task result contains the connected client.</returns>
        public static async Task<Client> Connect(IRobotConfiguration robotConfiguration, IPAddress ipAddress, int timeout)
        {
            var channelCredentials = CreateChannelCredentials(robotConfiguration.Certificate, robotConfiguration.Guid);
            var channel = await ConnectChannel(channelCredentials, ipAddress.ToString(), robotConfiguration.RobotName, timeout).ConfigureAwait(false);
            return await ConnectClient(channel, robotConfiguration.Certificate, robotConfiguration.Guid, ipAddress).ConfigureAwait(false);
        }

        /// <summary>
        /// Connects to the robot using the remote connection information.  This is used for connecting to Vector when he's not on the LAN.  
        /// </summary>
        /// <param name="robotConfiguration">The robot configuration.</param>
        /// <param name="timeout">The timeout.</param>
        /// <returns>A task that represents the asynchronous operation; the task result contains the connected client.</returns>
        public static async Task<Client> RemoteConnect(IRemoteRobotConfiguration robotConfiguration, int timeout)
        {
            // Create the channel credentials
            var channelCredentials = CreateChannelCredentials(robotConfiguration.Certificate, robotConfiguration.Guid);
            var channel = await ConnectChannel(channelCredentials, robotConfiguration.RemoteHost, robotConfiguration.RobotName, timeout).ConfigureAwait(false);
            return await ConnectClient(channel, robotConfiguration.Certificate, robotConfiguration.Guid, null).ConfigureAwait(false);
        }

        /// <summary>
        /// Connects to the robot for authentication.
        /// </summary>
        /// <param name="certificate">The certificate.</param>
        /// <param name="host">The host.</param>
        /// <param name="robotName">Name of the robot.</param>
        /// <param name="timeout">The timeout.</param>
        /// <returns>A task that represents the asynchronous operation; the task result contains the connected client.</returns>
        public static async Task<Client> ConnectForAuth(string certificate, string host, string robotName, int timeout)
        {
            // Create the channel credentials
            var channelCredentials = CreateChannelCredentials(certificate, null);
            var channel = await ConnectChannel(channelCredentials, host, robotName, timeout).ConfigureAwait(false);
            return new Client(channel, certificate, null, null);
        }

        /// <summary>
        /// Creates and connects the client instance.
        /// </summary>
        /// <param name="channel">The channel.</param>
        /// <param name="certificate">The certificate.</param>
        /// <param name="guid">The unique identifier.</param>
        /// <param name="ipAddress">The ip address.</param>
        /// <returns>A task that represents the asynchronous operation; the task result contains the connected client.</returns>
        /// <exception cref="VectorInvalidVersionException">Your SDK version is not compatible with Vector’s version.</exception>
        private static async Task<Client> ConnectClient(Channel channel, string certificate, string guid, IPAddress ipAddress = null)
        {
            Client client = null;

            try
            {
                // Create the client
                client = new Client(channel, certificate, guid, ipAddress);

                // Get the client protocol version (also verifies connectivity)
                var result = await client.RunCommand(grpc => grpc.ProtocolVersionAsync(new ProtocolVersionRequest()
                {
                    ClientVersion = (long)ExternalInterface.ProtocolVersion.Current,
                    MinHostVersion = (long)ExternalInterface.ProtocolVersion.Minimum
                }));

                // Check the version
                if (result.Result != ProtocolVersionResponse.Types.Result.Success || (long)ExternalInterface.ProtocolVersion.Minimum > result.HostVersion)
                {
                    throw new VectorInvalidVersionException("Your SDK version is not compatible with Vector’s version.");
                }
            }
            catch (Exception)
            {
                client?.Dispose();
                throw;
            }

            return client;
        }

        /// <summary>
        /// Runs a command against the gRPC client instance.
        /// </summary>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="command">The command.</param>
        /// <returns>A task that represents the asynchronous operation; the task result contains the result of the command.</returns>
        public async Task<TResult> RunCommand<TResult>(Func<ExternalInterfaceClient, Task<TResult>> command)
        {
            try
            {
                return await command(grpcClient).ConfigureAwait(false);
            }
            catch (Grpc.Core.RpcException ex)
            {
                throw TranslateGrpcException(ex);
            }
        }

        /// <summary>
        /// Runs a command against the gRPC client instance.
        /// </summary>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="command">The command.</param>
        /// <returns>A task that represents the asynchronous operation; the task result contains the result of the command.</returns>
        public TResult RunCommand<TResult>(Func<ExternalInterfaceClient, TResult> command)
        {
            try
            {
                return command(grpcClient);
            }
            catch (Grpc.Core.RpcException ex)
            {
                throw TranslateGrpcException(ex);
            }
        }

        /// <summary>
        /// Creates the GRPC channel credentials.
        /// </summary>
        /// <param name="certificate">The certificate.</param>
        /// <param name="guid">The unique identifier (optional).</param>
        /// <returns>The constructed channel credentials</returns>
        private static ChannelCredentials CreateChannelCredentials(string certificate, string guid)
        {
            return ChannelCredentials.Create(
                new SslCredentials(certificate),
                CallCredentials.FromInterceptor((context, metadata) =>
                {
                    if (guid != null) metadata.Add("authorization", $"Bearer {guid}");
                    return Task.CompletedTask;
                })
            );
        }

        /// <summary>
        /// Creates the GRPC channel.
        /// </summary>
        /// <param name="channelCredentials">The channel credentials.</param>
        /// <param name="hostAndPort">The host and port.</param>
        /// <param name="robotName">Name of the robot.</param>
        /// <param name="timeout">The timeout.</param>
        /// <returns>A task that represents the asynchronous operation; the task result contains the connected channel.</returns>
        /// <exception cref="VectorNotFoundException">Unable to establish a connection to Vector.</exception>
        private static async Task<Channel> ConnectChannel(ChannelCredentials channelCredentials, string hostAndPort, string robotName, int timeout)
        {
            // Append the port if it doesn't exist
            if (!hostAndPort.Contains(':')) hostAndPort += ":443";
            
            // Create the channel
            var channel = new Channel(
                hostAndPort,
                channelCredentials,
                new ChannelOption[] { new ChannelOption("grpc.ssl_target_name_override", robotName) }
            );

            // Connect to the channel
            try
            {
                // Open the channel
                await channel.ConnectAsync(GrpcDeadline(timeout)).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                // If failed to open channel throw exception
                channel?.ShutdownAsync();
                throw new VectorNotFoundException("Unable to establish a connection to Vector.", ex);
            }

            return channel;
        }

        /// <summary>
        /// Creates the HTTP client for REST calls from the certificate and Guid.
        /// </summary>
        /// <param name="certificate">The certificate.</param>
        /// <param name="guid">The unique identifier.</param>
        /// <returns>The HTTP client to connect to Vector</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "Disposed by HTTP Client")]
        private static HttpClient CreateHttpClient(string certificate, string guid)
        {
            // Fix for Windows 7 secure download
            ServicePointManager.Expect100Continue = true;
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            certificate = certificate.Replace("-----BEGIN CERTIFICATE-----", "").Replace("-----END CERTIFICATE-----", string.Empty);

            string thumbprint;
            using (var cert = new X509Certificate2(Convert.FromBase64String(certificate))) thumbprint = cert.Thumbprint;
            var httpClient = new HttpClient(new HttpClientHandler()
            {
                ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => cert.Thumbprint == thumbprint
            }, true);
            if (guid != null)
            {
                httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", guid);
            }
            return httpClient;
        }

        /// <summary>
        /// Sends the HTTP REST command to Vector
        /// </summary>
        /// <typeparam name="TResponse">The type of the response.</typeparam>
        /// <typeparam name="TRequest">The type of the request.</typeparam>
        /// <param name="address">The address (path of REST call) .</param>
        /// <param name="request">The request instance.</param>
        /// <returns>A task that represents the asynchronous operation; the task result contains the result of the command.</returns>
        /// <exception cref="VectorNotConnectedException">Vector is not connected.</exception>
        internal async Task<TResponse> RunHttpCommand<TResponse, TRequest>(string address, TRequest request) where TRequest : IHttpJsonData where TResponse : IHttpJsonData
        {
            var body = JsonConvert.SerializeObject(request, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
            using (HttpContent content = new StringContent(body, System.Text.Encoding.UTF8, "application/json"))
            {
                var uri = new Uri("https://" + channel.ResolvedTarget + address);
                var result = await httpClient.PostAsync(uri, content).ConfigureAwait(false);
                result.EnsureSuccessStatusCode();
                var data = await result.Content.ReadAsStringAsync().ConfigureAwait(false);
                return JsonConvert.DeserializeObject<TResponse>(data);
            }
        }

        /// <summary>
        /// Generate a deadline for GRPC calls
        /// </summary>
        /// <param name="timeout">The timeout in milliseconds.</param>
        /// <returns>Deadline value</returns>
        internal static DateTime GrpcDeadline(int timeout) => DateTime.UtcNow.AddMilliseconds(timeout);

        /// <summary>
        /// Translates the GRPC exception into a Vector exception
        /// </summary>
        /// <param name="grpcException">The GRPC exception.</param>
        /// <returns>Translated exception</returns>
        private static Exception TranslateGrpcException(Grpc.Core.RpcException grpcException)
        {
            switch (grpcException.StatusCode)
            {
                case Grpc.Core.StatusCode.Cancelled:
                    if (grpcException.Status.Detail == "Received http2 header with status: 401")
                    {
                        return new VectorUnauthenticatedException("Failed to authenticate communication with Vector.", grpcException);
                    }
                    return new TaskCanceledException("Operation was cancelled", grpcException);
                case Grpc.Core.StatusCode.DeadlineExceeded:
                    return new VectorTimeoutException("Communication with Vector timed out", grpcException);
                case Grpc.Core.StatusCode.Unauthenticated:
                    return new VectorUnauthenticatedException("Failed to authenticate communication with Vector.", grpcException);
                case Grpc.Core.StatusCode.Unavailable:
                    return new VectorUnavailableException("Unable to reach Vector.", grpcException);
                case Grpc.Core.StatusCode.Unimplemented:
                    return new VectorUnimplementedException("Vector does not handle this message.", grpcException);
                default:
                    return new VectorConnectionException("Connection to Vector failed.", grpcException);
            }
        }

        #region IDisposable Support

        /// <summary>To detect redundant calls </summary>
        private bool disposedValue = false;

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources.
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Ignore all exceptions on channel shutdown")]
        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    if (channel != null)
                    {
                        try
                        {
                            channel.ShutdownAsync().ConfigureAwait(false);
                        }
                        catch
                        {
                            // Ignore all exceptions
                        }
                    }
                    channel = null;
                    httpClient?.Dispose();
                    httpClient = null;
                }

                disposedValue = true;
            }
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
            Dispose(true);
        }

        #endregion
    }
}


<|start_filename|>Anki.Vector/Types/CameraConfig.cs<|end_filename|>
﻿// <copyright file="CameraConfig.cs" company="<NAME>">
//     Copyright (c) 2020 <NAME>. All rights reserved.
// </copyright>

using System;
using System.Collections.Generic;
using System.Text;
using Anki.Vector.ExternalInterface;

namespace Anki.Vector.Types
{
    /// <summary>
    /// The fixed properties for Vector's camera.
    /// <para>
    /// A full 3x3 calibration matrix for doing 3D reasoning based on the camera images would look like:
    /// 
    ///    +--------------+--------------+---------------+
    ///    |focal_length.x|      0       |    center.x   |
    ///    +--------------+--------------+---------------+
    ///    |       0      |focal_length.y|    center.y   |
    ///    +--------------+--------------+---------------+
    ///    |       0      |       0      |        1      |
    ///    +--------------+--------------+---------------+
    ///    
    /// </para>
    /// <remarks></remarks>
    /// </summary>
    public class CameraConfig
    {
        /// <summary>
        /// Gets the focal center of the camera.
        /// <para>This is the position of the optical center of projection within the image. It will be close to the center of the image, but adjusted 
        /// based on the calibration of the lens. It is in floating point pixel values e.g. (155.11, 111.40).</para>
        /// </summary>
        public Vector2 Center { get; }

        /// <summary>
        /// Gets the focal length of the camera.
        /// <para>This is focal length combined with pixel skew (as the pixels aren't perfectly square), so there are subtly different values for x and y.  
        /// It is in floating point pixel values e.g. (288.87, 288.36)</para>
        /// </summary>
        public Vector2 FocalLength { get; }

        /// <summary>Gets the x (horizontal) field of view in radians.</summary>
        public float FovX { get; }

        /// <summary>Gets the y (vertical) field of view in radians.</summary>
        public float FovY { get; }

        /// <summary>Gets the minimum supported exposure time in milliseconds.</summary>
        public uint MinCameraExposureTimeMs { get; }

        /// <summary>Gets the maximum supported exposure time in milliseconds.</summary>
        public uint MaxCameraExposureTimeMs { get; }

        /// <summary>Gets the minimum supported camera gain.</summary>
        public float MinCameraGain { get; }

        /// <summary>Gets the maximum supported camera gain.</summary>
        public float MaxCameraGain { get; }

        /// <summary>
        /// Initializes a new instance of the <see cref="CameraConfig"/> class.
        /// </summary>
        /// <param name="response">The response.</param>
        internal CameraConfig(CameraConfigResponse response)
        {
            Center = new Vector2(response.CenterX, response.CenterY);
            FocalLength = new Vector2(response.FocalLengthX, response.FocalLengthY);
            FovX = response.FovX;
            FovY = response.FovY;
            MinCameraExposureTimeMs = response.MinCameraExposureTimeMs;
            MaxCameraExposureTimeMs = response.MaxCameraExposureTimeMs;
            MinCameraGain = response.MinCameraGain;
            MaxCameraGain = response.MaxCameraGain;
        }
    }
}


<|start_filename|>Anki.Vector/Capabilities.cs<|end_filename|>
﻿// <copyright file="Capabilities.cs" company="<NAME>">
//     Copyright (c) 2020 <NAME>. All rights reserved.
// </copyright>

using System;
using System.Collections.Generic;
using System.Text;

namespace Anki.Vector
{
    /// <summary>
    /// Features supported by the current robot firmware.
    /// </summary>
    public class Capabilities
    {
        /// <summary>
        /// Gets the robot instance
        /// </summary>
        private Robot Robot { get; }

        /// <summary>
        /// Gets the version 1.7 constant
        /// </summary>
        internal static Version Version1_7 { get; } = new Version(1, 7);

        /// <summary>
        /// Gets a value indicating whether high resolution image capture are supported.
        /// </summary>
        public bool HighResolutionImageCapture => Robot.FirmwareVersion >= Version1_7;

        /// <summary>
        /// Gets a value indicating whether behaviors can be cancelled.
        /// </summary>
        internal bool CancelBehaviors => Robot.FirmwareVersion >= Version1_7;

        /// <summary>
        /// Gets a value indicating whether camera settings are supported.
        /// </summary>
        public bool CameraSettings => Robot.FirmwareVersion >= Version1_7;

        /// <summary>
        /// Gets a value indicating whether SayText supports the pitch parameter.
        /// </summary>
        public bool SayTextPitch => Robot.FirmwareVersion >= Version1_7;

        /// <summary>
        /// Initializes a new instance of the <see cref="Capabilities" /> class.
        /// </summary>
        /// <param name="robot">The robot.</param>
        internal Capabilities(Robot robot)
        {
            this.Robot = robot;
        }

        /// <summary>
        /// Asserts the specified version.
        /// </summary>
        /// <param name="version">The version.</param>
        /// <exception cref="Exceptions.VectorInvalidVersionException">This method requires at least firmware version XXX.</exception>
        internal void Assert(Version version)
        {
            if (Robot.FirmwareVersion >= version) return;
            throw new Exceptions.VectorInvalidVersionException($"This method requires at least firmware version {version.ToString(2)}.");
        }
    }
}


<|start_filename|>docs/api/Anki.Vector.AnimationComponent.html<|end_filename|>
﻿<!DOCTYPE html>
<!--[if IE]><![endif]-->
<html>
  
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>Class AnimationComponent
   </title>
    <meta name="viewport" content="width=device-width">
    <meta name="title" content="Class AnimationComponent
   ">
    <meta name="generator" content="docfx 2.48.1.0">
    
    <link rel="shortcut icon" href="../favicon.ico">
    <link rel="stylesheet" href="../styles/docfx.vendor.css">
    <link rel="stylesheet" href="../styles/docfx.css">
    <link rel="stylesheet" href="../styles/main.css">
    <meta property="docfx:navrel" content="../toc.html">
    <meta property="docfx:tocrel" content="toc.html">
    
    <meta property="docfx:rel" content="../">
    
  </head>
  <body data-spy="scroll" data-target="#affix" data-offset="120">
    <div id="wrapper">
      <header>
        
        <nav id="autocollapse" class="navbar navbar-inverse ng-scope" role="navigation">
          <div class="container-fluid">
            <div class="navbar-header">
              <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
              </button>
              
              <a class="navbar-brand" href="../index.html">
                <img id="logo" class="svg" src="../logo.svg" alt="">
                  .NET SDK Documentation
              </a>
            </div>
            <div class="collapse navbar-collapse" id="navbar">
              <form class="navbar-form navbar-right" role="search" id="search">
                <div class="form-group">
                  <input type="text" class="form-control" id="search-query" placeholder="Search" autocomplete="off">
                </div>
              </form>
            </div>
          </div>
        </nav>
        
        <div class="subnav navbar navbar-default">
          <div class="container-fluid hide-when-search" id="breadcrumb">
            <ul class="breadcrumb">
              <li></li>
            </ul>
          </div>
        </div>
      </header>
      <div class="container-fluid body-content">
        
        <div id="search-results">
          <div class="search-list"></div>
          <div class="sr-items">
            <p><i class="glyphicon glyphicon-refresh index-loading"></i></p>
          </div>
          <ul id="pagination"></ul>
        </div>
      </div>
      <div role="main" class="container-fluid body-content hide-when-search">
        
        <div class="sidenav hide-when-search" style="margin-left: -15px">
          <a class="btn toc-toggle collapse" data-toggle="collapse" href="#sidetoggle" aria-expanded="false" aria-controls="sidetoggle">Show / Hide Table of Contents</a>
          <div class="sidetoggle collapse" id="sidetoggle">
            <div id="sidetoc"></div>
          </div>
        </div>
        <div class="article row grid-right">
          <div class="col-md-10">
            <article class="content wrap" id="_content" data-uid="Anki.Vector.AnimationComponent" style="max-width: 992px">
  
  
  <h1 id="Anki_Vector_AnimationComponent" data-uid="Anki.Vector.AnimationComponent" class="text-break">Class AnimationComponent
  </h1>
  <div class="markdown level0 summary"><p>Animation related classes, functions, events and values.</p>
<p>Animations represent a sequence of highly coordinated movements, faces, lights, and sounds used to demonstrate an emotion or reaction.</p><p>Animations can control the following tracks: head, lift, treads, face, audio and backpack lights.</p><p>There are two ways to play an animation on Vector: <a class="xref" href="Anki.Vector.AnimationComponent.html#Anki_Vector_AnimationComponent_PlayAnimation_Anki_Vector_Types_Animation_System_UInt32_System_Boolean_System_Boolean_System_Boolean_">PlayAnimation(Animation, UInt32, Boolean, Boolean, Boolean)</a> and <a class="xref" href="Anki.Vector.AnimationComponent.html#Anki_Vector_AnimationComponent_PlayAnimationTrigger_Anki_Vector_Types_AnimationTrigger_System_UInt32_System_Boolean_System_Boolean_System_Boolean_System_Boolean_">PlayAnimationTrigger(AnimationTrigger, UInt32, Boolean, Boolean, Boolean, Boolean)</a>.  For PlayAnimationTrigger, you select a pre-defined
group of animations, and the robot will choose which animation from the group to run when you execute the method.  When calling PlayAnimation, you
select the specific animation you want the robot to run.  We advise you to use PlayAnimationTrigger instead of PlayAnimation, since individual
animations can be deleted between Vector OS versions.</p>
By default, when an SDK program starts, the SDK will request a list of known animation triggers and animations from the robot, which will be loaded
and available from <a class="xref" href="Anki.Vector.AnimationComponent.html#Anki_Vector_AnimationComponent_GetAnimationTriggers">GetAnimationTriggers()</a> and <a class="xref" href="Anki.Vector.AnimationComponent.html#Anki_Vector_AnimationComponent_GetAnimations">GetAnimations()</a>, respectively.
</div>
  <div class="markdown level0 conceptual"></div>
  <div class="inheritance">
    <h5>Inheritance</h5>
    <div class="level0"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.object">Object</a></div>
    <div class="level1"><a class="xref" href="Anki.Vector.RobotObject.html">RobotObject</a></div>
    <div class="level2"><a class="xref" href="Anki.Vector.Component.html">Component</a></div>
    <div class="level3"><span class="xref">AnimationComponent</span></div>
  </div>
  <div class="implements">
    <h5>Implements</h5>
    <div><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.componentmodel.inotifypropertychanged">INotifyPropertyChanged</a></div>
  </div>
  <div class="inheritedMembers">
    <h5>Inherited Members</h5>
    <div>
      <a class="xref" href="Anki.Vector.RobotObject.html#Anki_Vector_RobotObject_PropertyChanged">RobotObject.PropertyChanged</a>
    </div>
  </div>
  <h6><strong>Namespace</strong>: <a class="xref" href="Anki.Vector.html">Anki.Vector</a></h6>
  <h6><strong>Assembly</strong>: Anki.Vector.dll</h6>
  <h5 id="Anki_Vector_AnimationComponent_syntax">Syntax</h5>
  <div class="codewrapper">
    <pre><code class="lang-csharp hljs">public class AnimationComponent : Component, INotifyPropertyChanged</code></pre>
  </div>
  <h3 id="properties">Properties
  </h3>
  <span class="small pull-right mobile-hide">
    <span class="divider">|</span>
    <a href="https://github.com/codaris/Anki.Vector.SDK/new/master/apiSpec/new?filename=Anki_Vector_AnimationComponent_IsAnimating.md&amp;value=---%0Auid%3A%20Anki.Vector.AnimationComponent.IsAnimating%0Asummary%3A%20'*You%20can%20override%20summary%20for%20the%20API%20here%20using%20*MARKDOWN*%20syntax'%0A---%0A%0A*Please%20type%20below%20more%20information%20about%20this%20API%3A*%0A%0A">Improve this Doc</a>
  </span>
  <span class="small pull-right mobile-hide">
    <a href="https://github.com/codaris/Anki.Vector.SDK/blob/master/Anki.Vector/AnimationComponent.cs/#L51">View Source</a>
  </span>
  <a id="Anki_Vector_AnimationComponent_IsAnimating_" data-uid="Anki.Vector.AnimationComponent.IsAnimating*"></a>
  <h4 id="Anki_Vector_AnimationComponent_IsAnimating" data-uid="Anki.Vector.AnimationComponent.IsAnimating">IsAnimating</h4>
  <div class="markdown level1 summary"><p>Gets a value indicating whether the SDK is running an animation</p>
</div>
  <div class="markdown level1 conceptual"></div>
  <div class="codewrapper">
    <pre><code class="lang-csharp hljs">public bool IsAnimating { get; }</code></pre>
  </div>
  <h5 class="propertyValue">Property Type</h5>
  <table class="table table-bordered table-striped table-condensed">
    <tbody>
      <tr>
        <td><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
      </tr>
    </tbody>
  </table>
  <h3 id="methods">Methods
  </h3>
  <span class="small pull-right mobile-hide">
    <span class="divider">|</span>
    <a href="https://github.com/codaris/Anki.Vector.SDK/new/master/apiSpec/new?filename=Anki_Vector_AnimationComponent_GetAnimations.md&amp;value=---%0Auid%3A%20Anki.Vector.AnimationComponent.GetAnimations%0Asummary%3A%20'*You%20can%20override%20summary%20for%20the%20API%20here%20using%20*MARKDOWN*%20syntax'%0A---%0A%0A*Please%20type%20below%20more%20information%20about%20this%20API%3A*%0A%0A">Improve this Doc</a>
  </span>
  <span class="small pull-right mobile-hide">
    <a href="https://github.com/codaris/Anki.Vector.SDK/blob/master/Anki.Vector/AnimationComponent.cs/#L87">View Source</a>
  </span>
  <a id="Anki_Vector_AnimationComponent_GetAnimations_" data-uid="Anki.Vector.AnimationComponent.GetAnimations*"></a>
  <h4 id="Anki_Vector_AnimationComponent_GetAnimations" data-uid="Anki.Vector.AnimationComponent.GetAnimations">GetAnimations()</h4>
  <div class="markdown level1 summary"><p>Gets the list of animations returned from the robot.</p>
<p>Animations are dynamically retrieved from the robot the first time this method is called and cached for subsequent requests.</p>
</div>
  <div class="markdown level1 conceptual"></div>
  <div class="codewrapper">
    <pre><code class="lang-csharp hljs">public Task&lt;IReadOnlyDictionary&lt;string, Animation&gt;&gt; GetAnimations()</code></pre>
  </div>
  <h5 class="returns">Returns</h5>
  <table class="table table-bordered table-striped table-condensed">
    <thead>
      <tr>
        <th>Type</th>
        <th>Description</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.threading.tasks.task-1">Task</a>&lt;<a class="xref" href="https://docs.microsoft.com/dotnet/api/system.collections.generic.ireadonlydictionary-2">IReadOnlyDictionary</a>&lt;<a class="xref" href="https://docs.microsoft.com/dotnet/api/system.string">String</a>, <a class="xref" href="Anki.Vector.Types.Animation.html">Animation</a>&gt;&gt;</td>
        <td><p>A task that represents the asynchronous operation; the task result contains a dictionary of animations by name.</p>
</td>
      </tr>
    </tbody>
  </table>
  <span class="small pull-right mobile-hide">
    <span class="divider">|</span>
    <a href="https://github.com/codaris/Anki.Vector.SDK/new/master/apiSpec/new?filename=Anki_Vector_AnimationComponent_GetAnimationTriggers.md&amp;value=---%0Auid%3A%20Anki.Vector.AnimationComponent.GetAnimationTriggers%0Asummary%3A%20'*You%20can%20override%20summary%20for%20the%20API%20here%20using%20*MARKDOWN*%20syntax'%0A---%0A%0A*Please%20type%20below%20more%20information%20about%20this%20API%3A*%0A%0A">Improve this Doc</a>
  </span>
  <span class="small pull-right mobile-hide">
    <a href="https://github.com/codaris/Anki.Vector.SDK/blob/master/Anki.Vector/AnimationComponent.cs/#L105">View Source</a>
  </span>
  <a id="Anki_Vector_AnimationComponent_GetAnimationTriggers_" data-uid="Anki.Vector.AnimationComponent.GetAnimationTriggers*"></a>
  <h4 id="Anki_Vector_AnimationComponent_GetAnimationTriggers" data-uid="Anki.Vector.AnimationComponent.GetAnimationTriggers">GetAnimationTriggers()</h4>
  <div class="markdown level1 summary"><p>Gets the the set of animation triggers returned from the robot.</p>
<p>Animation triggers are dynamically retrieved from the robot the first time this method is called and cached for subsequent requests.</p>
<p>Playing an animation trigger causes the robot to play an animation of a particular type.</p>
<p>The robot may pick one of a number of actual animations to play based on Vector’s mood or emotion, or with random weighting. Thus playing the same trigger twice may not result in the exact same underlying animation playing twice.</p>
<p>To play an exact animation, use <a class="xref" href="Anki.Vector.AnimationComponent.html#Anki_Vector_AnimationComponent_PlayAnimation_Anki_Vector_Types_Animation_System_UInt32_System_Boolean_System_Boolean_System_Boolean_">PlayAnimation(Animation, UInt32, Boolean, Boolean, Boolean)</a>.</p>
</div>
  <div class="markdown level1 conceptual"></div>
  <div class="codewrapper">
    <pre><code class="lang-csharp hljs">public Task&lt;IReadOnlyDictionary&lt;string, AnimationTrigger&gt;&gt; GetAnimationTriggers()</code></pre>
  </div>
  <h5 class="returns">Returns</h5>
  <table class="table table-bordered table-striped table-condensed">
    <thead>
      <tr>
        <th>Type</th>
        <th>Description</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.threading.tasks.task-1">Task</a>&lt;<a class="xref" href="https://docs.microsoft.com/dotnet/api/system.collections.generic.ireadonlydictionary-2">IReadOnlyDictionary</a>&lt;<a class="xref" href="https://docs.microsoft.com/dotnet/api/system.string">String</a>, <a class="xref" href="Anki.Vector.Types.AnimationTrigger.html">AnimationTrigger</a>&gt;&gt;</td>
        <td><p>A task that represents the asynchronous operation; the task result contains a dictionary of animations triggers by name.</p>
</td>
      </tr>
    </tbody>
  </table>
  <span class="small pull-right mobile-hide">
    <span class="divider">|</span>
    <a href="https://github.com/codaris/Anki.Vector.SDK/new/master/apiSpec/new?filename=Anki_Vector_AnimationComponent_Play_Anki_Vector_Types_IAnimation_System_UInt32_System_Boolean_System_Boolean_System_Boolean_System_Boolean_.md&amp;value=---%0Auid%3A%20Anki.Vector.AnimationComponent.Play(Anki.Vector.Types.IAnimation%2CSystem.UInt32%2CSystem.Boolean%2CSystem.Boolean%2CSystem.Boolean%2CSystem.Boolean)%0Asummary%3A%20'*You%20can%20override%20summary%20for%20the%20API%20here%20using%20*MARKDOWN*%20syntax'%0A---%0A%0A*Please%20type%20below%20more%20information%20about%20this%20API%3A*%0A%0A">Improve this Doc</a>
  </span>
  <span class="small pull-right mobile-hide">
    <a href="https://github.com/codaris/Anki.Vector.SDK/blob/master/Anki.Vector/AnimationComponent.cs/#L217">View Source</a>
  </span>
  <a id="Anki_Vector_AnimationComponent_Play_" data-uid="Anki.Vector.AnimationComponent.Play*"></a>
  <h4 id="Anki_Vector_AnimationComponent_Play_Anki_Vector_Types_IAnimation_System_UInt32_System_Boolean_System_Boolean_System_Boolean_System_Boolean_" data-uid="Anki.Vector.AnimationComponent.Play(Anki.Vector.Types.IAnimation,System.UInt32,System.Boolean,System.Boolean,System.Boolean,System.Boolean)">Play(IAnimation, UInt32, Boolean, Boolean, Boolean, Boolean)</h4>
  <div class="markdown level1 summary"><p>Starts an animation or animation trigger playing on robot.</p>
</div>
  <div class="markdown level1 conceptual"></div>
  <div class="codewrapper">
    <pre><code class="lang-csharp hljs">public Task&lt;StatusCode&gt; Play(IAnimation animationOrTrigger, uint loopCount = 1U, bool useLiftSafe = false, bool ignoreBodyTrack = false, bool ignoreHeadTrack = false, bool ignoreLiftTrack = false)</code></pre>
  </div>
  <h5 class="parameters">Parameters</h5>
  <table class="table table-bordered table-striped table-condensed">
    <thead>
      <tr>
        <th>Type</th>
        <th>Name</th>
        <th>Description</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="nowrap"><a class="xref" href="Anki.Vector.Types.IAnimation.html">IAnimation</a></td>
        <td class="nowrap"><span class="parametername">animationOrTrigger</span></td>
        <td><p>The animation or animation trigger to play.</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.uint32">UInt32</a></td>
        <td class="nowrap"><span class="parametername">loopCount</span></td>
        <td><p>The loop count.</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
        <td class="nowrap"><span class="parametername">useLiftSafe</span></td>
        <td><p>if set to <code>true</code> automatically ignore the lift track if Vector is currently carrying an object.</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
        <td class="nowrap"><span class="parametername">ignoreBodyTrack</span></td>
        <td><p>if set to <code>true</code> ignore the animation track for Vector’s body (i.e. the wheels / treads).</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
        <td class="nowrap"><span class="parametername">ignoreHeadTrack</span></td>
        <td><p>if set to <code>true</code> ignore the animation track for Vector’s head.</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
        <td class="nowrap"><span class="parametername">ignoreLiftTrack</span></td>
        <td><p>if set to <code>true</code> ignore the animation track for Vector’s lift.</p>
</td>
      </tr>
    </tbody>
  </table>
  <h5 class="returns">Returns</h5>
  <table class="table table-bordered table-striped table-condensed">
    <thead>
      <tr>
        <th>Type</th>
        <th>Description</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.threading.tasks.task-1">Task</a>&lt;<a class="xref" href="Anki.Vector.Types.StatusCode.html">StatusCode</a>&gt;</td>
        <td><p>A task that represents the asynchronous operation; the task result contains the result from the robot.</p>
</td>
      </tr>
    </tbody>
  </table>
  <h5 id="Anki_Vector_AnimationComponent_Play_Anki_Vector_Types_IAnimation_System_UInt32_System_Boolean_System_Boolean_System_Boolean_System_Boolean__remarks">Remarks</h5>
  <div class="markdown level1 remarks"><p>Requires behavior control; see <a class="xref" href="Anki.Vector.ControlComponent.html#Anki_Vector_ControlComponent_RequestControl_System_Int32_">RequestControl(Int32)</a>.</p>
</div>
  <h5 class="exceptions">Exceptions</h5>
  <table class="table table-bordered table-striped table-condensed">
    <thead>
      <tr>
        <th>Type</th>
        <th>Condition</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.argumentexception">ArgumentException</a></td>
        <td><p>Parameter is not an Animation or AnimationTrigger</p>
</td>
      </tr>
    </tbody>
  </table>
  <span class="small pull-right mobile-hide">
    <span class="divider">|</span>
    <a href="https://github.com/codaris/Anki.Vector.SDK/new/master/apiSpec/new?filename=Anki_Vector_AnimationComponent_PlayAnimation_Anki_Vector_Types_Animation_System_UInt32_System_Boolean_System_Boolean_System_Boolean_.md&amp;value=---%0Auid%3A%20Anki.Vector.AnimationComponent.PlayAnimation(Anki.Vector.Types.Animation%2CSystem.UInt32%2CSystem.Boolean%2CSystem.Boolean%2CSystem.Boolean)%0Asummary%3A%20'*You%20can%20override%20summary%20for%20the%20API%20here%20using%20*MARKDOWN*%20syntax'%0A---%0A%0A*Please%20type%20below%20more%20information%20about%20this%20API%3A*%0A%0A">Improve this Doc</a>
  </span>
  <span class="small pull-right mobile-hide">
    <a href="https://github.com/codaris/Anki.Vector.SDK/blob/master/Anki.Vector/AnimationComponent.cs/#L125">View Source</a>
  </span>
  <a id="Anki_Vector_AnimationComponent_PlayAnimation_" data-uid="Anki.Vector.AnimationComponent.PlayAnimation*"></a>
  <h4 id="Anki_Vector_AnimationComponent_PlayAnimation_Anki_Vector_Types_Animation_System_UInt32_System_Boolean_System_Boolean_System_Boolean_" data-uid="Anki.Vector.AnimationComponent.PlayAnimation(Anki.Vector.Types.Animation,System.UInt32,System.Boolean,System.Boolean,System.Boolean)">PlayAnimation(Animation, UInt32, Boolean, Boolean, Boolean)</h4>
  <div class="markdown level1 summary"><p>Starts an animation playing on a robot.</p>
</div>
  <div class="markdown level1 conceptual"></div>
  <div class="codewrapper">
    <pre><code class="lang-csharp hljs">public Task&lt;StatusCode&gt; PlayAnimation(Animation animation, uint loopCount = 1U, bool ignoreBodyTrack = false, bool ignoreHeadTrack = false, bool ignoreLiftTrack = false)</code></pre>
  </div>
  <h5 class="parameters">Parameters</h5>
  <table class="table table-bordered table-striped table-condensed">
    <thead>
      <tr>
        <th>Type</th>
        <th>Name</th>
        <th>Description</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="nowrap"><a class="xref" href="Anki.Vector.Types.Animation.html">Animation</a></td>
        <td class="nowrap"><span class="parametername">animation</span></td>
        <td><p>The animation to play.</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.uint32">UInt32</a></td>
        <td class="nowrap"><span class="parametername">loopCount</span></td>
        <td><p>Number of times to play the animation.</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
        <td class="nowrap"><span class="parametername">ignoreBodyTrack</span></td>
        <td><p>if set to <code>true</code> ignore the animation track for Vector’s body (i.e. the wheels / treads).</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
        <td class="nowrap"><span class="parametername">ignoreHeadTrack</span></td>
        <td><p>if set to <code>true</code> ignore the animation track for Vector’s head.</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
        <td class="nowrap"><span class="parametername">ignoreLiftTrack</span></td>
        <td><p>if set to <code>true</code> ignore the animation track for Vector’s lift.</p>
</td>
      </tr>
    </tbody>
  </table>
  <h5 class="returns">Returns</h5>
  <table class="table table-bordered table-striped table-condensed">
    <thead>
      <tr>
        <th>Type</th>
        <th>Description</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.threading.tasks.task-1">Task</a>&lt;<a class="xref" href="Anki.Vector.Types.StatusCode.html">StatusCode</a>&gt;</td>
        <td><p>A task that represents the asynchronous operation; the task result contains the result from the robot.</p>
</td>
      </tr>
    </tbody>
  </table>
  <h5 id="Anki_Vector_AnimationComponent_PlayAnimation_Anki_Vector_Types_Animation_System_UInt32_System_Boolean_System_Boolean_System_Boolean__remarks">Remarks</h5>
  <div class="markdown level1 remarks"><p>Requires behavior control; see <a class="xref" href="Anki.Vector.ControlComponent.html#Anki_Vector_ControlComponent_RequestControl_System_Int32_">RequestControl(Int32)</a>.</p>
</div>
  <span class="small pull-right mobile-hide">
    <span class="divider">|</span>
    <a href="https://github.com/codaris/Anki.Vector.SDK/new/master/apiSpec/new?filename=Anki_Vector_AnimationComponent_PlayAnimation_System_String_System_UInt32_System_Boolean_System_Boolean_System_Boolean_.md&amp;value=---%0Auid%3A%20Anki.Vector.AnimationComponent.PlayAnimation(System.String%2CSystem.UInt32%2CSystem.Boolean%2CSystem.Boolean%2CSystem.Boolean)%0Asummary%3A%20'*You%20can%20override%20summary%20for%20the%20API%20here%20using%20*MARKDOWN*%20syntax'%0A---%0A%0A*Please%20type%20below%20more%20information%20about%20this%20API%3A*%0A%0A">Improve this Doc</a>
  </span>
  <span class="small pull-right mobile-hide">
    <a href="https://github.com/codaris/Anki.Vector.SDK/blob/master/Anki.Vector/AnimationComponent.cs/#L151">View Source</a>
  </span>
  <a id="Anki_Vector_AnimationComponent_PlayAnimation_" data-uid="Anki.Vector.AnimationComponent.PlayAnimation*"></a>
  <h4 id="Anki_Vector_AnimationComponent_PlayAnimation_System_String_System_UInt32_System_Boolean_System_Boolean_System_Boolean_" data-uid="Anki.Vector.AnimationComponent.PlayAnimation(System.String,System.UInt32,System.Boolean,System.Boolean,System.Boolean)">PlayAnimation(String, UInt32, Boolean, Boolean, Boolean)</h4>
  <div class="markdown level1 summary"><p>Starts an animation playing on a robot.</p>
</div>
  <div class="markdown level1 conceptual"></div>
  <div class="codewrapper">
    <pre><code class="lang-csharp hljs">public Task&lt;StatusCode&gt; PlayAnimation(string animationName, uint loopCount = 1U, bool ignoreBodyTrack = false, bool ignoreHeadTrack = false, bool ignoreLiftTrack = false)</code></pre>
  </div>
  <h5 class="parameters">Parameters</h5>
  <table class="table table-bordered table-striped table-condensed">
    <thead>
      <tr>
        <th>Type</th>
        <th>Name</th>
        <th>Description</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.string">String</a></td>
        <td class="nowrap"><span class="parametername">animationName</span></td>
        <td><p>Name of the animation to play.</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.uint32">UInt32</a></td>
        <td class="nowrap"><span class="parametername">loopCount</span></td>
        <td><p>Number of times to play the animation.</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
        <td class="nowrap"><span class="parametername">ignoreBodyTrack</span></td>
        <td><p>if set to <code>true</code> ignore the animation track for Vector’s body (i.e. the wheels / treads).</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
        <td class="nowrap"><span class="parametername">ignoreHeadTrack</span></td>
        <td><p>if set to <code>true</code> ignore the animation track for Vector’s head.</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
        <td class="nowrap"><span class="parametername">ignoreLiftTrack</span></td>
        <td><p>if set to <code>true</code> ignore the animation track for Vector’s lift.</p>
</td>
      </tr>
    </tbody>
  </table>
  <h5 class="returns">Returns</h5>
  <table class="table table-bordered table-striped table-condensed">
    <thead>
      <tr>
        <th>Type</th>
        <th>Description</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.threading.tasks.task-1">Task</a>&lt;<a class="xref" href="Anki.Vector.Types.StatusCode.html">StatusCode</a>&gt;</td>
        <td><p>A task that represents the asynchronous operation; the task result contains the result from the robot.</p>
</td>
      </tr>
    </tbody>
  </table>
  <h5 id="Anki_Vector_AnimationComponent_PlayAnimation_System_String_System_UInt32_System_Boolean_System_Boolean_System_Boolean__remarks">Remarks</h5>
  <div class="markdown level1 remarks"><p>Requires behavior control; see <a class="xref" href="Anki.Vector.ControlComponent.html#Anki_Vector_ControlComponent_RequestControl_System_Int32_">RequestControl(Int32)</a>.</p>
</div>
  <h5 class="exceptions">Exceptions</h5>
  <table class="table table-bordered table-striped table-condensed">
    <thead>
      <tr>
        <th>Type</th>
        <th>Condition</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.collections.generic.keynotfoundexception">KeyNotFoundException</a></td>
        <td><p>Unknown animation '{animationName}'</p>
</td>
      </tr>
    </tbody>
  </table>
  <span class="small pull-right mobile-hide">
    <span class="divider">|</span>
    <a href="https://github.com/codaris/Anki.Vector.SDK/new/master/apiSpec/new?filename=Anki_Vector_AnimationComponent_PlayAnimationTrigger_Anki_Vector_Types_AnimationTrigger_System_UInt32_System_Boolean_System_Boolean_System_Boolean_System_Boolean_.md&amp;value=---%0Auid%3A%20Anki.Vector.AnimationComponent.PlayAnimationTrigger(Anki.Vector.Types.AnimationTrigger%2CSystem.UInt32%2CSystem.Boolean%2CSystem.Boolean%2CSystem.Boolean%2CSystem.Boolean)%0Asummary%3A%20'*You%20can%20override%20summary%20for%20the%20API%20here%20using%20*MARKDOWN*%20syntax'%0A---%0A%0A*Please%20type%20below%20more%20information%20about%20this%20API%3A*%0A%0A">Improve this Doc</a>
  </span>
  <span class="small pull-right mobile-hide">
    <a href="https://github.com/codaris/Anki.Vector.SDK/blob/master/Anki.Vector/AnimationComponent.cs/#L170">View Source</a>
  </span>
  <a id="Anki_Vector_AnimationComponent_PlayAnimationTrigger_" data-uid="Anki.Vector.AnimationComponent.PlayAnimationTrigger*"></a>
  <h4 id="Anki_Vector_AnimationComponent_PlayAnimationTrigger_Anki_Vector_Types_AnimationTrigger_System_UInt32_System_Boolean_System_Boolean_System_Boolean_System_Boolean_" data-uid="Anki.Vector.AnimationComponent.PlayAnimationTrigger(Anki.Vector.Types.AnimationTrigger,System.UInt32,System.Boolean,System.Boolean,System.Boolean,System.Boolean)">PlayAnimationTrigger(AnimationTrigger, UInt32, Boolean, Boolean, Boolean, Boolean)</h4>
  <div class="markdown level1 summary"><p>Starts an animation trigger playing on a robot.</p>
<p>Playing a trigger requests that an animation of a certain class starts playing, rather than an exact animation name.</p>
</div>
  <div class="markdown level1 conceptual"></div>
  <div class="codewrapper">
    <pre><code class="lang-csharp hljs">public Task&lt;StatusCode&gt; PlayAnimationTrigger(AnimationTrigger animationTrigger, uint loopCount = 1U, bool useLiftSafe = false, bool ignoreBodyTrack = false, bool ignoreHeadTrack = false, bool ignoreLiftTrack = false)</code></pre>
  </div>
  <h5 class="parameters">Parameters</h5>
  <table class="table table-bordered table-striped table-condensed">
    <thead>
      <tr>
        <th>Type</th>
        <th>Name</th>
        <th>Description</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="nowrap"><a class="xref" href="Anki.Vector.Types.AnimationTrigger.html">AnimationTrigger</a></td>
        <td class="nowrap"><span class="parametername">animationTrigger</span></td>
        <td><p>The animation trigger to play.</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.uint32">UInt32</a></td>
        <td class="nowrap"><span class="parametername">loopCount</span></td>
        <td><p>The loop count.</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
        <td class="nowrap"><span class="parametername">useLiftSafe</span></td>
        <td><p>if set to <code>true</code> automatically ignore the lift track if Vector is currently carrying an object.</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
        <td class="nowrap"><span class="parametername">ignoreBodyTrack</span></td>
        <td><p>if set to <code>true</code> ignore the animation track for Vector’s body (i.e. the wheels / treads).</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
        <td class="nowrap"><span class="parametername">ignoreHeadTrack</span></td>
        <td><p>if set to <code>true</code> ignore the animation track for Vector’s head.</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
        <td class="nowrap"><span class="parametername">ignoreLiftTrack</span></td>
        <td><p>if set to <code>true</code> ignore the animation track for Vector’s lift.</p>
</td>
      </tr>
    </tbody>
  </table>
  <h5 class="returns">Returns</h5>
  <table class="table table-bordered table-striped table-condensed">
    <thead>
      <tr>
        <th>Type</th>
        <th>Description</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.threading.tasks.task-1">Task</a>&lt;<a class="xref" href="Anki.Vector.Types.StatusCode.html">StatusCode</a>&gt;</td>
        <td><p>A task that represents the asynchronous operation; the task result contains the result from the robot.</p>
</td>
      </tr>
    </tbody>
  </table>
  <h5 id="Anki_Vector_AnimationComponent_PlayAnimationTrigger_Anki_Vector_Types_AnimationTrigger_System_UInt32_System_Boolean_System_Boolean_System_Boolean_System_Boolean__remarks">Remarks</h5>
  <div class="markdown level1 remarks"><p>Requires behavior control; see <a class="xref" href="Anki.Vector.ControlComponent.html#Anki_Vector_ControlComponent_RequestControl_System_Int32_">RequestControl(Int32)</a>.</p>
</div>
  <span class="small pull-right mobile-hide">
    <span class="divider">|</span>
    <a href="https://github.com/codaris/Anki.Vector.SDK/new/master/apiSpec/new?filename=Anki_Vector_AnimationComponent_PlayAnimationTrigger_System_String_System_UInt32_System_Boolean_System_Boolean_System_Boolean_System_Boolean_.md&amp;value=---%0Auid%3A%20Anki.Vector.AnimationComponent.PlayAnimationTrigger(System.String%2CSystem.UInt32%2CSystem.Boolean%2CSystem.Boolean%2CSystem.Boolean%2CSystem.Boolean)%0Asummary%3A%20'*You%20can%20override%20summary%20for%20the%20API%20here%20using%20*MARKDOWN*%20syntax'%0A---%0A%0A*Please%20type%20below%20more%20information%20about%20this%20API%3A*%0A%0A">Improve this Doc</a>
  </span>
  <span class="small pull-right mobile-hide">
    <a href="https://github.com/codaris/Anki.Vector.SDK/blob/master/Anki.Vector/AnimationComponent.cs/#L198">View Source</a>
  </span>
  <a id="Anki_Vector_AnimationComponent_PlayAnimationTrigger_" data-uid="Anki.Vector.AnimationComponent.PlayAnimationTrigger*"></a>
  <h4 id="Anki_Vector_AnimationComponent_PlayAnimationTrigger_System_String_System_UInt32_System_Boolean_System_Boolean_System_Boolean_System_Boolean_" data-uid="Anki.Vector.AnimationComponent.PlayAnimationTrigger(System.String,System.UInt32,System.Boolean,System.Boolean,System.Boolean,System.Boolean)">PlayAnimationTrigger(String, UInt32, Boolean, Boolean, Boolean, Boolean)</h4>
  <div class="markdown level1 summary"><p>Starts an animation trigger playing on a robot.</p>
<p>Playing a trigger requests that an animation of a certain class starts playing, rather than an exact animation name.</p>
</div>
  <div class="markdown level1 conceptual"></div>
  <div class="codewrapper">
    <pre><code class="lang-csharp hljs">public Task&lt;StatusCode&gt; PlayAnimationTrigger(string animationTriggerName, uint loopCount = 1U, bool useLiftSafe = false, bool ignoreBodyTrack = false, bool ignoreHeadTrack = false, bool ignoreLiftTrack = false)</code></pre>
  </div>
  <h5 class="parameters">Parameters</h5>
  <table class="table table-bordered table-striped table-condensed">
    <thead>
      <tr>
        <th>Type</th>
        <th>Name</th>
        <th>Description</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.string">String</a></td>
        <td class="nowrap"><span class="parametername">animationTriggerName</span></td>
        <td><p>Name of the animation trigger to play.</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.uint32">UInt32</a></td>
        <td class="nowrap"><span class="parametername">loopCount</span></td>
        <td><p>The loop count.</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
        <td class="nowrap"><span class="parametername">useLiftSafe</span></td>
        <td><p>if set to <code>true</code> automatically ignore the lift track if Vector is currently carrying an object.</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
        <td class="nowrap"><span class="parametername">ignoreBodyTrack</span></td>
        <td><p>if set to <code>true</code> ignore the animation track for Vector’s body (i.e. the wheels / treads).</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
        <td class="nowrap"><span class="parametername">ignoreHeadTrack</span></td>
        <td><p>if set to <code>true</code> ignore the animation track for Vector’s head.</p>
</td>
      </tr>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a></td>
        <td class="nowrap"><span class="parametername">ignoreLiftTrack</span></td>
        <td><p>if set to <code>true</code> ignore the animation track for Vector’s lift.</p>
</td>
      </tr>
    </tbody>
  </table>
  <h5 class="returns">Returns</h5>
  <table class="table table-bordered table-striped table-condensed">
    <thead>
      <tr>
        <th>Type</th>
        <th>Description</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.threading.tasks.task-1">Task</a>&lt;<a class="xref" href="Anki.Vector.Types.StatusCode.html">StatusCode</a>&gt;</td>
        <td><p>A task that represents the asynchronous operation; the task result contains the result from the robot.</p>
</td>
      </tr>
    </tbody>
  </table>
  <h5 id="Anki_Vector_AnimationComponent_PlayAnimationTrigger_System_String_System_UInt32_System_Boolean_System_Boolean_System_Boolean_System_Boolean__remarks">Remarks</h5>
  <div class="markdown level1 remarks"><p>Requires behavior control; see <a class="xref" href="Anki.Vector.ControlComponent.html#Anki_Vector_ControlComponent_RequestControl_System_Int32_">RequestControl(Int32)</a>.</p>
</div>
  <span class="small pull-right mobile-hide">
    <span class="divider">|</span>
    <a href="https://github.com/codaris/Anki.Vector.SDK/new/master/apiSpec/new?filename=Anki_Vector_AnimationComponent_WaitForAnimationCompletion.md&amp;value=---%0Auid%3A%20Anki.Vector.AnimationComponent.WaitForAnimationCompletion%0Asummary%3A%20'*You%20can%20override%20summary%20for%20the%20API%20here%20using%20*MARKDOWN*%20syntax'%0A---%0A%0A*Please%20type%20below%20more%20information%20about%20this%20API%3A*%0A%0A">Improve this Doc</a>
  </span>
  <span class="small pull-right mobile-hide">
    <a href="https://github.com/codaris/Anki.Vector.SDK/blob/master/Anki.Vector/AnimationComponent.cs/#L240">View Source</a>
  </span>
  <a id="Anki_Vector_AnimationComponent_WaitForAnimationCompletion_" data-uid="Anki.Vector.AnimationComponent.WaitForAnimationCompletion*"></a>
  <h4 id="Anki_Vector_AnimationComponent_WaitForAnimationCompletion" data-uid="Anki.Vector.AnimationComponent.WaitForAnimationCompletion">WaitForAnimationCompletion()</h4>
  <div class="markdown level1 summary"><p>Waits for the current animation to complete.</p>
<p>The task will complete when animation is finished.  If no animation is running, this method will return immediately.</p>
</div>
  <div class="markdown level1 conceptual"></div>
  <div class="codewrapper">
    <pre><code class="lang-csharp hljs">public Task&lt;bool&gt; WaitForAnimationCompletion()</code></pre>
  </div>
  <h5 class="returns">Returns</h5>
  <table class="table table-bordered table-striped table-condensed">
    <thead>
      <tr>
        <th>Type</th>
        <th>Description</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="nowrap"><a class="xref" href="https://docs.microsoft.com/dotnet/api/system.threading.tasks.task-1">Task</a>&lt;<a class="xref" href="https://docs.microsoft.com/dotnet/api/system.boolean">Boolean</a>&gt;</td>
        <td><p>A task that represents the asynchronous operation.  The task result is false if no task was running.</p>
</td>
      </tr>
    </tbody>
  </table>
  <h3 id="implements">Implements</h3>
  <div>
      <a class="xref" href="https://docs.microsoft.com/dotnet/api/system.componentmodel.inotifypropertychanged">System.ComponentModel.INotifyPropertyChanged</a>
  </div>
  <h3 id="seealso">See Also</h3>
  <div class="seealso">
      <div><a class="xref" href="Anki.Vector.Component.html">Component</a></div>
  </div>
  
</article>
          </div>
          
          <div class="hidden-sm col-md-2" role="complementary">
            <div class="sideaffix">
              <div class="contribution">
                <ul class="nav">
                  <li>
                    <a href="https://github.com/codaris/Anki.Vector.SDK/new/master/apiSpec/new?filename=Anki_Vector_AnimationComponent.md&amp;value=---%0Auid%3A%20Anki.Vector.AnimationComponent%0Asummary%3A%20'*You%20can%20override%20summary%20for%20the%20API%20here%20using%20*MARKDOWN*%20syntax'%0A---%0A%0A*Please%20type%20below%20more%20information%20about%20this%20API%3A*%0A%0A" class="contribution-link">Improve this Doc</a>
                  </li>
                  <li>
                    <a href="https://github.com/codaris/Anki.Vector.SDK/blob/master/Anki.Vector/AnimationComponent.cs/#L26" class="contribution-link">View Source</a>
                  </li>
                </ul>
              </div>
              <nav class="bs-docs-sidebar hidden-print hidden-xs hidden-sm affix" id="affix">
              <!-- <p><a class="back-to-top" href="#top">Back to top</a><p> -->
              </nav>
            </div>
          </div>
        </div>
      </div>
      
      <footer>
        <div class="grad-bottom"></div>
        <div class="footer">
          <div class="container-fluid">
            <span class="pull-right">
              <a href="#top">Back to top</a>
            </span>
            
            <span>Copyright (c) 2019 by <NAME></span>
          </div>
        </div>
      </footer>
    </div>
    
    <script type="text/javascript" src="../styles/docfx.vendor.js"></script>
    <script type="text/javascript" src="../styles/docfx.js"></script>
    <script type="text/javascript" src="../styles/main.js"></script>
  </body>
</html>


<|start_filename|>Anki.Vector/Events/RobotErasedEnrolledFaceEventArgs.cs<|end_filename|>
﻿// <copyright file="RobotErasedEnrolledFaceEventArgs.cs" company="<NAME>">
//     Copyright (c) 2020 <NAME>. All rights reserved.
// </copyright>

using System;
using System.Collections.Generic;
using System.Text;

namespace Anki.Vector.Events
{
    /// <summary>
    /// Robot erased enrolled face event args
    /// </summary>
    /// <seealso cref="Anki.Vector.Events.RobotEventArgs" />
    [Serializable]
    public class RobotErasedEnrolledFaceEventArgs : RobotEventArgs
    {
        /// <summary>
        /// Gets the face identifier.
        /// </summary>
        public int FaceId { get; }

        /// <summary>
        /// Gets the face name.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2235:Mark all non-serializable fields", Justification = "Invalid")]
        public string Name { get; }

        /// <summary>
        /// Initializes a new instance of the <see cref="RobotErasedEnrolledFaceEventArgs"/> class.
        /// </summary>
        /// <param name="e">The e.</param>
        internal RobotErasedEnrolledFaceEventArgs(ExternalInterface.Event e) : base(e)
        {
            FaceId = e.RobotErasedEnrolledFace.FaceId;
            Name = e.RobotErasedEnrolledFace.Name;
        }
    }
}


<|start_filename|>Anki.Vector/Events/CheckUpdateStatusEventArgs.cs<|end_filename|>
﻿// <copyright file="CheckUpdateStatusEventArgs.cs" company="<NAME>">
//     Copyright (c) 2020 <NAME>. All rights reserved.
// </copyright>

using System;
using System.Collections.Generic;
using System.Text;
using Anki.Vector.ExternalInterface;
using Anki.Vector.Types;

namespace Anki.Vector.Events
{
    /// <summary>
    /// Check update status event args
    /// </summary>
    [Serializable]
    public class CheckUpdateStatusEventArgs : RobotEventArgs
    {
        /// <summary>Gets the expected number of bytes to download</summary>
        public long Expected { get; }

        /// <summary>Gets the current number of bytes downloaded</summary>
        public long Progress { get; }

        /// <summary>Gets the update status.</summary>
        public UpdateStatus UpdateStatus { get; }

        /// <summary>
        /// Gets the update version.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2235:Mark all non-serializable fields", Justification = "Invalid")]
        public string UpdateVersion { get; }

        /// <summary>
        /// Initializes a new instance of the <see cref="CheckUpdateStatusEventArgs"/> class.
        /// </summary>
        /// <param name="e">The e.</param>
        internal CheckUpdateStatusEventArgs(ExternalInterface.Event e) : base(e)
        {
            Expected = e.CheckUpdateStatusResponse.Expected;
            Progress = e.CheckUpdateStatusResponse.Progress;
            UpdateStatus = (UpdateStatus)e.CheckUpdateStatusResponse.UpdateStatus;
            UpdateVersion = e.CheckUpdateStatusResponse.UpdateVersion;
        }
    }
}


<|start_filename|>Anki.Vector/Events/AlexaAuthEventArgs.cs<|end_filename|>
﻿// <copyright file="AlexaAuthEventArgs.cs" company="<NAME>">
//     Copyright (c) 2020 <NAME>. All rights reserved.
// </copyright>

using System;
using System.Collections.Generic;
using System.Text;

namespace Anki.Vector.Events
{
    /// <summary>
    /// Alexa authorization state
    /// </summary>
    public enum AlexaAuthState
    {
        /// <summary>
        /// Invalid/error/versioning issue
        /// </summary>
        AlexaAuthInvalid = 0,
        /// <summary>
        /// Not opted in, or opt-in attempted but failed
        /// </summary>
        AlexaAuthUninitialized = 1,
        /// <summary>
        /// Opted in, and attempting to authorize
        /// </summary>
        AlexaAuthRequestingAuth = 2,
        /// <summary>
        /// Opted in, and waiting on the user to enter a code
        /// </summary>
        AlexaAuthWaitingForCode = 3,
        /// <summary>
        /// Opted in, and authorized / in use
        /// </summary>
        AlexaAuthAuthorized = 4,
    }

    /// <summary>
    /// Alexa authorization event args
    /// </summary>
    /// <seealso cref="Anki.Vector.Events.RobotEventArgs" />
    [Serializable]
    public class AlexaAuthEventArgs : RobotEventArgs
    {
        /// <summary>
        /// Gets the state of the alexa authentication.
        /// </summary>
        public AlexaAuthState AuthState { get; }

        /// <summary>
        /// Gets the extra data associated with this event.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2235:Mark all non-serializable fields", Justification = "Invalid")]
        public string Extra { get; }

        /// <summary>
        /// Initializes a new instance of the <see cref="AlexaAuthEventArgs"/> class.
        /// </summary>
        /// <param name="e">The e.</param>
        internal AlexaAuthEventArgs(ExternalInterface.Event e) : base(e)
        {
            AuthState = (AlexaAuthState)e.AlexaAuthEvent.AuthState;
            Extra = e.AlexaAuthEvent.Extra;
        }
    }
}


<|start_filename|>Anki.Vector/Events/JdocsChangedEventArgs.cs<|end_filename|>
﻿// <copyright file="JdocsChangedEventArgs.cs" company="<NAME>">
//     Copyright (c) 2020 <NAME>. All rights reserved.
// </copyright>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Anki.Vector.Events
{
    /// <summary>
    /// The JDOC type that changed
    /// </summary>
    public enum JdocType
    {
        /// <summary>The robot settings </summary>
        RobotSettings = 0,
        /// <summary>The robot lifetime stats</summary>
        RobotLifetimeStats = 1,
        /// <summary>The account settings</summary>
        AccountSettings = 2,
        /// <summary>The user entitlements</summary>
        UserEntitlements = 3,
    }

    /// <summary>
    /// JDocs changes event args
    /// </summary>
    /// <seealso cref="Anki.Vector.Events.RobotEventArgs" />
    [Serializable]
    public class JdocsChangedEventArgs : RobotEventArgs
    {
        /// <summary>
        /// Gets the changed jdocs.
        /// </summary>
        public IReadOnlyList<JdocType> ChangedJdocs { get; }

        /// <summary>
        /// Initializes a new instance of the <see cref="JdocsChangedEventArgs"/> class.
        /// </summary>
        /// <param name="e">The event.</param>
        internal JdocsChangedEventArgs(ExternalInterface.Event e) : base(e)
        {
            // List the change jdocs
            ChangedJdocs = e.JdocsChanged.JdocTypes.Select(jdoc => (JdocType)jdoc).ToList();
        }
    }
}


<|start_filename|>Anki.Vector/Events/UnexpectedMovementEventArgs.cs<|end_filename|>
﻿// <copyright file="UnexpectedMovementEventArgs.cs" company="<NAME>">
//     Copyright (c) 2020 <NAME>. All rights reserved.
// </copyright>

using System;
using System.Collections.Generic;
using System.Text;

namespace Anki.Vector.Events
{
    /// <summary>
    /// The side of the unexpected movement
    /// </summary>
    public enum UnexpectedMovementSide
    {
        /// <summary>The side is unknown</summary>
        Unknown = 0,
        /// <summary>The front side</summary>
        Front = 1,
        /// <summary>The back side</summary>
        Back = 2,
        /// <summary>The left side</summary>
        Left = 3,
        /// <summary>The right side</summary>
        Right = 4,
    }

    /// <summary>
    /// Unexpected movement type
    /// </summary>
    public enum UnexpectedMovementType
    {
        /// <summary>Turned but stopped</summary>
        TurnedButStopped = 0,
        /// <summary>Turned in same direction</summary>
        TurnedInSameDirection = 1,
        /// <summary>Turned in opposite direction</summary>
        TurnedInOppositeDirection = 2,
        /// <summary>Rotating without motors</summary>
        RotatingWithoutMotors = 3,
    }

    /// <summary>
    /// Unexpected movement event args
    /// </summary>
    /// <seealso cref="Anki.Vector.Events.RobotEventArgs" />
    [Serializable]
    public class UnexpectedMovementEventArgs : RobotEventArgs
    {
        /// <summary>Gets the unexpected movement side.</summary>
        public UnexpectedMovementSide UnexpectedMovementSide { get; }

        /// <summary>Gets the type of the unexpected movement.</summary>
        public UnexpectedMovementType UnexpectedMovementType { get; }

        /// <summary>Gets the timestamp.</summary>
        public uint Timestamp { get; }

        /// <summary>
        /// Initializes a new instance of the <see cref="UnexpectedMovementEventArgs"/> class.
        /// </summary>
        /// <param name="e">The e.</param>
        internal UnexpectedMovementEventArgs(ExternalInterface.Event e) : base(e)
        {
            UnexpectedMovementSide = (UnexpectedMovementSide)e.UnexpectedMovement.MovementSide;
            UnexpectedMovementType = (UnexpectedMovementType)e.UnexpectedMovement.MovementType;
            Timestamp = e.UnexpectedMovement.Timestamp;              
        }
    }
}
