Ejemplo n.º 1
0
        /// <summary>
        /// Run the specified script.
        /// </summary>
        /// <param name="script">Script.</param>
        /// <param name="previousDatum">Previous datum.</param>
        /// <param name="currentDatum">Current datum.</param>
        private void Run(Script script, Datum previousDatum = null, Datum currentDatum = null)
        {
            SensusServiceHelper.Get().Logger.Log($"Running \"{Name}\".", LoggingLevel.Normal, GetType());

            script.RunTime = DateTimeOffset.UtcNow;

            // scheduled scripts have their expiration dates set when they're scheduled. scripts triggered by other probes
            // as well as on-start scripts will not yet have their expiration dates set. so check the script we've been
            // given and set the expiration date if needed. triggered scripts don't have windows, so the only expiration
            // condition comes from the maximum age.
            if (script.ExpirationDate == null && _maxAge.HasValue)
            {
                script.ExpirationDate = script.Birthdate + _maxAge.Value;
            }

            // script could have already expired (e.g., if user took too long to open notification).
            if (script.ExpirationDate.HasValue && script.ExpirationDate.Value < DateTime.Now)
            {
                SensusServiceHelper.Get().Logger.Log("Script expired before it was run.", LoggingLevel.Normal, GetType());
                return;
            }

            // do not run a one-shot script if it has already been run
            if (OneShot && RunTimes.Count > 0)
            {
                SensusServiceHelper.Get().Logger.Log("Not running one-shot script multiple times.", LoggingLevel.Normal, GetType());
                return;
            }

            lock (RunTimes)
            {
                // track participation by recording the current time. use this instead of the script's run timestamp, since
                // the latter is the time of notification on ios rather than the time that the user actually viewed the script.
                RunTimes.Add(DateTime.Now);
                RunTimes.RemoveAll(r => r < Probe.Protocol.ParticipationHorizon);
            }

            #region submit a separate datum indicating each time the script was run.
            Task.Run(async() =>
            {
                // geotag the script-run datum if any of the input groups are also geotagged. if none of the groups are geotagged, then
                // it wouldn't make sense to gather location data from a user.
                double?latitude  = null;
                double?longitude = null;
                DateTimeOffset?locationTimestamp = null;
                if (script.InputGroups.Any(inputGroup => inputGroup.Geotag))
                {
                    try
                    {
                        Position currentPosition = GpsReceiver.Get().GetReading(new CancellationToken(), false);

                        if (currentPosition == null)
                        {
                            throw new Exception("GPS receiver returned null position.");
                        }

                        latitude          = currentPosition.Latitude;
                        longitude         = currentPosition.Longitude;
                        locationTimestamp = currentPosition.Timestamp;
                    }
                    catch (Exception ex)
                    {
                        SensusServiceHelper.Get().Logger.Log("Failed to get position for script-run datum:  " + ex.Message, LoggingLevel.Normal, GetType());
                    }
                }

                await Probe.StoreDatumAsync(new ScriptRunDatum(script.RunTime.Value, Script.Id, Name, script.Id, script.ScheduledRunTime, script.CurrentDatum?.Id, latitude, longitude, locationTimestamp), default(CancellationToken));
            });
            #endregion

            // this method can be called with previous / current datum values (e.g., when the script is first triggered). it
            // can also be called without previous / current datum values (e.g., when triggering on a schedule). if
            // we have such values, set them on the script.

            if (previousDatum != null)
            {
                script.PreviousDatum = previousDatum;
            }

            if (currentDatum != null)
            {
                script.CurrentDatum = currentDatum;
            }

            SensusServiceHelper.Get().AddScriptToRun(script, RunMode);
        }
