/// <summary> /// This sample demonstrates querying for raw events, series and aggregate series data from a Time Series Insights environment. /// </summary> /// <remarks> /// The Query APIs make use Time Series Expressions (TSX) to build filters, value and aggregation expressions. Visit /// <see href="https://docs.microsoft.com/rest/api/time-series-insights/reference-time-series-expression-syntax"/> to learn more about TSX. /// </remarks> public async Task RunSamplesAsync(TimeSeriesInsightsClient client, DeviceClient deviceClient) { PrintHeader("TIME SERIES INSIGHTS QUERY SAMPLE"); // Figure out what keys make up the Time Series Id TimeSeriesInsightsModelSettings modelSettingsClient = client.GetModelSettingsClient(); TimeSeriesModelSettings modelSettings = await modelSettingsClient.GetAsync(); TimeSeriesId tsId = TimeSeriesIdHelper.CreateTimeSeriesId(modelSettings); TimeSeriesInsightsQueries queriesClient = client.GetQueriesClient(); // In order to query for data, let's first send events to the IoT Hub await SendEventsToIotHubAsync(deviceClient, tsId, modelSettings.TimeSeriesIdProperties.ToArray()); // Sleeping for a few seconds to allow data to fully propagate in the Time Series Insights service Thread.Sleep(TimeSpan.FromSeconds(3)); await RunQueryEventsSample(queriesClient, tsId); await RunQueryAggregateSeriesSample(queriesClient, tsId); await RunQuerySeriesSampleWithInlineVariables(queriesClient, tsId); await RunQuerySeriesSampleWithPreDefinedVariables(client, tsId); }
private static async Task SendEventsToIotHubAsync( DeviceClient deviceClient, TimeSeriesId tsId, TimeSeriesIdProperty[] timeSeriesIdProperties) { IDictionary <string, object> messageBase = BuildMessageBase(timeSeriesIdProperties, tsId); double minTemperature = 20; double minHumidity = 60; var rand = new Random(); Console.WriteLine("\n\nSending temperature and humidity events to the IoT Hub.\n"); // Build the message base that is used as the base for every event going out for (int i = 0; i < 10; i++) { double currentTemperature = minTemperature + rand.NextDouble() * 15; double currentHumidity = minHumidity + rand.NextDouble() * 20; messageBase["Temperature"] = currentTemperature; messageBase["Humidity"] = currentHumidity; string messageBody = JsonSerializer.Serialize(messageBase); var message = new Microsoft.Azure.Devices.Client.Message(Encoding.ASCII.GetBytes(messageBody)) { ContentType = "application/json", ContentEncoding = "utf-8", }; await deviceClient.SendEventAsync(message); Console.WriteLine($"{DateTime.UtcNow} - Temperature: {currentTemperature}. " + $"Humidity: {currentHumidity}."); await Task.Delay(TimeSpan.FromSeconds(1)); } }
internal static async Task SendEventsToHubAsync( DeviceClient client, TimeSeriesId tsiId, TimeSeriesIdProperty[] timeSeriesIdProperties, int numberOfEventsToSend) { IDictionary<string, object> messageBase = BuildMessageBase(timeSeriesIdProperties, tsiId); double minTemperature = 20; double minHumidity = 60; var rand = new Random(); // Build the message base that is used as the base for every event going out for (int i = 0; i < numberOfEventsToSend; i++) { double currentTemperature = minTemperature + rand.NextDouble() * 15; double currentHumidity = minHumidity + rand.NextDouble() * 20; messageBase[Temperature] = currentTemperature; messageBase[Humidity] = currentHumidity; string messageBody = JsonSerializer.Serialize(messageBase); var message = new Message(Encoding.ASCII.GetBytes(messageBody)) { ContentType = "application/json", ContentEncoding = "utf-8", }; Func<Task> sendEventAct = async () => await client.SendEventAsync(message).ConfigureAwait(false); await sendEventAct.Should().NotThrowAsync(); } }
protected async Task <TimeSeriesId> GetUniqueTimeSeriesInstanceIdAsync(TimeSeriesInsightsInstances instancesClient, int numOfIdKeys) { numOfIdKeys.Should().BeInRange(1, 3); for (int tryNumber = 0; tryNumber < MaxTries; ++tryNumber) { var id = new List <string>(); for (int i = 0; i < numOfIdKeys; i++) { id.Add(Recording.GenerateAlphaNumericId(string.Empty)); } TimeSeriesId tsId = numOfIdKeys switch { 1 => new TimeSeriesId(id[0]), 2 => new TimeSeriesId(id[0], id[1]), 3 => new TimeSeriesId(id[0], id[1], id[2]), _ => throw new Exception($"Invalid number of Time Series Insights Id properties."), }; Response <InstancesOperationResult[]> getInstancesResult = await instancesClient .GetAsync(new List <TimeSeriesId> { tsId }) .ConfigureAwait(false); if (getInstancesResult.Value?.First()?.Error != null) { return(tsId); } } throw new Exception($"Unique Id could not be found"); }
public void TimeSeriesId_IdsNotEqual() { // Arrange var tsiId1 = new TimeSeriesId("B17", "F1"); var tsiId2 = new TimeSeriesId("B17", "F2"); tsiId1.Equals(tsiId2).Should().BeFalse(); }
public void TimeSeriesId_IdsWithNullEqual() { // Arrange var tsiId1 = new TimeSeriesId("B17", null, "R1"); var tsiId2 = new TimeSeriesId("B17", null, "R1"); tsiId1.Equals(tsiId2).Should().BeTrue(); }
/// <inheritdoc/> public void Register(Source source, Tag tag, TimeSeriesId timeSeriesId) { var timeSeriesMap = new TimeSeriesMap { Source = source, }; timeSeriesMap.TagToTimeSeriesId[tag] = timeSeriesId.ToProtobuf(); _timeSeriesMapIdentifierClient.Instance.RegisterAsync(timeSeriesMap); }
/// <summary> /// Register a mapping between <see cref="Source"/> and <see cref="Tag"/> to <see cref="TimeSeriesId"/>. /// </summary> /// <param name="source"><see cref="Source"/> to register for.</param> /// <param name="tag"><see cref="Tag"/> to register for.</param> /// <param name="timeSeriesId"><see cref="TimeSeriesId"/> to register for.</param> public void Register(Source source, Tag tag, TimeSeriesId timeSeriesId) { if (!_map.ContainsKey(source)) { _map[source] = new ConcurrentDictionary <Tag, TimeSeriesId>(); } var tagToTimeSeries = _map[source]; tagToTimeSeries[tag] = timeSeriesId; }
public void TimeSeriesId_ToArrayWith1StringKey() { // Arrange var key1 = "B17"; var tsiId = new TimeSeriesId(key1); // Act var idAsArray = tsiId.ToStringArray(); // Assert idAsArray.Should().Equal(new string[] { key1 }); }
/// <summary> /// This sample demonstrates querying for raw events, series and aggregate series data from a Time Series Insights environment. /// </summary> /// <remarks> /// The Query APIs make use Time Series Expressions (TSX) to build filters, value and aggregation expressions. Visit /// <see href="https://docs.microsoft.com/rest/api/time-series-insights/reference-time-series-expression-syntax"/> to learn more about TSX. /// </remarks> public async Task RunSamplesAsync(TimeSeriesInsightsClient client, TimeSeriesId tsId) { PrintHeader("TIME SERIES INSIGHTS QUERY SAMPLE"); TimeSeriesInsightsQueries queriesClient = client.GetQueriesClient(); await RunQueryEventsSample(queriesClient, tsId); await RunQueryAggregateSeriesSample(queriesClient, tsId); await RunQuerySeriesSampleWithInlineVariables(queriesClient, tsId); await RunQuerySeriesSampleWithPreDefinedVariables(client, tsId); }
/// <summary> /// Main entry point to the sample. /// </summary> public static async Task Main(string[] args) { // Parse and validate paramters Options options = null; ParserResult <Options> result = Parser.Default.ParseArguments <Options>(args) .WithParsed(parsedOptions => { options = parsedOptions; }) .WithNotParsed(errors => { Environment.Exit(1); }); // Instantiate the Time Series Insights client TimeSeriesInsightsClient tsiClient = GetTimeSeriesInsightsClient( options.TenantId, options.ClientId, options.ClientSecret, options.TsiEnvironmentFqdn); // Instantiate an IoT Hub device client client in order to send telemetry to the hub DeviceClient deviceClient = await GetDeviceClientAsync(options.IoTHubConnectionString).ConfigureAwait(false); // Figure out what keys make up the Time Series Id TimeSeriesInsightsModelSettings modelSettingsClient = tsiClient.GetModelSettingsClient(); TimeSeriesModelSettings modelSettings = await modelSettingsClient.GetAsync(); TimeSeriesId tsId = TimeSeriesIdHelper.CreateTimeSeriesId(modelSettings); // In order to query for data, let's first send events to the IoT Hub await SendEventsToIotHubAsync(deviceClient, tsId, modelSettings.TimeSeriesIdProperties.ToArray()); // Run the samples var tsiInstancesSamples = new InstancesSamples(); await tsiInstancesSamples.RunSamplesAsync(tsiClient); var tsiTypesSamples = new TypesSamples(); await tsiTypesSamples.RunSamplesAsync(tsiClient); var tsiHierarchiesSamples = new HierarchiesSamples(); await tsiHierarchiesSamples.RunSamplesAsync(tsiClient); var tsiModelSettingsSamples = new ModelSettingsSamples(); await tsiModelSettingsSamples.RunSamplesAsync(tsiClient); var querySamples = new QuerySamples(); await querySamples.RunSamplesAsync(tsiClient, tsId); }
public void TimeSeriesId_GetIdForStringKeys() { // Arrange var key1 = "B17"; var key2 = "F1"; var key3 = "R400"; var tsiId = new TimeSeriesId(key1, key2, key3); // Act var idAsString = tsiId.ToString(); // Assert idAsString.Should().Be($"[\"{key1}\",\"{key2}\",\"{key3}\"]"); }
public void TimeSeriesId_ToArrayWith3StringKeys() { // Arrange var key1 = "B17"; var key2 = "F1"; var key3 = "R1"; var tsiId = new TimeSeriesId(key1, key2, key3); // Act var idAsArray = tsiId.ToStringArray(); // Assert idAsArray.Should().Equal(new string[] { key1, key2, key3 }); }
internal static TimeSeriesId CreateTimeSeriesId(TimeSeriesModelSettings modelSettings) { int numOfIdKeys = modelSettings.TimeSeriesIdProperties.Count; // Create a Time Series Id where the number of keys that make up the Time Series Id is fetched from Model Settings var id = new List <string>(); for (int i = 0; i < numOfIdKeys; i++) { id.Add(Guid.NewGuid().ToString()); } TimeSeriesId tsId = numOfIdKeys switch { 1 => new TimeSeriesId(id[0]), 2 => new TimeSeriesId(id[0], id[1]), 3 => new TimeSeriesId(id[0], id[1], id[2]), _ => throw new Exception($"Invalid number of Time Series Insights Id properties."), }; return(tsId); }
public async Task TimeSeriesInsightsQuery_AggregateSeriesWithNumericVariable() { // Arrange TimeSeriesInsightsClient tsiClient = GetClient(); DeviceClient deviceClient = await GetDeviceClient().ConfigureAwait(false); // Figure out what the Time Series Id is composed of TimeSeriesModelSettings modelSettings = await tsiClient.ModelSettings.GetAsync().ConfigureAwait(false); // Create a Time Series Id where the number of keys that make up the Time Series Id is fetched from Model Settings TimeSeriesId tsiId = await GetUniqueTimeSeriesInstanceIdAsync(tsiClient, modelSettings.TimeSeriesIdProperties.Count) .ConfigureAwait(false); try { // Send some events to the IoT hub with with a second between each event await QueryTestsHelper.SendEventsToHubAsync( deviceClient, tsiId, modelSettings.TimeSeriesIdProperties.ToArray(), 30) .ConfigureAwait(false); DateTimeOffset now = Recording.UtcNow; DateTimeOffset endTime = now.AddMinutes(10); DateTimeOffset startTime = now.AddMinutes(-10); var temperatureNumericVariable = new NumericVariable( new TimeSeriesExpression($"$event.{QueryTestsHelper.Temperature}"), new TimeSeriesExpression("avg($value)")); var queryAggregateSeriesRequestOptions = new QueryAggregateSeriesRequestOptions(); queryAggregateSeriesRequestOptions.InlineVariables[QueryTestsHelper.Temperature] = temperatureNumericVariable; queryAggregateSeriesRequestOptions.ProjectedVariables.Add(QueryTestsHelper.Temperature); // This retry logic was added as the TSI instance are not immediately available after creation await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() => { QueryAnalyzer queryAggregateSeriesPages = tsiClient.Queries.CreateAggregateSeriesQueryAnalyzer( tsiId, startTime, endTime, TimeSpan.FromSeconds(5), queryAggregateSeriesRequestOptions); var nonNullFound = false; await foreach (Page <TimeSeriesPoint> aggregateSeriesPage in queryAggregateSeriesPages.GetResultsAsync().AsPages()) { foreach (TimeSeriesPoint point in aggregateSeriesPage.Values) { point.GetUniquePropertyNames().Should().HaveCount(1).And.Contain((property) => property == QueryTestsHelper.Temperature); var value = (double?)point.GetValue(QueryTestsHelper.Temperature); if (value != null) { nonNullFound = true; } } nonNullFound.Should().BeTrue(); } queryAggregateSeriesPages.Progress.Should().Be(100); return(null); }, MaxNumberOfRetries, s_retryDelay); // Add an interpolated variable var linearInterpolationNumericVariable = new NumericVariable( new TimeSeriesExpression($"$event.{QueryTestsHelper.Temperature}"), new TimeSeriesExpression("left($value)")); linearInterpolationNumericVariable.Interpolation = new InterpolationOperation { Kind = InterpolationKind.Linear, Boundary = new InterpolationBoundary() { Span = TimeSpan.FromSeconds(1), }, }; const string linearInterpolation = "linearInterpolation"; queryAggregateSeriesRequestOptions.InlineVariables[linearInterpolation] = linearInterpolationNumericVariable; queryAggregateSeriesRequestOptions.ProjectedVariables.Add(linearInterpolation); await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() => { QueryAnalyzer queryAggregateSeriesPages = tsiClient.Queries.CreateAggregateSeriesQueryAnalyzer( tsiId, startTime, endTime, TimeSpan.FromSeconds(5), queryAggregateSeriesRequestOptions); await foreach (Page <TimeSeriesPoint> aggregateSeriesPage in queryAggregateSeriesPages.GetResultsAsync().AsPages()) { aggregateSeriesPage.Values.Should().HaveCountGreaterThan(0); foreach (var point in aggregateSeriesPage.Values) { point.GetUniquePropertyNames().Should().HaveCount(2) .And .Contain((property) => property == QueryTestsHelper.Temperature) .And .Contain((property) => property == linearInterpolation); } } return(null); }, MaxNumberOfRetries, s_retryDelay); // Send 2 events with a special condition that can be used later to query on IDictionary <string, object> messageBase = QueryTestsHelper.BuildMessageBase(modelSettings.TimeSeriesIdProperties.ToArray(), tsiId); messageBase[QueryTestsHelper.Temperature] = 1.2; messageBase[QueryTestsHelper.Humidity] = 3.4; string messageBody = JsonSerializer.Serialize(messageBase); var message = new Message(Encoding.ASCII.GetBytes(messageBody)) { ContentType = "application/json", ContentEncoding = "utf-8", }; Func <Task> sendEventAct = async() => await deviceClient.SendEventAsync(message).ConfigureAwait(false); sendEventAct.Should().NotThrow(); // Send it again sendEventAct.Should().NotThrow(); // Query for the two events with a filter queryAggregateSeriesRequestOptions.Filter = "$event.Temperature.Double = 1.2"; await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() => { QueryAnalyzer queryAggregateSeriesPages = tsiClient.Queries.CreateAggregateSeriesQueryAnalyzer( tsiId, startTime, endTime, TimeSpan.FromSeconds(5), queryAggregateSeriesRequestOptions); var valueFound = false; await foreach (TimeSeriesPoint point in queryAggregateSeriesPages.GetResultsAsync()) { point.GetUniquePropertyNames().Should().HaveCount(2) .And .Contain((property) => property == QueryTestsHelper.Temperature) .And .Contain((property) => property == linearInterpolation); var value = (double?)point.GetValue(QueryTestsHelper.Temperature); if (value == 1.2) { valueFound = true; } } valueFound.Should().BeTrue(); return(null); }, MaxNumberOfRetries, s_retryDelay); } finally { deviceClient?.Dispose(); } }
public async Task TimeSeriesInsightsQuery_AggregateSeriesWithCategoricalVariable() { // Arrange TimeSeriesInsightsClient tsiClient = GetClient(); DeviceClient deviceClient = await GetDeviceClient().ConfigureAwait(false); // Figure out what the Time Series Id is composed of TimeSeriesModelSettings modelSettings = await tsiClient.ModelSettings.GetAsync().ConfigureAwait(false); // Create a Time Series Id where the number of keys that make up the Time Series Id is fetched from Model Settings TimeSeriesId tsiId = await GetUniqueTimeSeriesInstanceIdAsync(tsiClient, modelSettings.TimeSeriesIdProperties.Count) .ConfigureAwait(false); try { // Send some events to the IoT hub with with a second between each event await QueryTestsHelper.SendEventsToHubAsync( deviceClient, tsiId, modelSettings.TimeSeriesIdProperties.ToArray(), 30) .ConfigureAwait(false); DateTimeOffset now = Recording.UtcNow; DateTimeOffset endTime = now.AddMinutes(10); DateTimeOffset startTime = now.AddMinutes(-10); var queryAggregateSeriesRequestOptions = new QueryAggregateSeriesRequestOptions(); var categoricalVariable = new CategoricalVariable( new TimeSeriesExpression($"tolong($event.{QueryTestsHelper.Temperature}.Double)"), new TimeSeriesDefaultCategory("N/A")); categoricalVariable.Categories.Add(new TimeSeriesAggregateCategory("good", new List <object> { 1 })); queryAggregateSeriesRequestOptions.InlineVariables["categorical"] = categoricalVariable; await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() => { QueryAnalyzer queryAggregateSeriesPages = tsiClient.Queries.CreateAggregateSeriesQueryAnalyzer( tsiId, startTime, endTime, TimeSpan.FromSeconds(5), queryAggregateSeriesRequestOptions); await foreach (TimeSeriesPoint point in queryAggregateSeriesPages.GetResultsAsync()) { point.GetUniquePropertyNames().Should().HaveCount(3) .And .Contain((property) => property == "categorical[good]") .And .Contain((property) => property == "categorical[N/A]"); } return(null); }, MaxNumberOfRetries, s_retryDelay); } finally { deviceClient?.Dispose(); } }
public async Task TimeSeriesInsightsQuery_AggregateSeriesWithAggregateVariable() { // Arrange TimeSeriesInsightsClient tsiClient = GetClient(); DeviceClient deviceClient = await GetDeviceClient().ConfigureAwait(false); // Figure out what the Time Series Id is composed of TimeSeriesModelSettings modelSettings = await tsiClient.ModelSettings.GetAsync().ConfigureAwait(false); // Create a Time Series Id where the number of keys that make up the Time Series Id is fetched from Model Settings TimeSeriesId tsiId = await GetUniqueTimeSeriesInstanceIdAsync(tsiClient, modelSettings.TimeSeriesIdProperties.Count) .ConfigureAwait(false); try { // Send some events to the IoT hub with with a second between each event await QueryTestsHelper.SendEventsToHubAsync( deviceClient, tsiId, modelSettings.TimeSeriesIdProperties.ToArray(), 30) .ConfigureAwait(false); // Query for temperature events with two calculateions. First with the temperature value as is, and the second // with the temperature value multiplied by 2. DateTimeOffset now = Recording.UtcNow; DateTimeOffset endTime = now.AddMinutes(10); DateTimeOffset startTime = now.AddMinutes(-10); var aggregateVariable = new AggregateVariable( new TimeSeriesExpression("count()")); var queryAggregateSeriesRequestOptions = new QueryAggregateSeriesRequestOptions(); queryAggregateSeriesRequestOptions.InlineVariables["Count"] = aggregateVariable; queryAggregateSeriesRequestOptions.ProjectedVariables.Add("Count"); // This retry logic was added as the TSI instance are not immediately available after creation await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() => { QueryAnalyzer queryAggregateSeriesPages = tsiClient.Queries.CreateAggregateSeriesQueryAnalyzer( tsiId, startTime, endTime, TimeSpan.FromSeconds(5), queryAggregateSeriesRequestOptions); long?totalCount = 0; await foreach (TimeSeriesPoint point in queryAggregateSeriesPages.GetResultsAsync()) { var currentCount = (long?)point.GetValue("Count"); totalCount += currentCount; } totalCount.Should().Be(30); return(null); }, MaxNumberOfRetries, s_retryDelay); } finally { deviceClient?.Dispose(); } }
public async Task TimeSeriesId_CreateInstanceWith3Keys() { // Arrange TimeSeriesInsightsClient client = GetClient(); TimeSeriesInsightsInstances instancesClient = client.GetInstancesClient(); // Create a Time Series Id with 3 keys. Middle key is a null var idWithNull = new TimeSeriesId( Recording.GenerateAlphaNumericId(string.Empty, 5), null, Recording.GenerateAlphaNumericId(string.Empty, 5)); var timeSeriesInstances = new List <TimeSeriesInstance> { new TimeSeriesInstance(idWithNull, DefaultType), }; // Act and assert try { // Create TSI instances Response <TimeSeriesOperationError[]> createInstancesResult = await instancesClient .CreateOrReplaceAsync(timeSeriesInstances) .ConfigureAwait(false); // Assert that the result error array does not contain any object that is set createInstancesResult.Value.Should().OnlyContain((errorResult) => errorResult == null); // This retry logic was added as the TSI instance are not immediately available after creation await TestRetryHelper.RetryAsync <Response <InstancesOperationResult[]> >(async() => { // Get the instance with a null item in its Id Response <InstancesOperationResult[]> getInstanceWithNullInId = await instancesClient .GetByIdAsync(new List <TimeSeriesId> { idWithNull }) .ConfigureAwait(false); getInstanceWithNullInId.Value.Length.Should().Be(1); InstancesOperationResult resultItem = getInstanceWithNullInId.Value.First(); resultItem.Instance.Should().NotBeNull(); resultItem.Instance.TimeSeriesId.ToStringArray().Length.Should().Be(3); resultItem.Instance.TimeSeriesId.ToStringArray()[1].Should().BeNull(); return(null); }, MaxNumberOfRetries, s_retryDelay); } finally { // clean up try { Response <TimeSeriesOperationError[]> deleteInstancesResponse = await instancesClient .DeleteByIdAsync(timeSeriesInstances.Select((instance) => instance.TimeSeriesId)) .ConfigureAwait(false); // Assert that the response array does not have any error object set deleteInstancesResponse.Value.Should().OnlyContain((errorResult) => errorResult == null); } catch (Exception ex) { Console.WriteLine($"Test clean up failed: {ex.Message}"); throw; } } }
private async Task RunQuerySeriesSampleWithInlineVariables(TimeSeriesInsightsQueries queriesClient, TimeSeriesId tsId) { // Query for two series, one with the temperature values in Celsius and another in Fahrenheit #region Snippet:TimeSeriesInsightsSampleQuerySeriesWithInlineVariables Console.WriteLine("\n\nQuery for temperature series in Celsius and Fahrenheit over the past 10 minutes.\n"); var celsiusVariable = new NumericVariable( new TimeSeriesExpression("$event.Temperature"), new TimeSeriesExpression("avg($value)")); var fahrenheitVariable = new NumericVariable( new TimeSeriesExpression("$event.Temperature * 1.8 + 32"), new TimeSeriesExpression("avg($value)")); var querySeriesRequestOptions = new QuerySeriesRequestOptions(); querySeriesRequestOptions.InlineVariables["TemperatureInCelsius"] = celsiusVariable; querySeriesRequestOptions.InlineVariables["TemperatureInFahrenheit"] = fahrenheitVariable; QueryAnalyzer seriesQuery = queriesClient.CreateSeriesQuery( tsId, TimeSpan.FromMinutes(10), null, querySeriesRequestOptions); await foreach (TimeSeriesPoint point in seriesQuery.GetResultsAsync()) { double?tempInCelsius = (double?)point.GetValue("TemperatureInCelsius"); double?tempInFahrenheit = (double?)point.GetValue("TemperatureInFahrenheit"); Console.WriteLine($"{point.Timestamp} - Average temperature in Celsius: {tempInCelsius}. Average temperature in Fahrenheit: {tempInFahrenheit}."); } #endregion Snippet:TimeSeriesInsightsSampleQuerySeriesWithInlineVariables }
private static IDictionary <string, object> BuildMessageBase(TimeSeriesIdProperty[] timeSeriesIdProperties, TimeSeriesId tsiId) { var messageBase = new Dictionary <string, object>(); string[] tsiIdArray = tsiId.ToStringArray(); for (int i = 0; i < timeSeriesIdProperties.Count(); i++) { TimeSeriesIdProperty idProperty = timeSeriesIdProperties[i]; string tsiIdValue = tsiIdArray[i]; messageBase[idProperty.Name] = tsiIdValue; } return(messageBase); }
public async Task TimeSeriesInsightsQuery_GetSeriesLifecycle() { // Arrange TimeSeriesInsightsClient tsiClient = GetClient(); DeviceClient deviceClient = await GetDeviceClient().ConfigureAwait(false); // Figure out what the Time Series Id is composed of TimeSeriesModelSettings modelSettings = await tsiClient.ModelSettings.GetAsync().ConfigureAwait(false); // Create a Time Series Id where the number of keys that make up the Time Series Id is fetched from Model Settings TimeSeriesId tsiId = await GetUniqueTimeSeriesInstanceIdAsync(tsiClient, modelSettings.TimeSeriesIdProperties.Count) .ConfigureAwait(false); try { // Send some events to the IoT hub await QueryTestsHelper.SendEventsToHubAsync( deviceClient, tsiId, modelSettings.TimeSeriesIdProperties.ToArray(), 10) .ConfigureAwait(false); // Act // Query for temperature events with two calculateions. First with the temperature value as is, and the second // with the temperature value multiplied by 2. DateTimeOffset now = Recording.UtcNow; DateTimeOffset endTime = now.AddMinutes(10); DateTimeOffset startTime = now.AddMinutes(-10); var temperatureNumericVariable = new NumericVariable( new TimeSeriesExpression($"$event.{QueryTestsHelper.Temperature}"), new TimeSeriesExpression("avg($value)")); var temperatureNumericVariableTimesTwo = new NumericVariable( new TimeSeriesExpression($"$event.{QueryTestsHelper.Temperature} * 2"), new TimeSeriesExpression("avg($value)")); var temperatureTimesTwoVariableName = $"{QueryTestsHelper.Temperature}TimesTwo"; var querySeriesRequestOptions = new QuerySeriesRequestOptions(); querySeriesRequestOptions.InlineVariables[QueryTestsHelper.Temperature] = temperatureNumericVariable; querySeriesRequestOptions.InlineVariables[temperatureTimesTwoVariableName] = temperatureNumericVariableTimesTwo; // This retry logic was added as the TSI instance are not immediately available after creation await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() => { AsyncPageable <QueryResultPage> querySeriesEventsPages = tsiClient.Query.GetSeriesAsync( tsiId, startTime, endTime, querySeriesRequestOptions); await foreach (QueryResultPage seriesEventsPage in querySeriesEventsPages) { seriesEventsPage.Timestamps.Should().HaveCount(10); seriesEventsPage.Timestamps.Should().OnlyContain(timeStamp => timeStamp >= startTime) .And .OnlyContain(timeStamp => timeStamp <= endTime); seriesEventsPage.Properties.Count.Should().Be(3); // EventCount, Temperature and TemperatureTimesTwo seriesEventsPage.Properties.Should().Contain((property) => property.Name == QueryTestsHelper.Temperature) .And .Contain((property) => property.Name == temperatureTimesTwoVariableName); // Assert that the values for the Temperature property is equal to the values for the other property, multiplied by 2 var temperatureValues = seriesEventsPage .Properties .First((property) => property.Name == QueryTestsHelper.Temperature) .Values.Cast <double>().ToList(); var temperatureTimesTwoValues = seriesEventsPage .Properties .First((property) => property.Name == temperatureTimesTwoVariableName) .Values.Cast <double>().ToList(); temperatureTimesTwoValues.Should().Equal(temperatureValues.Select((property) => property * 2).ToList()); } return(null); }, MaxNumberOfRetries, s_retryDelay); // Query for all the series events using a timespan AsyncPageable <QueryResultPage> querySeriesEventsPagesWithTimespan = tsiClient.Query.GetSeriesAsync(tsiId, TimeSpan.FromMinutes(10), null, querySeriesRequestOptions); await foreach (QueryResultPage seriesEventsPage in querySeriesEventsPagesWithTimespan) { seriesEventsPage.Timestamps.Should().HaveCount(10); seriesEventsPage.Properties.Count.Should().Be(3); // EventCount, Temperature and TemperatureTimesTwo } // Query for temperature and humidity var humidityNumericVariable = new NumericVariable( new TimeSeriesExpression("$event.Humidity"), new TimeSeriesExpression("avg($value)")); querySeriesRequestOptions.InlineVariables[QueryTestsHelper.Humidity] = humidityNumericVariable; querySeriesRequestOptions.ProjectedVariables.Add(QueryTestsHelper.Temperature); querySeriesRequestOptions.ProjectedVariables.Add(QueryTestsHelper.Humidity); await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() => { AsyncPageable <QueryResultPage> querySeriesEventsPages = tsiClient.Query.GetSeriesAsync(tsiId, startTime, endTime, querySeriesRequestOptions); await foreach (QueryResultPage seriesEventsPage in querySeriesEventsPages) { seriesEventsPage.Timestamps.Should().HaveCount(10); seriesEventsPage.Timestamps.Should().OnlyContain(timeStamp => timeStamp >= startTime) .And .OnlyContain(timeStamp => timeStamp <= endTime); seriesEventsPage.Properties.Count.Should().Be(2); // Temperature and Humidity seriesEventsPage.Properties.Should().Contain((property) => property.Name == QueryTestsHelper.Temperature) .And .Contain((property) => property.Name == QueryTestsHelper.Humidity); } return(null); }, MaxNumberOfRetries, s_retryDelay); // Send 2 events with a special condition that can be used later to query on IDictionary <string, object> messageBase = QueryTestsHelper.BuildMessageBase(modelSettings.TimeSeriesIdProperties.ToArray(), tsiId); messageBase[QueryTestsHelper.Temperature] = 1.2; messageBase[QueryTestsHelper.Humidity] = 3.4; string messageBody = JsonSerializer.Serialize(messageBase); var message = new Message(Encoding.ASCII.GetBytes(messageBody)) { ContentType = "application/json", ContentEncoding = "utf-8", }; Func <Task> sendEventAct = async() => await deviceClient.SendEventAsync(message).ConfigureAwait(false); sendEventAct.Should().NotThrow(); // Send it again sendEventAct.Should().NotThrow(); // Query for the two events with a filter querySeriesRequestOptions.Filter = "$event.Temperature.Double = 1.2"; await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() => { AsyncPageable <QueryResultPage> querySeriesEventsPages = tsiClient.Query.GetSeriesAsync(tsiId, startTime, endTime, querySeriesRequestOptions); await foreach (QueryResultPage seriesEventsPage in querySeriesEventsPages) { seriesEventsPage.Timestamps.Should().HaveCount(2); seriesEventsPage.Properties.Should().HaveCount(2) .And .Contain((property) => property.Name == QueryTestsHelper.Temperature) .And .Contain((property) => property.Name == QueryTestsHelper.Humidity); var temperatureValues = seriesEventsPage .Properties .First((property) => property.Name == QueryTestsHelper.Temperature) .Values.Cast <double>().ToList(); temperatureValues.Should().AllBeEquivalentTo(1.2); } return(null); }, MaxNumberOfRetries, s_retryDelay); // Query for the two events with a filter, but only take 1 querySeriesRequestOptions.MaximumNumberOfEvents = 1; AsyncPageable <QueryResultPage> querySeriesEventsPagesWithFilter = tsiClient.Query.GetSeriesAsync(tsiId, startTime, endTime, querySeriesRequestOptions); await foreach (QueryResultPage seriesEventsPage in querySeriesEventsPagesWithFilter) { seriesEventsPage.Timestamps.Should().HaveCount(1); seriesEventsPage.Properties.Should().HaveCount(2) .And .Contain((property) => property.Name == QueryTestsHelper.Temperature) .And .Contain((property) => property.Name == QueryTestsHelper.Humidity); var temperatureValues = seriesEventsPage .Properties .First((property) => property.Name == QueryTestsHelper.Temperature) .Values.Cast <double>().ToList(); temperatureValues.Should().AllBeEquivalentTo(1.2); } } finally { deviceClient?.Dispose(); } }
public async Task TimeSeriesInsightsInstances_Lifecycle() { // Arrange TimeSeriesInsightsClient client = GetClient(); TimeSeriesInsightsModelSettings modelSettingsClient = client.GetModelSettingsClient(); TimeSeriesInsightsInstances instancesClient = client.GetInstancesClient(); int numOfInstancesToSetup = 2; var timeSeriesInstances = new List <TimeSeriesInstance>(); Response <TimeSeriesModelSettings> currentSettings = await modelSettingsClient.GetAsync().ConfigureAwait(false); string defaultTypeId = currentSettings.Value.DefaultTypeId; int numOfIdProperties = currentSettings.Value.TimeSeriesIdProperties.Count; for (int i = 0; i < numOfInstancesToSetup; i++) { TimeSeriesId id = await GetUniqueTimeSeriesInstanceIdAsync(instancesClient, numOfIdProperties) .ConfigureAwait(false); var instance = new TimeSeriesInstance(id, defaultTypeId) { Name = Recording.GenerateAlphaNumericId("instance"), }; timeSeriesInstances.Add(instance); } IEnumerable <TimeSeriesId> timeSeriesInstancesIds = timeSeriesInstances.Select((instance) => instance.TimeSeriesId); // Act and assert try { await TestRetryHelper.RetryAsync <Response <InstancesOperationResult[]> >((Func <Task <Response <InstancesOperationResult[]> > >)(async() => { // Create TSI instances Response <TimeSeriesOperationError[]> createInstancesResult = await instancesClient .CreateOrReplaceAsync(timeSeriesInstances) .ConfigureAwait(false); // Assert that the result error array does not contain any object that is set createInstancesResult.Value.Should().OnlyContain((errorResult) => errorResult == null); // Get the created instances by Ids Response <InstancesOperationResult[]> getInstancesByIdsResult = await instancesClient .GetByIdAsync(timeSeriesInstancesIds) .ConfigureAwait(false); getInstancesByIdsResult.Value.Length.Should().Be(timeSeriesInstances.Count); foreach (InstancesOperationResult instanceResult in getInstancesByIdsResult.Value) { instanceResult.Instance.Should().NotBeNull(); instanceResult.Error.Should().BeNull(); instanceResult.Instance.TimeSeriesId.ToStringArray().Length.Should().Be(numOfIdProperties); AssertionExtensions.Should(instanceResult.Instance.TimeSeriesTypeId).Be(defaultTypeId); instanceResult.Instance.HierarchyIds.Count.Should().Be(0); instanceResult.Instance.InstanceFields.Count.Should().Be(0); } // Update the instances by adding descriptions to them timeSeriesInstances.ForEach((timeSeriesInstance) => timeSeriesInstance.Description = "Description"); Response <InstancesOperationResult[]> replaceInstancesResult = await instancesClient .ReplaceAsync(timeSeriesInstances) .ConfigureAwait(false); replaceInstancesResult.Value.Length.Should().Be(timeSeriesInstances.Count); replaceInstancesResult.Value.Should().OnlyContain((errorResult) => errorResult.Error == null); // Get instances by name Response <InstancesOperationResult[]> getInstancesByNameResult = await instancesClient .GetByNameAsync(timeSeriesInstances.Select((instance) => instance.Name)) .ConfigureAwait(false); getInstancesByNameResult.Value.Length.Should().Be(timeSeriesInstances.Count); foreach (InstancesOperationResult instanceResult in getInstancesByNameResult.Value) { instanceResult.Instance.Should().NotBeNull(); instanceResult.Error.Should().BeNull(); instanceResult.Instance.TimeSeriesId.ToStringArray().Length.Should().Be(numOfIdProperties); AssertionExtensions.Should(instanceResult.Instance.TimeSeriesTypeId).Be(defaultTypeId); instanceResult.Instance.HierarchyIds.Count.Should().Be(0); instanceResult.Instance.InstanceFields.Count.Should().Be(0); } // Get all Time Series instances in the environment AsyncPageable <TimeSeriesInstance> getAllInstancesResponse = instancesClient.GetAsync(); int numOfInstances = 0; await foreach (TimeSeriesInstance tsiInstance in getAllInstancesResponse) { numOfInstances++; tsiInstance.Should().NotBeNull(); } numOfInstances.Should().BeGreaterOrEqualTo(numOfInstancesToSetup); return(null); }), MaxNumberOfRetries, s_retryDelay); } finally { // clean up try { Response <TimeSeriesOperationError[]> deleteInstancesResponse = await instancesClient .DeleteByIdAsync(timeSeriesInstancesIds) .ConfigureAwait(false); // Assert that the response array does not have any error object set deleteInstancesResponse.Value.Should().OnlyContain((errorResult) => errorResult == null); } catch (Exception ex) { Console.WriteLine($"Test clean up failed: {ex.Message}"); throw; } } }
public async Task TimeSeriesInsightsInstances_Lifecycle() { // Arrange TimeSeriesInsightsClient client = GetClient(); int numOfIdProperties = 3; int numOfInstancesToSetup = 2; var timeSeriesInstances = new List <TimeSeriesInstance>(); string defaultTypeId = await getDefaultTypeIdAsync(client).ConfigureAwait(false); for (int i = 0; i < numOfInstancesToSetup; i++) { TimeSeriesId id = await GetUniqueTimeSeriesInstanceIdAsync(client, numOfIdProperties) .ConfigureAwait(false); var instance = new TimeSeriesInstance(id, defaultTypeId) { Name = Recording.GenerateAlphaNumericId("instance"), }; timeSeriesInstances.Add(instance); } IEnumerable <TimeSeriesId> timeSeriesInstancesIds = timeSeriesInstances.Select((instance) => instance.TimeSeriesId); // Act and assert try { await TestRetryHelper.RetryAsync <Response <InstancesOperationResult[]> >(async() => { // Create TSI instances Response <TimeSeriesOperationError[]> createInstancesResult = await client .Instances .CreateOrReplaceAsync(timeSeriesInstances) .ConfigureAwait(false); // Assert that the result error array does not contain any object that is set createInstancesResult.Value.Should().OnlyContain((errorResult) => errorResult == null); // Get the created instances by Ids Response <InstancesOperationResult[]> getInstancesByIdsResult = await client .Instances .GetAsync(timeSeriesInstancesIds) .ConfigureAwait(false); getInstancesByIdsResult.Value.Length.Should().Be(timeSeriesInstances.Count); foreach (InstancesOperationResult instanceResult in getInstancesByIdsResult.Value) { instanceResult.Instance.Should().NotBeNull(); instanceResult.Error.Should().BeNull(); instanceResult.Instance.TimeSeriesId.ToArray().Length.Should().Be(numOfIdProperties); instanceResult.Instance.TypeId.Should().Be(defaultTypeId); instanceResult.Instance.HierarchyIds.Count.Should().Be(0); instanceResult.Instance.InstanceFields.Count.Should().Be(0); } return(null); }, MaxNumberOfRetries, s_retryDelay); // Update the instances by adding descriptions to them timeSeriesInstances.ForEach((timeSeriesInstance) => timeSeriesInstance.Description = "Description"); Response <InstancesOperationResult[]> replaceInstancesResult = await client .Instances .ReplaceAsync(timeSeriesInstances) .ConfigureAwait(false); replaceInstancesResult.Value.Length.Should().Be(timeSeriesInstances.Count); replaceInstancesResult.Value.Should().OnlyContain((errorResult) => errorResult.Error == null); // This retry logic was added as the TSI instance are not immediately available after creation await TestRetryHelper.RetryAsync <Response <InstancesOperationResult[]> >(async() => { // Get instances by name Response <InstancesOperationResult[]> getInstancesByNameResult = await client .Instances .GetAsync(timeSeriesInstances.Select((instance) => instance.Name)) .ConfigureAwait(false); getInstancesByNameResult.Value.Length.Should().Be(timeSeriesInstances.Count); foreach (InstancesOperationResult instanceResult in getInstancesByNameResult.Value) { instanceResult.Instance.Should().NotBeNull(); instanceResult.Error.Should().BeNull(); instanceResult.Instance.TimeSeriesId.ToArray().Length.Should().Be(numOfIdProperties); instanceResult.Instance.TypeId.Should().Be(defaultTypeId); instanceResult.Instance.HierarchyIds.Count.Should().Be(0); instanceResult.Instance.InstanceFields.Count.Should().Be(0); } return(null); }, MaxNumberOfRetries, s_retryDelay); // Get all Time Series instances in the environment AsyncPageable <TimeSeriesInstance> getAllInstancesResponse = client.Instances.GetAsync(); int numOfInstances = 0; await foreach (TimeSeriesInstance tsiInstance in getAllInstancesResponse) { numOfInstances++; tsiInstance.Should().NotBeNull(); } numOfInstances.Should().BeGreaterOrEqualTo(numOfInstancesToSetup); // Get search suggestions for the first instance TimeSeriesId timeSeriesIdToSuggest = timeSeriesInstances.First().TimeSeriesId; string suggestionString = timeSeriesIdToSuggest.ToArray().First(); Response <SearchSuggestion[]> searchSuggestionResponse = await TestRetryHelper.RetryAsync(async() => { Response <SearchSuggestion[]> searchSuggestions = await client .Instances .GetSearchSuggestionsAsync(suggestionString) .ConfigureAwait(false); if (searchSuggestions.Value.Length == 0) { throw new Exception($"Unable to find a search suggestion for string {suggestionString}."); } return(searchSuggestions); }, MaxNumberOfRetries, s_retryDelay); searchSuggestionResponse.Value.Length.Should().Be(1); } finally { // clean up try { Response <TimeSeriesOperationError[]> deleteInstancesResponse = await client .Instances .DeleteAsync(timeSeriesInstancesIds) .ConfigureAwait(false); // Assert that the response array does not have any error object set deleteInstancesResponse.Value.Should().OnlyContain((errorResult) => errorResult == null); } catch (Exception ex) { Console.WriteLine($"Test clean up failed: {ex.Message}"); throw; } } }
private async Task RunQueryAggregateSeriesSample(TimeSeriesInsightsQueries queriesClient, TimeSeriesId tsId) { #region Snippet:TimeSeriesInsightsSampleQueryAggregateSeriesWithNumericVariable Console.WriteLine("\n\nQuery for the average temperature over the past 30 seconds, in 2-second time slots.\n"); var numericVariable = new NumericVariable( new TimeSeriesExpression("$event.Temperature"), new TimeSeriesExpression("avg($value)")); var requestOptions = new QueryAggregateSeriesRequestOptions(); requestOptions.InlineVariables["Temperature"] = numericVariable; requestOptions.ProjectedVariableNames.Add("Temperature"); QueryAnalyzer aggregateSeriesQuery = queriesClient.CreateAggregateSeriesQuery( tsId, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(30), null, requestOptions); await foreach (TimeSeriesPoint point in aggregateSeriesQuery.GetResultsAsync()) { double?averageTemperature = point.GetNullableDouble("Temperature"); if (averageTemperature != null) { Console.WriteLine($"{point.Timestamp} - Average temperature: {averageTemperature}."); } } #endregion Snippet:TimeSeriesInsightsSampleQueryAggregateSeriesWithNumericVariable #region Snippet:TimeSeriesInsightsSampleQueryAggregateSeriesWithAggregateVariable Console.WriteLine("\n\nCount the number of temperature events over the past 3 minutes, in 1-minute time slots.\n"); // Get the count of events in 60-second time slots over the past 3 minutes DateTimeOffset endTime = DateTime.UtcNow; DateTimeOffset startTime = endTime.AddMinutes(-3); var aggregateVariable = new AggregateVariable( new TimeSeriesExpression("count()")); var countVariableName = "Count"; var aggregateSeriesRequestOptions = new QueryAggregateSeriesRequestOptions(); aggregateSeriesRequestOptions.InlineVariables[countVariableName] = aggregateVariable; aggregateSeriesRequestOptions.ProjectedVariableNames.Add(countVariableName); QueryAnalyzer query = queriesClient.CreateAggregateSeriesQuery( tsId, startTime, endTime, TimeSpan.FromSeconds(60), aggregateSeriesRequestOptions); await foreach (TimeSeriesPoint point in query.GetResultsAsync()) { long?temperatureCount = (long?)point.GetValue(countVariableName); Console.WriteLine($"{point.Timestamp} - Temperature count: {temperatureCount}"); } #endregion Snippet:TimeSeriesInsightsSampleQueryAggregateSeriesWithAggregateVariable }
public async Task TimeSeriesInsightsQuery_GetEventsLifecycle() { // Arrange TimeSeriesInsightsClient tsiClient = GetClient(); DeviceClient deviceClient = await GetDeviceClient().ConfigureAwait(false); // Figure out what the Time Series Id is composed of TimeSeriesModelSettings modelSettings = await tsiClient.GetModelSettingsAsync().ConfigureAwait(false); // Create a Time Series Id where the number of keys that make up the Time Series Id is fetched from Model Settings TimeSeriesId tsiId = await GetUniqueTimeSeriesInstanceIdAsync(tsiClient, modelSettings.TimeSeriesIdProperties.Count) .ConfigureAwait(false); try { // Send some events to the IoT hub await QueryTestsHelper.SendEventsToHubAsync( deviceClient, tsiId, modelSettings.TimeSeriesIdProperties.ToArray(), 2) .ConfigureAwait(false); // Act // Get events from last 1 minute DateTimeOffset now = Recording.UtcNow; DateTimeOffset endTime = now.AddMinutes(10); DateTimeOffset startTime = now.AddMinutes(-10); // This retry logic was added as the TSI instance are not immediately available after creation await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() => { AsyncPageable <QueryResultPage> queryEventsPages = tsiClient.QueryEventsAsync(tsiId, startTime, endTime); await foreach (QueryResultPage eventPage in queryEventsPages) { eventPage.Timestamps.Should().HaveCount(2); eventPage.Timestamps.Should().OnlyContain(timeStamp => timeStamp >= startTime).And.OnlyContain(timeStamp => timeStamp <= endTime); eventPage.Properties.Should().NotBeEmpty(); eventPage.Properties.First().Should().NotBeNull(); } return(null); }, MaxNumberOfRetries, s_retryDelay); // Send more events to the hub await QueryTestsHelper.SendEventsToHubAsync( deviceClient, tsiId, modelSettings.TimeSeriesIdProperties.ToArray(), 2) .ConfigureAwait(false); // This retry logic was added as the TSI instance are not immediately available after creation await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() => { AsyncPageable <QueryResultPage> queryEventsPages = tsiClient.QueryEventsAsync(tsiId, startTime, endTime); await foreach (QueryResultPage eventPage in queryEventsPages) { eventPage.Timestamps.Should().HaveCount(4); eventPage.Timestamps.Should() .OnlyContain(timeStamp => timeStamp >= startTime) .And .OnlyContain(timeStamp => timeStamp <= endTime); eventPage.Properties.Should().NotBeEmpty(); eventPage.Properties.First().Should().NotBeNull(); } return(null); }, MaxNumberOfRetries, s_retryDelay); // Send 2 events with a special condition that can be used later to query on IDictionary <string, object> messageBase = QueryTestsHelper.BuildMessageBase(modelSettings.TimeSeriesIdProperties.ToArray(), tsiId); messageBase[QueryTestsHelper.Temperature] = 1.2; messageBase[QueryTestsHelper.Humidity] = 3.4; string messageBody = JsonSerializer.Serialize(messageBase); var message = new Message(Encoding.ASCII.GetBytes(messageBody)) { ContentType = "application/json", ContentEncoding = "utf-8", }; Func <Task> sendEventAct = async() => await deviceClient.SendEventAsync(message).ConfigureAwait(false); await sendEventAct.Should().NotThrowAsync(); // Send it again sendEventAct.Should().NotThrow(); // Query for the two events with a filter // Only project Temperature and one of the Id properties var queryRequestOptions = new QueryEventsRequestOptions { Filter = "$event.Temperature.Double = 1.2", StoreType = StoreType.WarmStore, }; queryRequestOptions.ProjectedProperties.Add( new EventProperty { Name = QueryTestsHelper.Temperature, Type = "Double", }); queryRequestOptions.ProjectedProperties.Add( new EventProperty { Name = modelSettings.TimeSeriesIdProperties.First().Name, Type = modelSettings.TimeSeriesIdProperties.First().Type.ToString(), }); await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() => { AsyncPageable <QueryResultPage> queryEventsPages = tsiClient.QueryEventsAsync(tsiId, startTime, endTime, queryRequestOptions); await foreach (QueryResultPage eventPage in queryEventsPages) { eventPage.Timestamps.Should().HaveCount(2); eventPage.Properties.Should().HaveCount(2); eventPage.Properties.First().Should().NotBeNull(); eventPage.Properties.First().Name.Should().Be(QueryTestsHelper.Temperature); eventPage.Properties[1].Name.Should().Be(modelSettings.TimeSeriesIdProperties.First().Name); } return(null); }, MaxNumberOfRetries, s_retryDelay); // Query for the two events with a filter, but only take 1 queryRequestOptions.MaximumNumberOfEvents = 1; AsyncPageable <QueryResultPage> queryEventsPagesWithFilter = tsiClient.QueryEventsAsync(tsiId, startTime, endTime, queryRequestOptions); await foreach (QueryResultPage eventPage in queryEventsPagesWithFilter) { eventPage.Timestamps.Should().HaveCount(1); eventPage.Properties.Should().HaveCount(2); eventPage.Properties.First().Should().NotBeNull(); eventPage.Properties.First().Name.Should().Be(QueryTestsHelper.Temperature); eventPage.Properties[1].Name.Should().Be(modelSettings.TimeSeriesIdProperties.First().Name); } await TestRetryHelper.RetryAsync <AsyncPageable <QueryResultPage> >(async() => { // Query for all the events using a timespan AsyncPageable <QueryResultPage> queryEventsPagesWithTimespan = tsiClient.QueryEventsAsync(tsiId, TimeSpan.FromMinutes(20), endTime); await foreach (QueryResultPage eventPage in queryEventsPagesWithTimespan) { eventPage.Timestamps.Should().HaveCount(6); eventPage.Timestamps.Should() .OnlyContain(timeStamp => timeStamp >= startTime) .And .OnlyContain(timeStamp => timeStamp <= endTime); eventPage.Properties.Should().NotBeEmpty(); eventPage.Properties.First().Should().NotBeNull(); } return(null); }, MaxNumberOfRetries, s_retryDelay); } finally { deviceClient?.Dispose(); } }
public async Task TimeSeriesInsightsQuery_GetEventsLifecycle() { // Arrange TimeSeriesInsightsClient tsiClient = GetClient(); TimeSeriesInsightsModelSettings timeSeriesModelSettings = tsiClient.GetModelSettingsClient(); TimeSeriesInsightsInstances instancesClient = tsiClient.GetInstancesClient(); TimeSeriesInsightsQueries queriesClient = tsiClient.GetQueriesClient(); DeviceClient deviceClient = await GetDeviceClient().ConfigureAwait(false); // Figure out what the Time Series Id is composed of TimeSeriesModelSettings modelSettings = await timeSeriesModelSettings.GetAsync().ConfigureAwait(false); // Create a Time Series Id where the number of keys that make up the Time Series Id is fetched from Model Settings TimeSeriesId tsiId = await GetUniqueTimeSeriesInstanceIdAsync(instancesClient, modelSettings.TimeSeriesIdProperties.Count) .ConfigureAwait(false); try { var initialEventsCount = 50; // Send some events to the IoT hub await QueryTestsHelper.SendEventsToHubAsync( deviceClient, tsiId, modelSettings.TimeSeriesIdProperties.ToArray(), initialEventsCount) .ConfigureAwait(false); // Act // Get events from last 10 minute DateTimeOffset now = Recording.UtcNow; DateTimeOffset endTime = now.AddMinutes(10); DateTimeOffset startTime = now.AddMinutes(-10); // This retry logic was added as the TSI instance are not immediately available after creation await TestRetryHelper.RetryAsync <AsyncPageable <TimeSeriesPoint> >(async() => { TimeSeriesQueryAnalyzer queryEventsPages = queriesClient.CreateEventsQuery(tsiId, startTime, endTime); var count = 0; await foreach (TimeSeriesPoint timeSeriesPoint in queryEventsPages.GetResultsAsync()) { count++; timeSeriesPoint.Timestamp.Should().BeAfter(startTime).And.BeBefore(endTime); var temperatureValue = timeSeriesPoint.GetNullableDouble(QueryTestsHelper.Temperature); temperatureValue.Should().NotBeNull(); var humidityValue = (double?)timeSeriesPoint.GetValue(QueryTestsHelper.Humidity); humidityValue.Should().NotBeNull(); } count.Should().Be(initialEventsCount); return(null); }, MaxNumberOfRetries, s_retryDelay); // Send 2 events with a special condition that can be used later to query on IDictionary <string, object> messageBase = QueryTestsHelper.BuildMessageBase( modelSettings.TimeSeriesIdProperties.ToArray(), tsiId); messageBase[QueryTestsHelper.Temperature] = 1.2; messageBase[QueryTestsHelper.Humidity] = 3.4; string messageBody = JsonSerializer.Serialize(messageBase); var message = new Message(Encoding.ASCII.GetBytes(messageBody)) { ContentType = "application/json", ContentEncoding = "utf-8", }; Func <Task> sendEventAct = async() => await deviceClient.SendEventAsync(message).ConfigureAwait(false); await sendEventAct.Should().NotThrowAsync(); // Send it again sendEventAct.Should().NotThrow(); // Query for the two events with a filter // Only project Temperature and one of the Id properties var queryRequestOptions = new QueryEventsRequestOptions { Filter = new TimeSeriesExpression("$event.Temperature.Double = 1.2"), Store = StoreType.WarmStore, }; queryRequestOptions.ProjectedProperties.Add( new TimeSeriesInsightsEventProperty { Name = QueryTestsHelper.Temperature, PropertyValueType = "Double", }); queryRequestOptions.ProjectedProperties.Add( new TimeSeriesInsightsEventProperty { Name = modelSettings.TimeSeriesIdProperties.First().Name, PropertyValueType = modelSettings.TimeSeriesIdProperties.First().PropertyType.ToString(), }); await TestRetryHelper.RetryAsync <AsyncPageable <TimeSeriesPoint> >(async() => { TimeSeriesQueryAnalyzer queryEventsPages = queriesClient.CreateEventsQuery(tsiId, startTime, endTime, queryRequestOptions); await foreach (Page <TimeSeriesPoint> page in queryEventsPages.GetResultsAsync().AsPages()) { page.Values.Should().HaveCount(2); foreach (TimeSeriesPoint point in page.Values) { var value = (double?)point.GetValue(QueryTestsHelper.Temperature); value.Should().Be(1.2); } } return(null); }, MaxNumberOfRetries, s_retryDelay); // Query for the two events with a filter, but only take 1 queryRequestOptions.MaxNumberOfEvents = 1; TimeSeriesQueryAnalyzer queryEventsPagesWithFilter = queriesClient.CreateEventsQuery(tsiId, startTime, endTime, queryRequestOptions); await foreach (Page <TimeSeriesPoint> page in queryEventsPagesWithFilter.GetResultsAsync().AsPages()) { page.Values.Should().HaveCount(1); } await TestRetryHelper.RetryAsync <AsyncPageable <TimeSeriesPoint> >(async() => { // Query for all the events using a timespan TimeSeriesQueryAnalyzer queryEventsPagesWithTimespan = queriesClient .CreateEventsQuery(tsiId, TimeSpan.FromMinutes(20), endTime); await foreach (Page <TimeSeriesPoint> page in queryEventsPagesWithTimespan.GetResultsAsync().AsPages()) { page.Values.Should().HaveCount(52); foreach (TimeSeriesPoint point in page.Values) { point.Timestamp.Should().BeAfter(startTime).And.BeBefore(endTime); var temperatureValue = (double?)point.GetValue(QueryTestsHelper.Temperature); temperatureValue.Should().NotBeNull(); var humidityValue = (double?)point.GetValue(QueryTestsHelper.Humidity); humidityValue.Should().NotBeNull(); } } return(null); }, MaxNumberOfRetries, s_retryDelay); } finally { deviceClient?.Dispose(); } }
/// <summary> /// Initializes a new instance of the <see cref="TimeSeriesMetadata"/> class. /// </summary> /// <param name="timeSeries">The <see cref="TimeSeries"/> the metadata is for.</param> public TimeSeriesMetadata(TimeSeriesId timeSeries) { TimeSeries = timeSeries; }
/// <summary> /// Creates a Time Series Insights instance /// Gets all instances for the environment /// Gets a specific instance by Id /// Replaces an instance /// Deletes an instance. /// </summary> public async Task RunSamplesAsync(TimeSeriesInsightsClient client) { PrintHeader("TIME SERIES INSIGHTS INSTANCES SAMPLE"); // Figure out what keys make up the Time Series Id TimeSeriesModelSettings modelSettings = await client.ModelSettings.GetAsync(); TimeSeriesId tsId = TimeSeriesIdHelper.CreateTimeSeriesId(modelSettings); string defaultTypeId = modelSettings.DefaultTypeId; #region Snippet:TimeSeriesInsightsSampleCreateInstance // Create a Time Series Instance object with the default Time Series Insights type Id. // The default type Id can be obtained programmatically by using the ModelSettings client. // tsId is created above using `TimeSeriesIdHelper.CreateTimeSeriesId`. var instance = new TimeSeriesInstance(tsId, defaultTypeId) { Name = "instance1", }; var tsiInstancesToCreate = new List <TimeSeriesInstance> { instance, }; Response <TimeSeriesOperationError[]> createInstanceErrors = await client .Instances .CreateOrReplaceAsync(tsiInstancesToCreate); // The response of calling the API contains a list of error objects corresponding by position to the input parameter // array in the request. If the error object is set to null, this means the operation was a success. for (int i = 0; i < createInstanceErrors.Value.Length; i++) { TimeSeriesId tsiId = tsiInstancesToCreate[i].TimeSeriesId; if (createInstanceErrors.Value[i] == null) { Console.WriteLine($"Created Time Series Insights instance with Id '{tsiId}'."); } else { Console.WriteLine($"Failed to create a Time Series Insights instance with Id '{tsiId}', " + $"Error Message: '{createInstanceErrors.Value[i].Message}, " + $"Error code: '{createInstanceErrors.Value[i].Code}'."); } } #endregion Snippet:TimeSeriesInsightsSampleCreateInstance #region Snippet:TimeSeriesInsightsGetAllInstances // Get all instances for the Time Series Insights environment AsyncPageable <TimeSeriesInstance> tsiInstances = client.Instances.GetAsync(); await foreach (TimeSeriesInstance tsiInstance in tsiInstances) { Console.WriteLine($"Retrieved Time Series Insights instance with Id '{tsiInstance.TimeSeriesId}' and name '{tsiInstance.Name}'."); } #endregion Snippet:TimeSeriesInsightsGetAllInstances #region Snippet:TimeSeriesInsightsReplaceInstance // Get Time Series Insights instances by Id // tsId is created above using `TimeSeriesIdHelper.CreateTimeSeriesId`. var instanceIdsToGet = new List <TimeSeriesId> { tsId, }; Response <InstancesOperationResult[]> getInstancesByIdResult = await client.Instances.GetAsync(instanceIdsToGet); TimeSeriesInstance instanceResult = getInstancesByIdResult.Value[0].Instance; Console.WriteLine($"Retrieved Time Series Insights instance with Id '{instanceResult.TimeSeriesId}' and name '{instanceResult.Name}'."); // Now let's replace the instance with an updated name instanceResult.Name = "newInstanceName"; var instancesToReplace = new List <TimeSeriesInstance> { instanceResult, }; Response <InstancesOperationResult[]> replaceInstancesResult = await client.Instances.ReplaceAsync(instancesToReplace); // The response of calling the API contains a list of error objects corresponding by position to the input parameter. // array in the request. If the error object is set to null, this means the operation was a success. for (int i = 0; i < replaceInstancesResult.Value.Length; i++) { TimeSeriesId tsiId = instancesToReplace[i].TimeSeriesId; TimeSeriesOperationError currentError = replaceInstancesResult.Value[i].Error; if (currentError != null) { Console.WriteLine($"Failed to replace Time Series Insights instance with Id '{tsiId}'," + $" Error Message: '{currentError.Message}', Error code: '{currentError.Code}'."); } else { Console.WriteLine($"Replaced Time Series Insights instance with Id '{tsiId}'."); } } #endregion Snippet:TimeSeriesInsightsReplaceInstance #region Snippet:TimeSeriesInsightsGetnstancesById // Get Time Series Insights instances by Id // tsId is created above using `TimeSeriesIdHelper.CreateTimeSeriesId`. var timeSeriesIds = new List <TimeSeriesId> { tsId, }; Response <InstancesOperationResult[]> getByIdsResult = await client.Instances.GetAsync(timeSeriesIds); // The response of calling the API contains a list of instance or error objects corresponding by position to the array in the request. // Instance object is set when operation is successful and error object is set when operation is unsuccessful. for (int i = 0; i < getByIdsResult.Value.Length; i++) { InstancesOperationResult currentOperationResult = getByIdsResult.Value[i]; if (currentOperationResult.Instance != null) { Console.WriteLine($"Retrieved Time Series Insights instance with Id '{currentOperationResult.Instance.TimeSeriesId}' and name '{currentOperationResult.Instance.Name}'."); } else if (currentOperationResult.Error != null) { Console.WriteLine($"Failed to retrieve a Time Series Insights instance with Id '{timeSeriesIds[i]}'. Error message: '{currentOperationResult.Error.Message}'."); } } #endregion Snippet:TimeSeriesInsightsGetnstancesById // Clean up try { #region Snippet:TimeSeriesInsightsSampleDeleteInstanceById // tsId is created above using `TimeSeriesIdHelper.CreateTimeSeriesId`. var instancesToDelete = new List <TimeSeriesId> { tsId, }; Response <TimeSeriesOperationError[]> deleteInstanceErrors = await client .Instances .DeleteAsync(instancesToDelete); // The response of calling the API contains a list of error objects corresponding by position to the input parameter // array in the request. If the error object is set to null, this means the operation was a success. for (int i = 0; i < deleteInstanceErrors.Value.Length; i++) { TimeSeriesId tsiId = instancesToDelete[i]; if (deleteInstanceErrors.Value[i] == null) { Console.WriteLine($"Deleted Time Series Insights instance with Id '{tsiId}'."); } else { Console.WriteLine($"Failed to delete a Time Series Insights instance with Id '{tsiId}'. Error Message: '{deleteInstanceErrors.Value[i].Message}'"); } } #endregion Snippet:TimeSeriesInsightsSampleDeleteInstanceById } catch (Exception ex) { Console.WriteLine($"Failed to delete Time Series Insights instance: {ex.Message}"); } }
private async Task RunQueryEventsSample(TimeSeriesInsightsQueries queriesClient, TimeSeriesId tsId) { #region Snippet:TimeSeriesInsightsSampleQueryEvents Console.WriteLine("\n\nQuery for raw temperature events over the past 10 minutes.\n"); // Get events from last 10 minute DateTimeOffset endTime = DateTime.UtcNow; DateTimeOffset startTime = endTime.AddMinutes(-10); QueryAnalyzer temperatureEventsQuery = queriesClient.CreateEventsQuery(tsId, startTime, endTime); await foreach (TimeSeriesPoint point in temperatureEventsQuery.GetResultsAsync()) { TimeSeriesValue temperatureValue = point.GetValue("Temperature"); // Figure out what is the underlying type for the time series value. Since you know your Time Series Insights // environment best, you probably do not need this logic and you can skip to directly casting to the proper // type. This logic demonstrates how you can figure out what type to cast to in the case where you are not // too familiar with the property type. if (temperatureValue.Type == typeof(double?)) { Console.WriteLine($"{point.Timestamp} - Temperature: {point.GetNullableDouble("Temperature")}"); } else if (temperatureValue.Type == typeof(int?)) { Console.WriteLine($"{point.Timestamp} - Temperature: {point.GetNullableInt("Temperature")}"); } else { Console.WriteLine("The type of the Time Series value for Temperature is not numeric."); } } #endregion Snippet:TimeSeriesInsightsSampleQueryEvents // Query for raw events using a time interval #region Snippet:TimeSeriesInsightsSampleQueryEventsUsingTimeSpan Console.WriteLine("\n\nQuery for raw humidity events over the past 30 seconds.\n"); QueryAnalyzer humidityEventsQuery = queriesClient.CreateEventsQuery(tsId, TimeSpan.FromSeconds(30)); await foreach (TimeSeriesPoint point in humidityEventsQuery.GetResultsAsync()) { TimeSeriesValue humidityValue = point.GetValue("Humidity"); // Figure out what is the underlying type for the time series value. Since you know your Time Series Insights // environment best, you probably do not need this logic and you can skip to directly casting to the proper // type. This logic demonstrates how you can figure out what type to cast to in the case where you are not // too familiar with the property type. if (humidityValue.Type == typeof(double?)) { Console.WriteLine($"{point.Timestamp} - Humidity: {point.GetNullableDouble("Humidity")}"); } else if (humidityValue.Type == typeof(int?)) { Console.WriteLine($"{point.Timestamp} - Humidity: {point.GetNullableInt("Humidity")}"); } else { Console.WriteLine("The type of the Time Series value for Humidity is not numeric."); } } #endregion Snippet:TimeSeriesInsightsSampleQueryEventsUsingTimeSpan }
private async Task RunQuerySeriesSampleWithPreDefinedVariables(TimeSeriesInsightsClient client, TimeSeriesId tsId) { // Setup TimeSeriesInsightsInstances instancesClient = client.GetInstancesClient(); TimeSeriesInsightsTypes typesClient = client.GetTypesClient(); TimeSeriesInsightsQueries queriesClient = client.GetQueriesClient(); // First create the Time Series type along with the numeric variables var timeSeriesTypes = new List <TimeSeriesType>(); var celsiusVariable = new NumericVariable( new TimeSeriesExpression("$event.Temperature"), new TimeSeriesExpression("avg($value)")); var fahrenheitVariable = new NumericVariable( new TimeSeriesExpression("$event.Temperature * 1.8 + 32"), new TimeSeriesExpression("avg($value)")); var celsiusVariableName = "TemperatureInCelsius"; var fahrenheitVariableName = "TemperatureInFahrenheit"; var variables = new Dictionary <string, TimeSeriesVariable> { { celsiusVariableName, celsiusVariable }, { fahrenheitVariableName, fahrenheitVariable } }; timeSeriesTypes.Add(new TimeSeriesType("TemperatureSensor", variables) { Id = "TemperatureSensorTypeId" }); Response <TimeSeriesTypeOperationResult[]> createTypesResult = await typesClient .CreateOrReplaceAsync(timeSeriesTypes) .ConfigureAwait(false); if (createTypesResult.Value.First().Error != null) { Console.WriteLine($"\n\nFailed to create a Time Series Insights type. " + $"Error Message: '{createTypesResult.Value.First().Error.Message}.' " + $"Code: '{createTypesResult.Value.First().Error.Code}'."); } // Get the Time Series instance and replace its type with the one we just created Response <InstancesOperationResult[]> getInstanceResult = await instancesClient .GetAsync(new List <TimeSeriesId> { tsId }); if (getInstanceResult.Value.First().Error != null) { Console.WriteLine($"\n\nFailed to retrieve Time Series instance with Id '{tsId}'. " + $"Error Message: '{getInstanceResult.Value.First().Error.Message}.' " + $"Code: '{getInstanceResult.Value.First().Error.Code}'."); } TimeSeriesInstance instanceToReplace = getInstanceResult.Value.First().Instance; instanceToReplace.TypeId = createTypesResult.Value.First().TimeSeriesType.Id; Response <InstancesOperationResult[]> replaceInstanceResult = await instancesClient .ReplaceAsync(new List <TimeSeriesInstance> { instanceToReplace }); if (replaceInstanceResult.Value.First().Error != null) { Console.WriteLine($"\n\nFailed to retrieve Time Series instance with Id '{tsId}'. " + $"Error Message: '{replaceInstanceResult.Value.First().Error.Message}.' " + $"Code: '{replaceInstanceResult.Value.First().Error.Code}'."); } // Now that we set up the instance with the property type, query for the data #region Snippet:TimeSeriesInsightsSampleQuerySeries Console.WriteLine($"\n\nQuery for temperature series in Celsius and Fahrenheit over the past 10 minutes. " + $"The Time Series instance belongs to a type that has predefined numeric variable that represents the temperature " + $"in Celsuis, and a predefined numeric variable that represents the temperature in Fahrenheit.\n"); DateTimeOffset endTime = DateTime.UtcNow; DateTimeOffset startTime = endTime.AddMinutes(-10); QueryAnalyzer seriesQuery = queriesClient.CreateSeriesQuery( tsId, startTime, endTime); await foreach (TimeSeriesPoint point in seriesQuery.GetResultsAsync()) { double?tempInCelsius = point.GetNullableDouble(celsiusVariableName); double?tempInFahrenheit = point.GetNullableDouble(fahrenheitVariableName); Console.WriteLine($"{point.Timestamp} - Average temperature in Celsius: {tempInCelsius}. " + $"Average temperature in Fahrenheit: {tempInFahrenheit}."); } #endregion Snippet:TimeSeriesInsightsSampleQuerySeries }