/// <summary> /// Requests that the <see cref="SensingAgent"/> consider beginning sensing control. /// </summary> /// <returns>A <see cref="ControlCompletionCheck"/> to be configured upon return, or <c>null</c> for no such check.</returns> /// <param name="cancellationToken">Cancellation token.</param> public async Task <ControlCompletionCheck> ActAsync(CancellationToken cancellationToken) { try { ControlCompletionCheck controlCompletionCheck = null; if (await TransitionToNewStateAsync(SensingAgentState.ActiveObservation, true, cancellationToken)) { // observe data for the specified duration. the current method is run as a scheduled callback, so we're guaranteed // to have some amount of background time. but watch out for background time expiration on iOS by monitoring the // passed cancellation token. an exception will be thrown if it expires, and it will be caught to return the agent // to idle. SensusServiceHelper.Logger.Log("Sensing agent " + Id + " is actively observing data for " + ActiveObservationDuration.Value + ".", LoggingLevel.Normal, GetType()); await Task.Delay(ActiveObservationDuration.Value, cancellationToken); // check criterion and begin control if warranted if (ObservedDataMeetControlCriterion()) { controlCompletionCheck = await BeginControlAsync(SensingAgentState.ActiveControl, cancellationToken); } else { await TransitionToNewStateAsync(SensingAgentState.Idle, true, cancellationToken); } } return(controlCompletionCheck); } catch (Exception ex) { await ReturnToIdle(cancellationToken); throw ex; } }
private async Task <ControlCompletionCheck> BeginControlAsync(SensingAgentState controlState, CancellationToken cancellationToken) { // this is a convenience method for beginning both active and opportunistic control. control state must be one of these two. if (controlState != SensingAgentState.ActiveControl && controlState != SensingAgentState.OpportunisticControl) { throw new Exception("Unrecognized control state: " + controlState); } ControlCompletionCheck controlCompletionCheck = null; if (await TransitionToNewStateAsync(controlState, true, cancellationToken)) { controlCompletionCheck = new ControlCompletionCheck(async controlCompletionCheckCancellationToken => { // the current check is called when a protocol is shutting down, and periodically while the // protocol remains running. as long as the protocol is running and the observed data meet // the control criterion, continue with sensing control; otherwise, end control and return // to idle. if (Protocol.State == ProtocolState.Running && ObservedDataMeetControlCriterion()) { SensusServiceHelper.Logger.Log("Continuing sensing control in state: " + StateDescription, LoggingLevel.Normal, GetType()); } else { await ReturnToIdle(controlCompletionCheckCancellationToken); } return(State); }, ControlCompletionCheckInterval, "Sensus would like to measure your environment. Please open this notification.", "Measuring environment. You may close this alert."); SensusServiceHelper.Logger.Log("Established sensing control in state: " + StateDescription, LoggingLevel.Normal, GetType()); } return(controlCompletionCheck); }
/// <summary> /// Asks the agent to observe an <see cref="IDatum"/> object that was generated by Sensus, either during /// <see cref="SensingAgentState.OpportunisticObservation"/> or during <see cref="SensingAgentState.ActiveObservation"/>. /// </summary> /// <returns>A <see cref="ControlCompletionCheck"/> to be configured upon return, or <c>null</c> for no such check.</returns> /// <param name="datum">Datum.</param> /// <param name="cancellationToken">Cancellation token.</param> public async Task <ControlCompletionCheck> ObserveAsync(IDatum datum, CancellationToken cancellationToken) { // certain probes (e.g., ios activity polling) return a null datum to signal that polling occurred // but no data were returned. ignore any such null readings when observing. if (datum == null) { return(null); } // accumulate observed data by type for later analysis lock (_typeData) { Type datumType = datum.GetType(); if (!_typeData.TryGetValue(datumType, out List <IDatum> data)) { data = new List <IDatum>(); _typeData.Add(datumType, data); } data.Add(datum); UpdateObservedData(_typeData); } // run opportunistic observation and control if warranted ControlCompletionCheck opportunisticControlCompletionCheck = null; try { // the current method is called at high rates during normal operation (e.g., when observing the // accelerometer). we want to avoid flooding the local data store with an agent-state datum for // each call, so don't write them here. if (await TransitionToNewStateAsync(SensingAgentState.OpportunisticObservation, false, cancellationToken)) { if (ObservedDataMeetControlCriterion()) { opportunisticControlCompletionCheck = await BeginControlAsync(SensingAgentState.OpportunisticControl, cancellationToken); } else { await TransitionToNewStateAsync(SensingAgentState.Idle, false, cancellationToken); } } } catch (Exception ex) { await ReturnToIdle(cancellationToken); throw ex; } return(opportunisticControlCompletionCheck); }