Ejemplo n.º 2
0
        public async Task RunAsync(Script script, Datum previousDatum = null, Datum currentDatum = null)
        {
            SensusServiceHelper.Get().Logger.Log($"Running \"{Name}\".", LoggingLevel.Normal, GetType());

            script.RunTime = DateTimeOffset.UtcNow;

            // this method can be called with previous / current datum values (e.g., when the script is first triggered). it
            // can also be called without previous / current datum values (e.g., when triggering on a schedule). if
            // we have such values, set them on the script.

            if (previousDatum != null)
            {
                script.PreviousDatum = previousDatum;
            }

            if (currentDatum != null)
            {
                script.CurrentDatum = currentDatum;
            }

            // scheduled scripts have their expiration dates set when they're scheduled. scripts triggered by other probes
            // as well as on-start scripts will not yet have their expiration dates set. so check the script we've been
            // given and set the expiration date if needed. triggered scripts don't have windows, so the only expiration
            // condition comes from the maximum age.
            if (script.ExpirationDate == null && _maxAge.HasValue)
            {
                script.ExpirationDate = script.Birthdate + _maxAge.Value;
            }

            // script could have already expired (e.g., if user took too long to open notification).
            if (script.ExpirationDate.HasValue && script.ExpirationDate.Value < DateTime.Now)
            {
                SensusServiceHelper.Get().Logger.Log("Script expired before it was run.", LoggingLevel.Normal, GetType());
                return;
            }

            // do not run a one-shot script if it has already been run
            if (OneShot && RunTimes.Count > 0)
            {
                SensusServiceHelper.Get().Logger.Log("Not running one-shot script multiple times.", LoggingLevel.Normal, GetType());
                return;
            }

            // check with the survey agent if there is one
            if (Probe.Agent != null)
            {
                Tuple <bool, DateTimeOffset?> deliverFutureTime = await Probe.Agent.DeliverSurveyNowAsync(script);

                if (deliverFutureTime.Item1)
                {
                    Probe.Protocol.LocalDataStore.WriteDatum(new ScriptStateDatum(ScriptState.AgentAccepted, script.RunTime.Value, script), CancellationToken.None);
                }
                else
                {
                    if (deliverFutureTime.Item2 == null)
                    {
                        SensusServiceHelper.Get().Logger.Log("Agent has declined survey without deferral.", LoggingLevel.Normal, GetType());

                        Probe.Protocol.LocalDataStore.WriteDatum(new ScriptStateDatum(ScriptState.AgentDeclined, script.RunTime.Value, script), CancellationToken.None);
                    }
                    else if (deliverFutureTime.Item2.Value > DateTimeOffset.UtcNow)
                    {
                        SensusServiceHelper.Get().Logger.Log("Agent has deferred survey until:  " + deliverFutureTime.Item2.Value, LoggingLevel.Normal, GetType());

                        Probe.Protocol.LocalDataStore.WriteDatum(new ScriptStateDatum(ScriptState.AgentDeferred, script.RunTime.Value, script), CancellationToken.None);

                        // check whether we need to expire the rescheduled script at some future point
                        DateTime?expiration = null;
                        DateTime trigger    = deliverFutureTime.Item2.Value.LocalDateTime;
                        if (_maxAge.HasValue)
                        {
                            expiration = trigger + _maxAge.Value;
                        }

                        // there is no window, so just add a descriptive, unique descriptor in place of the window
                        ScriptTriggerTime triggerTime = new ScriptTriggerTime(trigger, expiration, "DEFERRED-" + Guid.NewGuid());

                        // schedule the trigger. since this is a deferral, use the same script identifier that we currently have. this
                        // will maintain consistency and interpretability of the ScriptStateDatum objects that are recording the progression
                        // of scripts. this will also let survey agents better interpret what's going on with deferrals. this identifier is
                        // used as the RunId in the various tracked data types.
                        await ScheduleScriptRunAsync(triggerTime, script.Id);
                    }
                    else
                    {
                        SensusServiceHelper.Get().Logger.Log("Warning:  Agent has deferred survey to a time in the past:  " + deliverFutureTime.Item2.Value, LoggingLevel.Normal, GetType());
                    }

                    // do not proceed. the calling method (if scheduler-based) will take care of removing the current script.
                    return;
                }
            }

            lock (RunTimes)
            {
                // track participation by recording the current time. use this instead of the script's run timestamp, since
                // the latter is the time of notification on ios rather than the time that the user actually viewed the script.
                RunTimes.Add(DateTime.Now);
                RunTimes.RemoveAll(r => r < Probe.Protocol.ParticipationHorizon);
            }

            await SensusServiceHelper.Get().AddScriptAsync(script, RunMode);

            // let the script agent know and store a datum to record the event
            await(Probe.Agent?.ObserveAsync(script, ScriptState.Delivered) ?? Task.CompletedTask);
            Probe.Protocol.LocalDataStore.WriteDatum(new ScriptStateDatum(ScriptState.Delivered, script.RunTime.Value, script), CancellationToken.None);
        }