/// <summary>
 /// Called to retry finding a sensor when the SensorConflict or
 /// NoAvailableSensors flags are set.
 /// </summary>
 public void TryResolveConflict()
 {
     using (var callbackLock = new CallbackLock(lockObject))
     {
         TryFindAndStartKinect(callbackLock);
     }
 }
        /// <summary>
        /// Gives up the current sensor if it has one.  Stops watching for new sensors.
        /// </summary>
        public void Stop()
        {
            if (isStarted)
            {
                using (var callbackLock = new CallbackLock(lockObject))
                {
                    // Check again in case someone stopped us while
                    // we were waiting on the lock.
                    if (isStarted)
                    {
                        isStarted = false;

                        KinectSensor.KinectSensors.StatusChanged -= KinectSensorsOnStatusChanged;

                        SetSensorAndStatus(callbackLock, null, ChooserStatus.None);
                    }
                }
            }
        }
 private void KinectSensorsOnStatusChanged(object sender, StatusChangedEventArgs e)
 {
     if (e != null)
     {
         if ((e.Sensor == this.Kinect) || (this.Kinect == null))
         {
             using (var callbackLock = new CallbackLock(lockObject))
             {
                 // Check again in case things changed while we were
                 // waiting on the lock.
                 if ((e.Sensor == this.Kinect) || (this.Kinect == null))
                 {
                     // Something about our sensor changed or we don't have a sensor.
                     TryFindAndStartKinect(callbackLock);
                 }
             }
         }
     }
 }
        /// <summary>
        /// Starts choosing a sensor.  In the typical case, a sensor will
        /// be found and events will be fired before this function returns.
        /// </summary>
        public void Start()
        {
            if (!isStarted)
            {
                using (var callbackLock = new CallbackLock(lockObject))
                {
                    // Check again in case someone else started while
                    // we were waiting on the lock.
                    if (!isStarted)
                    {
                        isStarted = true;

                        KinectSensor.KinectSensors.StatusChanged += KinectSensorsOnStatusChanged;

                        TryFindAndStartKinect(callbackLock);
                    }
                }
            }
        }
        /// <summary>
        /// Helper to update our external status.
        /// </summary>
        /// <param name="callbackLock">Used to delay notifications to the client until after we exit the lock.</param>
        /// <param name="newKinect">The new sensor</param>
        /// <param name="newChooserStatus">The status we want to report to clients</param>
        private void SetSensorAndStatus(CallbackLock callbackLock, KinectSensor newKinect, ChooserStatus newChooserStatus)
        {
            KinectSensor oldKinect = Kinect;

            if (oldKinect != newKinect)
            {
                if (oldKinect != null)
                {
                    oldKinect.Stop();
                }

                Kinect = newKinect;

                callbackLock.LockExit += () => this.kinectChangedContextWrapper.Invoke(this, new KinectChangedEventArgs(oldKinect, newKinect));
                callbackLock.LockExit += () => RaisePropertyChanged("Kinect");
            }

            if (Status != newChooserStatus)
            {
                Status = newChooserStatus;

                callbackLock.LockExit += () => RaisePropertyChanged("Status");
            }
        }
 private void KinectSensorsOnStatusChanged(object sender, StatusChangedEventArgs e)
 {
     if (e != null)
     {
         if ((e.Sensor == this.Kinect) || (this.Kinect == null))
         {
             using (var callbackLock = new CallbackLock(lockObject))
             {
                 // Check again in case things changed while we were
                 // waiting on the lock.
                 if ((e.Sensor == this.Kinect) || (this.Kinect == null))
                 {
                     // Something about our sensor changed or we don't have a sensor.
                     TryFindAndStartKinect(callbackLock);
                 }
             }
         }
     }
 }
 /// <summary>
 /// Called to retry finding a sensor when the SensorConflict or
 /// NoAvailableSensors flags are set.
 /// </summary>
 public void TryResolveConflict()
 {
     using (var callbackLock = new CallbackLock(lockObject))
     {
         TryFindAndStartKinect(callbackLock);
     }
 }
        /// <summary>
        /// Gives up the current sensor if it has one.  Stops watching for new sensors.
        /// </summary>
        public void Stop()
        {
            if (isStarted)
            {
                using (var callbackLock = new CallbackLock(lockObject))
                {
                    // Check again in case someone stopped us while
                    // we were waiting on the lock.
                    if (isStarted)
                    {
                        isStarted = false;

                        KinectSensor.KinectSensors.StatusChanged -= KinectSensorsOnStatusChanged;

                        SetSensorAndStatus(callbackLock, null, ChooserStatus.None);
                    }
                }
            }
        }
        /// <summary>
        /// Starts choosing a sensor.  In the typical case, a sensor will
        /// be found and events will be fired before this function returns.
        /// </summary>
        public void Start()
        {
            if (!isStarted)
            {
                using (var callbackLock = new CallbackLock(lockObject))
                {
                    // Check again in case someone else started while
                    // we were waiting on the lock.
                    if (!isStarted)
                    {
                        isStarted = true;

                        KinectSensor.KinectSensors.StatusChanged += KinectSensorsOnStatusChanged;

                        TryFindAndStartKinect(callbackLock);
                    }
                }
            }
        }
        /// <summary>
        /// Called when we don't have a sensor or possibly have the wrong sensor
        /// and we want to see if we can get one.
        /// </summary>
        private void TryFindAndStartKinect(CallbackLock callbackLock)
        {
            if (!isStarted)
            {
                // We aren't started so we don't need to be finding anything.
                Debug.Assert(Status == ChooserStatus.None, "isStarted and Status out of sync");
                return;
            }

            if (Kinect != null && Kinect.Status == KinectStatus.Connected)
            {
                if (requiredConnectionId == null)
                {
                    // we already have an appropriate sensor
                    Debug.Assert(Status == ChooserStatus.SensorStarted, "Chooser in unexpected state");
                    return;
                }

                if (Kinect.DeviceConnectionId == requiredConnectionId)
                {
                    // we already have the requested sensor
                    Debug.Assert(Status == ChooserStatus.SensorStarted, "Chooser in unexpected state");
                    return;
                }
            }

            KinectSensor  newSensor = null;
            ChooserStatus newStatus = 0;

            if (KinectSensor.KinectSensors.Count == 0)
            {
                newStatus = ChooserStatus.NoAvailableSensors;
            }
            else
            {
                foreach (KinectSensor sensor in KinectSensor.KinectSensors)
                {
                    if (requiredConnectionId != null && sensor.DeviceConnectionId != requiredConnectionId)
                    {
                        // client has set a required connection Id and this
                        // sensor does not have that Id
                        newStatus |= ChooserStatus.NoAvailableSensors;
                        continue;
                    }

                    if (sensor.Status != KinectStatus.Connected)
                    {
                        // Sensor is in some unusable state
                        newStatus |= GetErrorStatusFromSensor(sensor);
                        continue;
                    }

                    if (sensor.IsRunning)
                    {
                        // Sensor is already in use by this application
                        newStatus |= ChooserStatus.NoAvailableSensors;
                        continue;
                    }

                    // There is a potentially available sensor, try to start it
                    try
                    {
                        sensor.Start();
                    }
                    catch (IOException)
                    {
                        // some other app has this sensor.
                        newStatus |= ChooserStatus.SensorConflict;
                        continue;
                    }
                    catch (InvalidOperationException)
                    {
                        // TODO: In multi-proc scenarios, this is getting thrown at the start before we see IOException.  Need to understand.
                        // some other app has this sensor.
                        newStatus |= ChooserStatus.SensorConflict;
                        continue;
                    }

                    // Woo hoo, we have a started sensor.
                    newStatus = ChooserStatus.SensorStarted;
                    newSensor = sensor;
                    break;
                }
            }

            SetSensorAndStatus(callbackLock, newSensor, newStatus);
        }
 /// <summary>
 /// Resets all state to the initial state, with no users remembered as engaged or tracked.
 /// </summary>
 public void Reset()
 {
     using (var callbackLock = new CallbackLock(this.lockObject))
     {
         this.activityMeter.Clear();
         this.TrackedUserTrackingIds.Clear();
         this.EngagedUserTrackingId = SharedConstants.InvalidUserTrackingId;
         this.SetPrimaryUserTrackingId(SharedConstants.InvalidUserTrackingId, callbackLock);
         this.UpdateUserStates(callbackLock);
     }
 }
        /// <summary>
        /// Update user states exposed to clients, if necessary.
        /// </summary>
        /// <param name="callbackLock">
        /// Lock used to delay all events until after we exit lock section.
        /// </param>
        private void UpdateUserStates(CallbackLock callbackLock)
        {
            this.userStatesAccumulator.Clear();

            // Add states for tracked users
            foreach (var trackingId in this.TrackedUserTrackingIds)
            {
                this.userStatesAccumulator.Add(trackingId, TrackedStateName);
            }

            if (this.EngagedUserTrackingId != SharedConstants.InvalidUserTrackingId)
            {
                // Engaged state supersedes all other states
                this.userStatesAccumulator[this.EngagedUserTrackingId] = EngagedStateName;
            }

            if (this.HaveUserStatesChanged())
            {
                var temporaryMap = this.publicUserStates;
                this.publicUserStates = this.userStatesAccumulator;
                this.userStatesAccumulator = temporaryMap;

                var userStatesToSend = GetStateMappingEntryArray(this.publicUserStates);

                callbackLock.LockExit +=
                    () =>
                    this.SendUserStateChanged(
                        new UserStatesChangedEventMessage
                        {
                            category = EventCategory,
                            eventType = UserStatesChangedEventType,
                            userStates = userStatesToSend
                        });
            }
        }
        /// <summary>
        /// Update the primary user being tracked.
        /// </summary>
        /// <param name="candidateUserInfo">
        /// User information collection from which we will choose a primary user.
        /// </param>
        /// <param name="callbackLock">
        /// Lock used to delay all events until after we exit lock section.
        /// </param>
        private void UpdatePrimaryUser(IEnumerable<UserInfo> candidateUserInfo, CallbackLock callbackLock)
        {
            int firstPrimaryUserCandidate = SharedConstants.InvalidUserTrackingId;
            bool currentPrimaryUserStillPrimary = false;
            bool engagedUserIsPrimary = false;

            var trackingIdsAvailable = new HashSet<int>();

            foreach (var userInfo in candidateUserInfo)
            {
                if (userInfo.SkeletonTrackingId == SharedConstants.InvalidUserTrackingId)
                {
                    continue;
                }

                trackingIdsAvailable.Add(userInfo.SkeletonTrackingId);

                foreach (var handPointer in userInfo.HandPointers)
                {
                    if (handPointer.IsPrimaryForUser)
                    {
                        if (this.PrimaryUserTrackingId == userInfo.SkeletonTrackingId)
                        {
                            // If the current primary user still has an active hand, we should continue to consider them the primary user.
                            currentPrimaryUserStillPrimary = true;
                        }
                        else if (SharedConstants.InvalidUserTrackingId == firstPrimaryUserCandidate)
                        {
                            // Else if this is the first user with an active hand, they are the alternative candidate for primary user.
                            firstPrimaryUserCandidate = userInfo.SkeletonTrackingId;
                        }

                        if (this.EngagedUserTrackingId == userInfo.SkeletonTrackingId)
                        {
                            engagedUserIsPrimary = true;
                        }
                    }
                }
            }

            // If engaged user has a primary hand, always pick that user as primary user.
            // If current primary user still has a primary hand, let them remain primary.
            // Otherwise default to first primary user candidate seen.
            int primaryUserTrackingId = engagedUserIsPrimary
                                            ? this.EngagedUserTrackingId
                                            : (currentPrimaryUserStillPrimary ? this.PrimaryUserTrackingId : firstPrimaryUserCandidate);
            this.SetPrimaryUserTrackingId(primaryUserTrackingId, callbackLock);
        }
        internal void SetPrimaryUserTrackingId(int newId, CallbackLock callbackLock)
        {
            int oldId = this.PrimaryUserTrackingId;
            this.PrimaryUserTrackingId = newId;

            if (oldId != newId)
            {
                callbackLock.LockExit +=
                    () =>
                    this.SendUserStateChanged(
                        new UserTrackingIdChangedEventMessage
                        {
                            category = EventCategory,
                            eventType = PrimaryUserChangedEventType,
                            oldValue = oldId,
                            newValue = newId
                        });
            }
        }
        /// <summary>
        /// Called whenever the set of tracked users has changed.
        /// </summary>
        /// <param name="trackedUserInfo">
        /// User information from which we'll update the set of tracked users and the primary user.
        /// </param>
        /// <param name="timestamp">
        /// Interaction frame timestamp corresponding to given user information.
        /// </param>
        public void UpdateUserInformation(IEnumerable<UserInfo> trackedUserInfo, long timestamp)
        {
            bool foundEngagedUser = false;
            int firstTrackedUser = SharedConstants.InvalidUserTrackingId;

            using (var callbackLock = new CallbackLock(this.lockObject))
            {
                this.previousTrackedUserTrackingIds.Clear();
                var nextTrackedIds = this.previousTrackedUserTrackingIds;
                this.previousTrackedUserTrackingIds = this.TrackedUserTrackingIds;
                this.TrackedUserTrackingIds = nextTrackedIds;

                var trackedUserInfoArray = trackedUserInfo as UserInfo[] ?? trackedUserInfo.ToArray();

                foreach (var userInfo in trackedUserInfoArray)
                {
                    if (userInfo.SkeletonTrackingId == SharedConstants.InvalidUserTrackingId)
                    {
                        continue;
                    }

                    if (this.EngagedUserTrackingId == userInfo.SkeletonTrackingId)
                    {
                        this.TrackedUserTrackingIds.Add(userInfo.SkeletonTrackingId);

                        foundEngagedUser = true;
                    }
                    else if (HasTrackedHands(userInfo)
                             && (this.previousTrackedUserTrackingIds.Contains(userInfo.SkeletonTrackingId)
                                 || this.IsInactive(userInfo, timestamp)))
                    {
                        // Keep track of the non-engaged users we find that have at least one
                        // tracked hand pointer and also either (1) were previously tracked or
                        // (2) are not moving too much
                        this.TrackedUserTrackingIds.Add(userInfo.SkeletonTrackingId);

                        if (firstTrackedUser == SharedConstants.InvalidUserTrackingId)
                        {
                            // Consider the first non-engaged, stationary user as a candidate for engagement
                            firstTrackedUser = userInfo.SkeletonTrackingId;
                        }
                    }
                }

                // If engaged user was not found in list of candidate users, engaged user has become invalid.
                if (!foundEngagedUser)
                {
                    this.EngagedUserTrackingId = SharedConstants.InvalidUserTrackingId;
                }

                // Decide who should be the primary user, if anyone
                this.UpdatePrimaryUser(trackedUserInfoArray, callbackLock);

                // If there's a primary user, it is the preferred candidate for engagement.
                // Otherwise, the first tracked user seen is the preferred candidate.
                int candidateUserTrackingId = (this.PrimaryUserTrackingId != SharedConstants.InvalidUserTrackingId)
                                                  ? this.PrimaryUserTrackingId
                                                  : firstTrackedUser;

                // If there is a valid candidate user that is not already the engaged user
                if ((candidateUserTrackingId != SharedConstants.InvalidUserTrackingId)
                    && (candidateUserTrackingId != this.EngagedUserTrackingId))
                {
                    // If there is currently no engaged user, or if candidate user is the
                    // primary user controlling interactions while the currently engaged user
                    // is not interacting
                    if ((this.EngagedUserTrackingId == SharedConstants.InvalidUserTrackingId)
                        || (candidateUserTrackingId == this.PrimaryUserTrackingId))
                    {
                        this.PromoteCandidateToEngaged(candidateUserTrackingId);
                    }
                }

                // Update user states as the very last action, to include results from updates
                // performed so far
                this.UpdateUserStates(callbackLock);
            }
        }
        /// <summary>
        /// Helper to update our external status.
        /// </summary>
        /// <param name="callbackLock">Used to delay notifications to the client until after we exit the lock.</param>
        /// <param name="newKinect">The new sensor</param>
        /// <param name="newChooserStatus">The status we want to report to clients</param>
        private void SetSensorAndStatus(CallbackLock callbackLock, KinectSensor newKinect, ChooserStatus newChooserStatus)
        {
            KinectSensor oldKinect = Kinect;
            if (oldKinect != newKinect)
            {
                if (oldKinect != null)
                {
                    oldKinect.Stop();
                }

                Kinect = newKinect;

                callbackLock.LockExit += () => this.kinectChangedContextWrapper.Invoke(this, new KinectChangedEventArgs(oldKinect, newKinect));
                callbackLock.LockExit += () => RaisePropertyChanged("Kinect");
            }

            if (Status != newChooserStatus)
            {
                Status = newChooserStatus;

                callbackLock.LockExit += () => RaisePropertyChanged("Status");
            }
        }
        /// <summary>
        /// Called when we don't have a sensor or possibly have the wrong sensor
        /// and we want to see if we can get one.
        /// </summary>
        private void TryFindAndStartKinect(CallbackLock callbackLock)
        {
            if (!isStarted)
            {
                // We aren't started so we don't need to be finding anything.
                Debug.Assert(Status == ChooserStatus.None, "isStarted and Status out of sync");
                return;
            }

            if (Kinect != null && Kinect.Status == KinectStatus.Connected)
            {
                if (requiredConnectionId == null)
                {
                    // we already have an appropriate sensor
                    Debug.Assert(Status == ChooserStatus.SensorStarted, "Chooser in unexpected state");
                    return;
                }

                if (Kinect.DeviceConnectionId == requiredConnectionId)
                {
                    // we already have the requested sensor
                    Debug.Assert(Status == ChooserStatus.SensorStarted, "Chooser in unexpected state");
                    return;
                }
            }

            KinectSensor newSensor = null;
            ChooserStatus newStatus = 0;

            if (KinectSensor.KinectSensors.Count == 0)
            {
                newStatus = ChooserStatus.NoAvailableSensors;
            }
            else
            {
                foreach (KinectSensor sensor in KinectSensor.KinectSensors)
                {
                    if (requiredConnectionId != null && sensor.DeviceConnectionId != requiredConnectionId)
                    {
                        // client has set a required connection Id and this
                        // sensor does not have that Id
                        newStatus |= ChooserStatus.NoAvailableSensors;
                        continue;
                    }

                    if (sensor.Status != KinectStatus.Connected)
                    {
                        // Sensor is in some unusable state
                        newStatus |= GetErrorStatusFromSensor(sensor);
                        continue;
                    }

                    if (sensor.IsRunning)
                    {
                        // Sensor is already in use by this application
                        newStatus |= ChooserStatus.NoAvailableSensors;
                        continue;
                    }

                    // There is a potentially available sensor, try to start it
                    try
                    {
                        sensor.Start();
                    }
                    catch (IOException)
                    {
                        // some other app has this sensor.
                        newStatus |= ChooserStatus.SensorConflict;
                        continue;
                    }
                    catch (InvalidOperationException)
                    {
                        // TODO: In multi-proc scenarios, this is getting thrown at the start before we see IOException.  Need to understand.
                        // some other app has this sensor.
                        newStatus |= ChooserStatus.SensorConflict;
                        continue;
                    }

                    // Woo hoo, we have a started sensor.
                    newStatus = ChooserStatus.SensorStarted;
                    newSensor = sensor;
                    break;
                }
            }

            SetSensorAndStatus(callbackLock, newSensor, newStatus);
        }
        /// <summary>
        /// Promote candidate user to be the engaged user.
        /// </summary>
        /// <param name="candidateTrackingId">
        /// Tracking Id of user to be promoted to engaged user.
        /// If tracking Id does not match the Id of one of the currently tracked users,
        /// no action is taken.
        /// </param>
        /// <returns>
        /// True if specified candidate could be confirmed as the new engaged user,
        /// false otherwise.
        /// </returns>
        public bool PromoteCandidateToEngaged(int candidateTrackingId)
        {
            bool isConfirmed = false;

            if ((candidateTrackingId != SharedConstants.InvalidUserTrackingId) && this.TrackedUserTrackingIds.Contains(candidateTrackingId))
            {
                using (var callbackLock = new CallbackLock(this.lockObject))
                {
                    this.EngagedUserTrackingId = candidateTrackingId;
                    this.UpdateUserStates(callbackLock);
                }

                isConfirmed = true;
            }

            return isConfirmed;
        }