Exemple #1
0
        static PISdsTypes()
        {
            DoubleSdsType             = SdsTypeBuilder.CreateSdsType <DoubleData>();
            DoubleSdsType.Id          = "PIDoubleValueAndTimestamp";
            DoubleSdsType.Name        = "PIDoubleValueAndTimestamp";
            DoubleSdsType.Description = "Represents simple time series data with a floating point value";

            IntegerSdsType             = SdsTypeBuilder.CreateSdsType <IntegerData>();
            IntegerSdsType.Id          = "PIIntegerValueAndTimestamp";
            IntegerSdsType.Name        = "PIIntegerValueAndTimestamp";
            IntegerSdsType.Description = "Represents simple time series data with an integer value";

            StringSdsType             = SdsTypeBuilder.CreateSdsType <StringData>();
            StringSdsType.Id          = "PIStringValueAndTimestamp";
            StringSdsType.Name        = "PIStringValueAndTimestamp";
            StringSdsType.Description = "Represents simple time series data with a string value";

            BlobSdsType             = SdsTypeBuilder.CreateSdsType <BlobData>();
            BlobSdsType.Id          = "PIBlobValueAndTimestamp";
            BlobSdsType.Name        = "PIBlobValueAndTimestamp";
            BlobSdsType.Description = "Represents simple time series data with a blob(byte array) value";

            TimestampSdsType             = SdsTypeBuilder.CreateSdsType <TimeData>();
            TimestampSdsType.Id          = "PITimestampValueAndTimestamp";
            TimestampSdsType.Name        = "PITimestampValueAndTimestamp";
            TimestampSdsType.Description = "Represents simple time series data with a Timestamp(DateTime) value";
        }
Exemple #2
0
        public static async Task <SdsStream> GetOrCreateTypeAndStreamAsync <T>(string id, string description)
        {
            // Stream retrieval call
            var streams = await SdsHelper.metadata.GetStreamsAsync(id);

            var stream = streams.FirstOrDefault();

            if (stream == null)
            {
                SdsType type = SdsTypeBuilder.CreateSdsType <T>();
                type.Id = type.Name;
                await SdsHelper.metadata.GetOrCreateTypeAsync(type);

                stream = new SdsStream
                {
                    Id          = id,
                    Name        = id,
                    TypeId      = type.Id,
                    Description = description
                };

                await SdsHelper.metadata.CreateOrUpdateStreamAsync(stream);
            }

            return(stream);
        }
        public async Task <SdsType> GetOrCreateWumpusType()
        {
            SdsType simpleType = SdsTypeBuilder.CreateSdsType <WumpusActionVote>();

            simpleType.Id          = _wumpusType;
            simpleType.Name        = _wumpusType;
            simpleType.Description = "Wumpus Action Vote stream with action enum.";

            /*var retType = await metadataService.GetTypeAsync(simpleType.Id);
             *
             * if (retType != null)
             * {
             *  return retType;
             * }*/

            var type = await metadataService.GetOrCreateTypeAsync(simpleType);

            return(type);
        }
        public static async Task <bool> MainAsync(bool test = false)
        {
            ISdsMetadataService metadataService = null;

            #region settings
            var typeValueTimeName = "Value_Time";
            var typePressureTemperatureTimeName = "Pressure_Temp_Time";

            var streamPressureName = "Pressure_Tank1";
            var streamTempName     = "Temperature_Tank1";
            var streamTank0        = "Vessel";
            var streamTank1        = "Tank1";
            var streamTank2        = "Tank2";
            #endregion

            try
            {
                #region configurationSettings

                _configuration = new ConfigurationBuilder()
                                 .SetBasePath(Directory.GetCurrentDirectory())
                                 .AddJsonFile("appsettings.json")
                                 .AddJsonFile("appsettings.test.json", optional: true)
                                 .Build();

                var tenantId    = _configuration["TenantId"];
                var namespaceId = _configuration["NamespaceId"];
                var resource    = _configuration["Resource"];
                var clientId    = _configuration["ClientId"];
                var clientKey   = _configuration["ClientKey"];
                #endregion

                (_configuration as ConfigurationRoot).Dispose();
                var uriResource = new Uri(resource);

                // Step 1
                // Get Sds Services to communicate with server
                #region step1
                AuthenticationHandler authenticationHandler = new AuthenticationHandler(uriResource, clientId, clientKey);

                SdsService sdsService = new SdsService(new Uri(resource), authenticationHandler);
                metadataService = sdsService.GetMetadataService(tenantId, namespaceId);
                var dataService  = sdsService.GetDataService(tenantId, namespaceId);
                var tableService = sdsService.GetTableService(tenantId, namespaceId);
                #endregion

                // Step 2
                #region step2b
                SdsType type = SdsTypeBuilder.CreateSdsType <TimeData>();
                type.Id = typeValueTimeName;
                type    = await metadataService.GetOrCreateTypeAsync(type).ConfigureAwait(false);

                #endregion

                // Step 3
                // create an SdsStream
                #region step3
                var pressure_stream = new SdsStream
                {
                    Id          = streamPressureName,
                    TypeId      = type.Id,
                    Description = "A stream for pressure data of tank1",
                };
                pressure_stream = await metadataService.GetOrCreateStreamAsync(pressure_stream).ConfigureAwait(false);

                var temperature_stream = new SdsStream
                {
                    Id          = streamTempName,
                    TypeId      = type.Id,
                    Description = "A stream for temperature data of tank1",
                };
                temperature_stream = await metadataService.GetOrCreateStreamAsync(temperature_stream).ConfigureAwait(false);

                #endregion

                // Step 4
                // insert simple data
                #region step4c
                await dataService.InsertValuesAsync(pressure_stream.Id, GetPressureData()).ConfigureAwait(false);

                await dataService.InsertValuesAsync(streamTempName, GetTemperatureData()).ConfigureAwait(false);

                #endregion

                // Step 5
                // create complex type
                #region step5b
                SdsType tankType = SdsTypeBuilder.CreateSdsType <PressureTemperatureData>();
                tankType.Id = typePressureTemperatureTimeName;
                tankType    = await metadataService.GetOrCreateTypeAsync(tankType).ConfigureAwait(false);

                #endregion

                // Step 6
                // create complex type stream
                #region step6
                var tankStream = new SdsStream
                {
                    Id          = streamTank1,
                    TypeId      = tankType.Id,
                    Description = "A stream for data of tank1",
                };
                tankStream = await metadataService.GetOrCreateStreamAsync(tankStream).ConfigureAwait(false);

                #endregion

                // Step 7
                // insert complex data
                #region step7
                var data = GetData();
                await dataService.InsertValuesAsync(streamTank1, data).ConfigureAwait(false);

                #endregion

                // Step 8 and Step 9
                //  view data
                // note: step 9 is not done in this example as the JSON conversion by the library takes care of it automatically for you
                #region step8
                var sortedData = data.OrderBy(entry => entry.Time).ToList();
                var firstTime  = sortedData.First();
                var lastTime   = sortedData.Last();

                var resultsPressure = (await dataService.GetWindowValuesAsync <TimeData>(
                                           streamPressureName,
                                           firstTime.Time.ToUniversalTime().ToString("o", CultureInfo.InvariantCulture),
                                           lastTime.Time.ToUniversalTime().ToString("o", CultureInfo.InvariantCulture))
                                       .ConfigureAwait(false))
                                      .ToList();

                Console.WriteLine("Values from Pressure of Tank1:");
                foreach (var evnt in resultsPressure)
                {
                    Console.WriteLine(JsonConvert.SerializeObject(evnt));
                }

                var resultsTank = (await dataService.GetWindowValuesAsync <PressureTemperatureData>(
                                       streamTank1,
                                       firstTime.Time.ToUniversalTime().ToString("o", CultureInfo.InvariantCulture),
                                       lastTime.Time.ToUniversalTime().ToString("o", CultureInfo.InvariantCulture))
                                   .ConfigureAwait(false))
                                  .ToList();

                Console.WriteLine("Values from Tank1:");
                foreach (var evnt in resultsTank)
                {
                    Console.WriteLine(JsonConvert.SerializeObject(evnt));
                }
                #endregion

                if (test)
                {
                    // Testing to make sure we get back expected stuff
                    if (string.Compare(JsonConvert.SerializeObject(resultsPressure.First()), "{\"Time\":\"2017-01-11T22:21:23.43Z\",\"Value\":346.0}", StringComparison.OrdinalIgnoreCase) != 0)
                    {
                        throw new Exception("Value retrieved isn't expected value for pressure of Tank1");
                    }

                    if (string.Compare(JsonConvert.SerializeObject(resultsTank.First()), "{\"Time\":\"2017-01-11T22:21:23.43Z\",\"Pressure\":346.0,\"Temperature\":91.0}", StringComparison.OrdinalIgnoreCase) != 0)
                    {
                        throw new Exception("Value retrieved isn't expected value for Temeprature from Tank1");
                    }
                }

                // Step 10
                //  view summary data
                #region step10
                var resultsTankSummary = (await dataService.GetIntervalsAsync <PressureTemperatureData>(
                                              streamTank1,
                                              firstTime.Time.ToUniversalTime().ToString("o", CultureInfo.InvariantCulture),
                                              lastTime.Time.ToUniversalTime().ToString("o", CultureInfo.InvariantCulture),
                                              1)
                                          .ConfigureAwait(false))
                                         .ToList();

                Console.WriteLine("Summaries from Tank1:");
                foreach (var evnt in resultsTankSummary)
                {
                    Console.WriteLine(JsonConvert.SerializeObject(evnt.Summaries));
                }
                #endregion

                // Step 11
                //  Bulk calls
                #region step11a
                var tankStream0 = new SdsStream
                {
                    Id     = streamTank0,
                    TypeId = tankType.Id,
                };
                tankStream = await metadataService.GetOrCreateStreamAsync(tankStream0).ConfigureAwait(false);

                var tankStream2 = new SdsStream
                {
                    Id          = streamTank2,
                    TypeId      = tankType.Id,
                    Description = "A stream for data of tank2",
                };
                tankStream2 = await metadataService.GetOrCreateStreamAsync(tankStream2).ConfigureAwait(false);

                var data2       = GetData2();
                var sortedData2 = data2.OrderBy(entry => entry.Time).ToList();
                var firstTime2  = sortedData2.First();
                var lastTime2   = sortedData2.Last();

                await dataService.InsertValuesAsync(tankStream2.Id, data2).ConfigureAwait(false);

                await dataService.InsertValuesAsync(tankStream0.Id, GetData()).ConfigureAwait(false);

                #endregion

                Thread.Sleep(200); // slight rest here for consistency

                #region step11b
                var results2Tanks = await dataService.GetJoinValuesAsync <PressureTemperatureData>(
                    new string[] { tankStream0.Id, tankStream2.Id },
                    SdsJoinType.Outer,
                    firstTime2.Time.ToUniversalTime().ToString("o", CultureInfo.InvariantCulture),
                    lastTime2.Time.ToUniversalTime().ToString("o", CultureInfo.InvariantCulture))
                                    .ConfigureAwait(false);

                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine($"Bulk Values:   {tankStream0.Id}  then {tankStream2.Id}: ");
                Console.WriteLine();
                foreach (var tankResult in results2Tanks)
                {
                    foreach (var dataEntry in tankResult)
                    {
                        Console.WriteLine(JsonConvert.SerializeObject(dataEntry));
                    }

                    Console.WriteLine();
                }

                #endregion
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                _toThrow = ex;
                throw;
            }
            finally
            {
                if (metadataService != null)
                {
                    // Step 12
                    // delete everything
                    #region step12
                    await RunInTryCatch(metadataService.DeleteStreamAsync, streamPressureName).ConfigureAwait(false);
                    await RunInTryCatch(metadataService.DeleteStreamAsync, streamTempName).ConfigureAwait(false);
                    await RunInTryCatch(metadataService.DeleteStreamAsync, streamTank0).ConfigureAwait(false);
                    await RunInTryCatch(metadataService.DeleteStreamAsync, streamTank1).ConfigureAwait(false);
                    await RunInTryCatch(metadataService.DeleteStreamAsync, streamTank2).ConfigureAwait(false);

                    await RunInTryCatch(metadataService.DeleteTypeAsync, typeValueTimeName).ConfigureAwait(false);
                    await RunInTryCatch(metadataService.DeleteTypeAsync, typePressureTemperatureTimeName).ConfigureAwait(false);

                    #endregion

                    Thread.Sleep(10); // slight rest here for consistency

                    // Check deletes
                    await RunInTryCatchExpectException(metadataService.GetStreamAsync, streamPressureName).ConfigureAwait(false);
                    await RunInTryCatchExpectException(metadataService.GetStreamAsync, streamTempName).ConfigureAwait(false);
                    await RunInTryCatchExpectException(metadataService.GetStreamAsync, streamTank0).ConfigureAwait(false);
                    await RunInTryCatchExpectException(metadataService.GetStreamAsync, streamTank1).ConfigureAwait(false);
                    await RunInTryCatchExpectException(metadataService.GetStreamAsync, streamTank2).ConfigureAwait(false);

                    await RunInTryCatchExpectException(metadataService.GetTypeAsync, typeValueTimeName).ConfigureAwait(false);
                    await RunInTryCatchExpectException(metadataService.GetTypeAsync, typePressureTemperatureTimeName).ConfigureAwait(false);
                }
            }

            if (test && _toThrow != null)
            {
                throw _toThrow;
            }

            return(_toThrow == null);
        }
Exemple #5
0
        public static async Task <bool> MainAsync(bool testIn = false, string fileLocation = "datafile.csv")
        {
            test = testIn;

            try
            {
                // Import data in.  Use csv reader and custom class to make it simple
                using (var reader = new StreamReader(fileLocation))
                    using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
                    {
                        dataList = csv.GetRecords <TemperatureReadingsWithIds>().ToList();
                    }
                // Use Linq to get the distinct StreamIds we need.
                streamsIdsToSendTo = dataList.Select(dataeEntry => dataeEntry.StreamId).Distinct();

                //Get Configuration information about where this is sending to
                configuration = new ConfigurationBuilder()
                                .SetBasePath(Directory.GetCurrentDirectory())
                                .AddJsonFile("appsettings.json")
                                .AddJsonFile("appsettings.test.json", optional: true)
                                .Build();

                var tenantId    = configuration["TenantId"];
                var namespaceId = configuration["NamespaceId"];
                var resource    = configuration["Resource"];
                var clientId    = configuration["ClientID"];

                if (!test)
                {
                    SystemBrowser.OpenBrowser = new OpenSystemBrowser();
                }
                else
                {
                    SystemBrowser.Password = configuration["Password"];
                    SystemBrowser.UserName = configuration["UserName"];
                }

                (configuration as ConfigurationRoot).Dispose();

                // Setup access to OCS
                AuthenticationHandler_PKCE authenticationHandler = new AuthenticationHandler_PKCE(tenantId, clientId, resource);

                SdsService sdsService = new SdsService(new Uri(resource), authenticationHandler);
                dataService = sdsService.GetDataService(tenantId, namespaceId);
                metaService = sdsService.GetMetadataService(tenantId, namespaceId);

                if (createStreams)
                {
                    SdsType typeToCreate = SdsTypeBuilder.CreateSdsType <TemperatureReadings>();
                    typeToCreate.Id = typeID;
                    Console.WriteLine("Creating Type");
                    await metaService.GetOrCreateTypeAsync(typeToCreate);

                    var stream1 = new SdsStream {
                        Id = stream1ID, TypeId = typeToCreate.Id
                    };
                    var stream2 = new SdsStream {
                        Id = stream2ID, TypeId = typeToCreate.Id
                    };
                    Console.WriteLine("Creating Stream");
                    stream1 = await metaService.GetOrCreateStreamAsync(stream1);

                    stream2 = await metaService.GetOrCreateStreamAsync(stream2);
                }

                Console.WriteLine("Sending Data");
                // Loop over each stream to send to and send the data as one call.
                foreach (string streamId in streamsIdsToSendTo)
                {
                    // Get all of the data for this stream in a list
                    var valueToSend = dataList.Where(dataEntry => dataEntry.StreamId == streamId) //gets only appropriate data for stream
                                      .Select(dataEntry => new TemperatureReadings(dataEntry))    // transforms it to the right data
                                      .ToList();                                                  // needed in IList format for insertValues
                    await dataService.InsertValuesAsync(streamId, valueToSend);
                }

                if (test)
                {
                    //checks to make sure values are written
                    await CheckValuesWrittenASync();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                toThrow = ex;
            }
            finally
            {
                if (test)
                {
                    if (!createStreams)
                    {
                        // if we just created the data lets just remove that
                        // Do Delete
                        Console.WriteLine("Deleting Data");
                        await DeleteValuesAsync();

                        // Do Delete check
                        await CheckDeletesValuesAsync();
                    }
                    else
                    {
                        Console.WriteLine("Deleting Streams");
                        // if we created the types and streams, lets remove those too
                        await RunInTryCatch(metaService.DeleteStreamAsync, stream1ID);
                        await RunInTryCatch(metaService.DeleteStreamAsync, stream2ID);

                        Console.WriteLine("Deleting Types");
                        await RunInTryCatch(metaService.DeleteTypeAsync, typeID);

                        // Check deletes
                        await RunInTryCatchExpectException(metaService.GetStreamAsync, stream1ID);
                        await RunInTryCatchExpectException(metaService.GetStreamAsync, stream2ID);
                        await RunInTryCatchExpectException(metaService.GetTypeAsync, typeID);
                    }
                }
            }

            if (toThrow != null)
            {
                throw toThrow;
            }

            return(toThrow == null);
        }
Exemple #6
0
        public static async Task MainAsync()
        {
            string tenantId     = ConfigurationManager.AppSettings["Tenant"];
            string namespaceId  = ConfigurationManager.AppSettings["Namespace"];
            string address      = ConfigurationManager.AppSettings["Address"];
            string resource     = ConfigurationManager.AppSettings["Resource"];
            string clientId     = ConfigurationManager.AppSettings["ClientId"];
            string clientSecret = ConfigurationManager.AppSettings["ClientSecret"];

            SdsSecurityHandler securityHandler = new SdsSecurityHandler(resource, tenantId, clientId, clientSecret);
            SdsService         service         = new SdsService(new Uri(address), securityHandler);

            ISdsMetadataService MetadataService = service.GetMetadataService(tenantId, namespaceId);
            ISdsDataService     DataService     = service.GetDataService(tenantId, namespaceId);

            /*
             * The following code provides an implementation for getting all the SdsUom ID's for each quantity.
             * If you are not aware with which SdsUom ID to use, you can uncomment the below code and find out the
             * uom id.
             *
             * e.g. I am using degree_fahrenheit and  degree_celsius UOMS for temperature SdsQuantity.
             *
             *
             * IEnumerable<SdsUomQuantity> sdsUomQuantities = await MetadataService.GetQuantitiesAsync();
             * IEnumerable<SdsUom> sdsUoms = null;
             *
             * foreach (SdsUomQuantity sdsUomQuantity in sdsUomQuantities)
             * {
             *  sdsUoms = await MetadataService.GetQuantityUomsAsync(sdsUomQuantity.Id);
             *
             *  foreach (SdsUom sdsUom in sdsUoms)
             *  {
             *      Console.WriteLine(sdsUom.Id);
             *  }
             * }
             */

            #region stream and type creation

            // Creating a Sdstype
            SdsType sdsType = SdsTypeBuilder.CreateSdsType <Widget>();
            sdsType.Id = TypeId;

            sdsType = await MetadataService.GetOrCreateTypeAsync(sdsType);

            //Creating a Stream overriding the distance property.
            SdsStream sdsStreamOne = new SdsStream()
            {
                Id                = StreamWithPropertyOverriden,
                TypeId            = TypeId,
                Name              = "UomStreamSourceWithPropertyOverridden",
                PropertyOverrides = new List <SdsStreamPropertyOverride>()
            };

            //Overriding the UOM of the distance property to be kilometer instead of mile.
            sdsStreamOne.PropertyOverrides.Add(new SdsStreamPropertyOverride()
            {
                Uom = "kilometer",
                SdsTypePropertyId = "Distance"
            });

            sdsStreamOne = await MetadataService.GetOrCreateStreamAsync(sdsStreamOne);

            //Creating a Stream without overriding properties.
            SdsStream sdsStreamTwo = new SdsStream()
            {
                Id     = StreamWithoutPropertyOverriden,
                TypeId = TypeId,
                Name   = "UomStreamSourceWithNoPropertyOverridden",
            };

            sdsStreamTwo = await MetadataService.GetOrCreateStreamAsync(sdsStreamTwo);

            // Generating data
            IList <Widget> data = new List <Widget>();
            for (int i = 0; i < 10; i++)
            {
                Widget widget = new Widget();
                widget.Time        = DateTime.UtcNow.AddSeconds(i);
                widget.Temperature = Random.Next(1, 100);
                widget.Distance    = Random.Next(1, 100);
                data.Add(widget);
            }

            /* In stream one, the temperature value will be inserted as Fahrenheit since we have defined the
             * default uom as Fahrenheit for Temperature in the Widget class. The distance value will be
             * inserted as kilometer, as we have overridden the Distance property in stream one,
             * regardless of the default uom for Distance in the Widget class.
             */
            await DataService.InsertValuesAsync <Widget>(sdsStreamOne.Id, data);

            /* In stream two, the temperature value will be inserted as Fahrenheit and the distance will be inserted as mile.
             *
             */
            await DataService.InsertValuesAsync <Widget>(sdsStreamTwo.Id, data);

            /*
             * The last value stored in stream one.
             */
            Widget widgetFromStreamOne = await DataService.GetLastValueAsync <Widget>(sdsStreamOne.Id);

            Console.WriteLine($"In stream one, the distance is {widgetFromStreamOne.Distance} kilometers and the temperature is {widgetFromStreamOne.Temperature} degrees fahrenheit");
            Console.WriteLine();

            /*
             * The last value stored in stream two.
             */
            Widget widgetFromStreamTwo = await DataService.GetLastValueAsync <Widget>(sdsStreamTwo.Id);

            Console.WriteLine($"In stream two, the distance is {widgetFromStreamTwo.Distance} miles and the temperature is {widgetFromStreamTwo.Temperature} degrees fahrenheit");
            Console.WriteLine();

            /*
             * If you want your data to be in specified uom, you can override your properties while making a call.
             * In the following, I want the temperature to be in Celsius, and the distance to be in feet.
             *
             * Then you can pass IList<SdsStreamPropertyOverride> to DataService while getting values.
             *
             */
            IList <SdsStreamPropertyOverride> requestOverrides = new List <SdsStreamPropertyOverride>();

            requestOverrides.Add(new SdsStreamPropertyOverride()
            {
                Uom = "degree_celsius",
                SdsTypePropertyId = "Temperature"
            });

            requestOverrides.Add(new SdsStreamPropertyOverride()
            {
                Uom = "foot",
                SdsTypePropertyId = "Distance"
            });

            /*
             * In the following call, data will be converted from Fahrenheit to Celsius for the temperature property,
             * and from kilometer to foot for the distance property.
             *
             * Uoms in Stream one (Temperature : Fahrenheit, Distance : Kilometer)
             *
             */
            widgetFromStreamOne = await DataService.GetLastValueAsync <Widget>(sdsStreamOne.Id, requestOverrides);

            Console.WriteLine($"In stream one, the distance is {widgetFromStreamOne.Distance} foot and the temperature is {widgetFromStreamOne.Temperature} degrees celsius");
            Console.WriteLine();

            /*
             * In the following call, data will be converted from Fahrenheit to Celsius for the temperature property,
             * and from mile to foot for the distance property.
             *
             * Uoms in Stream two (Temperature : Fahrenheit, Distance : Mile)
             *
             */
            widgetFromStreamTwo = await DataService.GetLastValueAsync <Widget>(sdsStreamTwo.Id, requestOverrides);

            Console.WriteLine($"In stream two, the distance is {widgetFromStreamTwo.Distance} foot and the temperature is {widgetFromStreamTwo.Temperature} degrees celsius");
            Console.WriteLine();

            #endregion stream and type creation

            #region Deletion of Streams and type

            await MetadataService.DeleteStreamAsync(sdsStreamOne.Id);

            await MetadataService.DeleteStreamAsync(sdsStreamTwo.Id);

            await MetadataService.DeleteTypeAsync(TypeId);

            #endregion Deletion of Streams and type
        }
Exemple #7
0
        private static async Task MainAsync()
        {
            IConfigurationBuilder builder = new ConfigurationBuilder()
                                            .SetBasePath(Directory.GetCurrentDirectory())
                                            .AddJsonFile("appsettings.json");
            IConfiguration configuration = builder.Build();

            // ==== Client constants ====
            var tenantId    = configuration["TenantId"];
            var namespaceId = configuration["NamespaceId"];
            var address     = configuration["Address"];
            var resource    = configuration["Resource"];
            var clientId    = configuration["ClientId"];
            var clientKey   = configuration["ClientKey"];

            // ==== Metadata IDs ====
            string streamId        = "SampleStream";
            string typeId          = "SampleType";
            string targetTypeId    = "SampleType_Target";
            string targetIntTypeId = "SampleType_TargetInt";
            string autoViewId      = "SampleAutoView";
            string manualViewId    = "SampleManualView";

            // Get Sds Services to communicate with server
            SdsSecurityHandler securityHandler = new SdsSecurityHandler(resource, tenantId, clientId, clientKey);

            SdsService sdsService      = new SdsService(new Uri(address), securityHandler);
            var        metadataService = sdsService.GetMetadataService(tenantId, namespaceId);
            var        dataService     = sdsService.GetDataService(tenantId, namespaceId);

            LoggerCallbackHandler.UseDefaultLogging = false;


            Console.WriteLine(@"-------------------------------------------------------------");
            Console.WriteLine(@"  _________    .___           _______  ______________________");
            Console.WriteLine(@" /   _____/  __| _/______     \      \ \_   _____/\__    ___/");
            Console.WriteLine(@" \_____  \  / __ |/  ___/     /   |   \ |    __)_   |    |   ");
            Console.WriteLine(@" /        \/ /_/ |\___ \     /    |    \|        \  |    |   ");
            Console.WriteLine(@"/_______  /\____ /____  > /\ \____|__  /_______  /  |____|   ");
            Console.WriteLine(@"        \/      \/    \/  \/         \/        \/            ");
            Console.WriteLine(@"-------------------------------------------------------------");
            Console.WriteLine();
            Console.WriteLine($"Sds endpoint at {address}");
            Console.WriteLine();

            try
            {
                // create an SdsType
                Console.WriteLine("Creating an SdsType");
                SdsType type = SdsTypeBuilder.CreateSdsType <WaveData>();
                type.Id = typeId;
                type    = await metadataService.GetOrCreateTypeAsync(type);

                // create an SdsStream
                Console.WriteLine("Creating an SdsStream");
                var stream = new SdsStream
                {
                    Id          = streamId,
                    Name        = "Wave Data Sample",
                    TypeId      = type.Id,
                    Description = "This is a sample SdsStream for storing WaveData type measurements"
                };
                stream = await metadataService.GetOrCreateStreamAsync(stream);

                // insert data
                Console.WriteLine("Inserting data");

                // insert a single event
                var wave = GetWave(0, 200, 2);
                await dataService.InsertValueAsync(stream.Id, wave);

                // insert a list of events
                var waves = new List <WaveData>();
                for (var i = 2; i <= 18; i += 2)
                {
                    waves.Add(GetWave(i, 200, 2));
                }
                await dataService.InsertValuesAsync(stream.Id, waves);

                // get last event
                Console.WriteLine("Getting latest event");
                var latest = await dataService.GetLastValueAsync <WaveData>(streamId);

                Console.WriteLine(latest.ToString());
                Console.WriteLine();

                // get all events
                Console.WriteLine("Getting all events");
                var allEvents = (List <WaveData>) await dataService.GetWindowValuesAsync <WaveData>(streamId, "0", "180");

                Console.WriteLine($"Total events found: {allEvents.Count}");
                foreach (var evnt in allEvents)
                {
                    Console.WriteLine(evnt.ToString());
                }
                Console.WriteLine();

                // update events
                Console.WriteLine("Updating events");

                // update one event
                var updatedWave = UpdateWave(allEvents.First(), 4);
                await dataService.UpdateValueAsync(stream.Id, updatedWave);

                // update all events, adding ten more
                var updatedCollection = new List <WaveData>();
                for (int i = 2; i < 40; i = i + 2)
                {
                    updatedCollection.Add(GetWave(i, 400, 4));
                }
                await dataService.UpdateValuesAsync(stream.Id, updatedCollection);

                allEvents = (List <WaveData>) await dataService.GetWindowValuesAsync <WaveData>(stream.Id, "0", "180");

                Console.WriteLine("Getting updated events");
                Console.WriteLine($"Total events found: {allEvents.Count}");

                foreach (var evnt in allEvents)
                {
                    Console.WriteLine(evnt.ToString());
                }
                Console.WriteLine();

                // replacing events
                Console.WriteLine("Replacing events");

                // replace one event
                var replaceEvent = allEvents.First();
                replaceEvent.Sin = 0.717;
                replaceEvent.Cos = 0.717;
                replaceEvent.Tan = Math.Sqrt(2 * (0.717 * 0.717));

                await dataService.ReplaceValueAsync <WaveData>(streamId, replaceEvent);

                // replace all events
                foreach (var evnt in allEvents)
                {
                    evnt.Sin = 5.0 / 2;
                    evnt.Cos = 5 * Math.Sqrt(3) / 2;
                    evnt.Tan = 5 / Math.Sqrt(3);
                }

                await dataService.ReplaceValuesAsync <WaveData>(streamId, allEvents);

                Console.WriteLine("Getting replaced events");
                var replacedEvents = (List <WaveData>) await dataService.GetWindowValuesAsync <WaveData>(streamId, "0", "180");

                Console.WriteLine($"Total events found: {replacedEvents.Count}");
                foreach (var evnt in replacedEvents)
                {
                    Console.WriteLine(evnt.ToString());
                }
                Console.WriteLine();

                // Property Overrides
                Console.WriteLine("Sds can interpolate or extrapolate data at an index location where data does not explicitly exist:");
                Console.WriteLine();

                // We will retrieve three events using the default behavior, Continuous
                var retrieved = await dataService
                                .GetRangeValuesAsync <WaveData>(stream.Id, "1", 3, SdsBoundaryType.ExactOrCalculated);

                Console.WriteLine("Default (Continuous) requesting data starting at index location '1', where we have not entered data, Sds will interpolate a value for each property:");
                foreach (var value in retrieved)
                {
                    Console.WriteLine($"Order: {value.Order}, Radians: {value.Radians}, Cos: {value.Cos}");
                }
                Console.WriteLine();

                // create a Discrete stream PropertyOverride indicating that we do not want Sds to calculate a value for Radians and update our stream
                var propertyOverride = new SdsStreamPropertyOverride()
                {
                    SdsTypePropertyId = "Radians",
                    InterpolationMode = SdsInterpolationMode.Discrete
                };
                var propertyOverrides = new List <SdsStreamPropertyOverride>()
                {
                    propertyOverride
                };

                // update the stream
                stream.PropertyOverrides = propertyOverrides;
                await metadataService.CreateOrUpdateStreamAsync(stream);

                retrieved = await dataService
                            .GetRangeValuesAsync <WaveData>(stream.Id, "1", 3, SdsBoundaryType.ExactOrCalculated);

                Console.WriteLine("We can override this behavior on a property by property basis, here we override the Radians property instructing Sds not to interpolate.");
                Console.WriteLine("Sds will now return the default value for the data type:");

                foreach (var value in retrieved)
                {
                    Console.WriteLine($"Order: {value.Order}, Radians: {value.Radians}, Cos: {value.Cos}");
                }
                Console.WriteLine();

                // Stream views
                Console.WriteLine("SdsViews");

                // create target types
                var targetType = SdsTypeBuilder.CreateSdsType <WaveDataTarget>();
                targetType.Id = targetTypeId;

                var targetIntType = SdsTypeBuilder.CreateSdsType <WaveDataInteger>();
                targetIntType.Id = targetIntTypeId;

                await metadataService.CreateOrUpdateTypeAsync(targetType);

                await metadataService.CreateOrUpdateTypeAsync(targetIntType);

                // create views
                var autoView = new SdsView()
                {
                    Id           = autoViewId,
                    SourceTypeId = typeId,
                    TargetTypeId = targetTypeId
                };

                // create explicit mappings
                var vp1 = new SdsViewProperty()
                {
                    SourceId = "Order", TargetId = "OrderTarget"
                };
                var vp2 = new SdsViewProperty()
                {
                    SourceId = "Sin", TargetId = "SinInt"
                };
                var vp3 = new SdsViewProperty()
                {
                    SourceId = "Cos", TargetId = "CosInt"
                };
                var vp4 = new SdsViewProperty()
                {
                    SourceId = "Tan", TargetId = "TanInt"
                };

                var manualView = new SdsView()
                {
                    Id           = manualViewId,
                    SourceTypeId = typeId,
                    TargetTypeId = targetIntTypeId,
                    Properties   = new List <SdsViewProperty>()
                    {
                        vp1, vp2, vp3, vp4
                    }
                };

                await metadataService.CreateOrUpdateViewAsync(autoView);

                await metadataService.CreateOrUpdateViewAsync(manualView);

                Console.WriteLine("Here is some of our data as it is stored on the server:");
                foreach (var evnt in retrieved)
                {
                    Console.WriteLine($"Sin: {evnt.Sin}, Cos: {evnt.Cos}, Tan {evnt.Tan}");
                }
                Console.WriteLine();

                // get autoview data
                var autoViewData = await dataService.GetRangeValuesAsync <WaveDataTarget>(stream.Id, "1", 3, SdsBoundaryType.ExactOrCalculated, autoViewId);

                Console.WriteLine("Specifying a view with an SdsType of the same shape returns values that are automatically mapped to the target SdsType's properties:");

                foreach (var value in autoViewData)
                {
                    Console.WriteLine($"SinTarget: {value.SinTarget} CosTarget: {value.CosTarget} TanTarget: {value.TanTarget}");
                }
                Console.WriteLine();

                // get manaulview data
                Console.WriteLine("SdsViews can also convert certain types of data, here we return integers where the original values were doubles:");
                var manualViewData = await dataService.GetRangeValuesAsync <WaveDataInteger>(stream.Id, "1", 3, SdsBoundaryType.ExactOrCalculated, manualViewId);

                foreach (var value in manualViewData)
                {
                    Console.WriteLine($"SinInt: {value.SinInt} CosInt: {value.CosInt} TanInt: {value.TanInt}");
                }
                Console.WriteLine();

                // get SdsViewMap
                Console.WriteLine("We can query Sds to return the SdsViewMap for our SdsView, here is the one generated automatically:");
                var autoViewMap = await metadataService.GetViewMapAsync(autoViewId);

                PrintViewMapProperties(autoViewMap);

                Console.WriteLine("Here is our explicit mapping, note SdsViewMap will return all properties of the Source Type, even those without a corresponding Target property:");
                var manualViewMap = await metadataService.GetViewMapAsync(manualViewId);

                PrintViewMapProperties(manualViewMap);

                // tags, metadata and search
                Console.WriteLine("Let's add some Tags and Metadata to our stream:");
                var tags = new List <string> {
                    "waves", "periodic", "2018", "validated"
                };
                var metadata = new Dictionary <string, string>()
                {
                    { "Region", "North America" }, { "Country", "Canada" }, { "Province", "Quebec" }
                };

                await metadataService.UpdateStreamTagsAsync(streamId, tags);

                await metadataService.UpdateStreamMetadataAsync(streamId, metadata);

                tags = (List <string>) await metadataService.GetStreamTagsAsync(streamId);

                Console.WriteLine();
                Console.WriteLine($"Tags now associated with {streamId}:");
                foreach (var tag in tags)
                {
                    Console.WriteLine(tag);
                }
                Console.WriteLine();
                Console.WriteLine($"Metadata now associated with {streamId}:");
                Console.WriteLine("Metadata key Region: " + await metadataService.GetStreamMetadataValueAsync(streamId, "Region"));
                Console.WriteLine("Metadata key Country: " + await metadataService.GetStreamMetadataValueAsync(streamId, "Country"));
                Console.WriteLine("Metadata key Province: " + await metadataService.GetStreamMetadataValueAsync(streamId, "Province"));

                Console.WriteLine();
                Console.WriteLine("Pausing to allow for search indexing...");
                // allow time for search indexing
                await Task.Delay(15000);

                Console.WriteLine("We can also use our tags to search for streams, let's search for streams tagged with 'periodic':");

                var streams = await metadataService.GetStreamsAsync("periodic");

                foreach (var strm in streams)
                {
                    Console.WriteLine(strm.Id);
                }
                Console.WriteLine();

                // delete values
                Console.WriteLine("Deleting values from the SdsStream");

                // delete one event
                await dataService.RemoveValueAsync(stream.Id, 0);

                // delete all events
                await dataService.RemoveWindowValuesAsync(stream.Id, 1, 200);

                retrieved = await dataService.GetWindowValuesAsync <WaveData>(stream.Id, "0", "200");

                if (retrieved.ToList <WaveData>().Count == 0)
                {
                    Console.WriteLine("All values deleted successfully!");
                }
                Console.WriteLine();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            finally
            {
                Console.WriteLine("Cleaning up");
                // Delete the stream, types and views
                Console.WriteLine("Deleteing stream");
                await metadataService.DeleteStreamAsync(streamId);

                Console.WriteLine("Deleteing views");
                await metadataService.DeleteViewAsync(autoViewId);

                await metadataService.DeleteViewAsync(manualViewId);

                Console.WriteLine("Deleteing types");
                await metadataService.DeleteTypeAsync(typeId);

                await metadataService.DeleteTypeAsync(targetTypeId);

                await metadataService.DeleteTypeAsync(targetIntTypeId);


                Console.WriteLine("done");
                Console.ReadKey();
            }
        }
        public static async Task <bool> MainAsync(bool test = false)
        {
            IConfigurationBuilder builder = new ConfigurationBuilder()
                                            .SetBasePath(Directory.GetCurrentDirectory())
                                            .AddJsonFile("appsettings.json")
                                            .AddJsonFile("appsettings.test.json", optional: true);

            _configuration = builder.Build();

            // ==== Client constants ====
            var tenantId    = _configuration["TenantId"];
            var namespaceId = _configuration["NamespaceId"];
            var resource    = _configuration["Resource"];
            var clientId    = _configuration["ClientId"];
            var clientKey   = _configuration["ClientKey"];

            // ==== Metadata IDs ====
            string streamId          = "SampleStream";
            string streamIdSecondary = "SampleStream_Secondary";
            string streamIdCompound  = "SampleStream_Compound";

            string typeId             = "SampleType";
            string targetTypeId       = "SampleType_Target";
            string targetIntTypeId    = "SampleType_TargetInt";
            string autoStreamViewId   = "SampleAutoStreamView";
            string manualStreamViewId = "SampleManualStreamView";
            string compoundTypeId     = "SampleType_Compound";

            var uriResource = new Uri(resource);

            // Step 1
            // Get Sds Services to communicate with server
            AuthenticationHandler authenticationHandler = new AuthenticationHandler(uriResource, clientId, clientKey);

            SdsService sdsService      = new SdsService(new Uri(resource), null, HttpCompressionMethod.GZip, authenticationHandler);
            var        metadataService = sdsService.GetMetadataService(tenantId, namespaceId);
            var        dataService     = sdsService.GetDataService(tenantId, namespaceId);
            var        tableService    = sdsService.GetTableService(tenantId, namespaceId);

            Console.WriteLine(@"-------------------------------------------------------------");
            Console.WriteLine(@"  _________    .___           _______  ______________________");
            Console.WriteLine(@" /   _____/  __| _/______     \      \ \_   _____/\__    ___/");
            Console.WriteLine(@" \_____  \  / __ |/  ___/     /   |   \ |    __)_   |    |   ");
            Console.WriteLine(@" /        \/ /_/ |\___ \     /    |    \|        \  |    |   ");
            Console.WriteLine(@"/_______  /\____ /____  > /\ \____|__  /_______  /  |____|   ");
            Console.WriteLine(@"        \/      \/    \/  \/         \/        \/            ");
            Console.WriteLine(@"-------------------------------------------------------------");
            Console.WriteLine();
            Console.WriteLine($"SDS endpoint at {resource}");
            Console.WriteLine();

            try
            {
                // Step 2
                // create an SdsType
                Console.WriteLine("Creating an SdsType");
                SdsType type = SdsTypeBuilder.CreateSdsType <WaveData>();
                type.Id = typeId;
                type    = await metadataService.GetOrCreateTypeAsync(type).ConfigureAwait(false);

                // Step 3
                // create an SdsStream
                Console.WriteLine("Creating an SdsStream");
                var stream = new SdsStream
                {
                    Id          = streamId,
                    Name        = "Wave Data Sample",
                    TypeId      = type.Id,
                    Description = "This is a sample SdsStream for storing WaveData type measurements",
                };
                stream = await metadataService.GetOrCreateStreamAsync(stream).ConfigureAwait(false);

                // Step 4
                // insert data
                Console.WriteLine("Inserting data");

                // insert a single event
                var wave = GetWave(0, 2);
                await dataService.InsertValueAsync(stream.Id, wave).ConfigureAwait(false);

                // insert a list of events
                var waves = new List <WaveData>();
                for (var i = 2; i <= 18; i += 2)
                {
                    waves.Add(GetWave(i, 2));
                }

                await dataService.InsertValuesAsync(stream.Id, waves).ConfigureAwait(false);

                // Step 5
                // get last event
                Console.WriteLine("Getting latest event");
                var latest = await dataService.GetLastValueAsync <WaveData>(streamId).ConfigureAwait(false);

                Console.WriteLine(latest.ToString());
                Console.WriteLine();

                // get all events
                Console.WriteLine("Getting all events");
                var allEvents = (List <WaveData>) await dataService.GetWindowValuesAsync <WaveData>(streamId, "0", "180").ConfigureAwait(false);

                Console.WriteLine($"Total events found: {allEvents.Count}");
                foreach (var evnt in allEvents)
                {
                    Console.WriteLine(evnt.ToString());
                }

                Console.WriteLine();

                // Step 6
                // getting all events in table format with headers
                var tableEvents = await tableService.GetWindowValuesAsync(stream.Id, "0", "180").ConfigureAwait(false);

                Console.WriteLine("Getting table events");
                foreach (var evnt in tableEvents.Rows)
                {
                    Console.WriteLine(string.Join(",", evnt.ToArray()));
                }

                Console.WriteLine();

                // Step 7
                // update events
                Console.WriteLine("Updating events");

                // update one event
                var updatedWave = UpdateWave(allEvents.First(), 4);
                await dataService.UpdateValueAsync(stream.Id, updatedWave).ConfigureAwait(false);

                // update all events, adding ten more
                var updatedCollection = new List <WaveData>();
                for (int i = 2; i < 40; i += 2)
                {
                    updatedCollection.Add(GetWave(i, 4));
                }

                await dataService.UpdateValuesAsync(stream.Id, updatedCollection).ConfigureAwait(false);

                allEvents = (List <WaveData>) await dataService.GetWindowValuesAsync <WaveData>(stream.Id, "0", "180").ConfigureAwait(false);

                Console.WriteLine("Getting updated events");
                Console.WriteLine($"Total events found: {allEvents.Count}");

                foreach (var evnt in allEvents)
                {
                    Console.WriteLine(evnt.ToString());
                }

                Console.WriteLine();

                // Step 8
                // replacing events
                Console.WriteLine("Replacing events");

                // replace one event
                var replaceEvent = UpdateWave(allEvents.First(), multiplier: 5);

                await dataService.ReplaceValueAsync <WaveData>(streamId, replaceEvent).ConfigureAwait(false);

                // replace all events
                for (int i = 1; i < allEvents.Count; i++)
                {
                    allEvents[i] = UpdateWave(allEvents[i], multiplier: 5);
                }

                await dataService.ReplaceValuesAsync <WaveData>(streamId, allEvents).ConfigureAwait(false);

                Console.WriteLine("Getting replaced events");
                var replacedEvents = (List <WaveData>) await dataService.GetWindowValuesAsync <WaveData>(streamId, "0", "180").ConfigureAwait(false);

                Console.WriteLine($"Total events found: {replacedEvents.Count}");
                foreach (var evnt in replacedEvents)
                {
                    Console.WriteLine(evnt.ToString());
                }

                Console.WriteLine();

                // Step 9
                // Property Overrides
                Console.WriteLine("SDS can interpolate or extrapolate data at an index location where data does not explicitly exist:");
                Console.WriteLine();

                // We will retrieve three events using the default behavior, Continuous
                var retrieved = await dataService
                                .GetRangeValuesAsync <WaveData>(stream.Id, "1", 3, SdsBoundaryType.ExactOrCalculated).ConfigureAwait(false);

                Console.WriteLine("Default (Continuous) requesting data starting at index location '1', where we have not entered data, SDS will interpolate a value for this property and then return entered values:");
                foreach (var value in retrieved)
                {
                    Console.WriteLine(value.ToString());
                }

                Console.WriteLine();

                var retrievedInterpolated = await dataService
                                            .GetValuesAsync <WaveData>(stream.Id, "5", "32", 4).ConfigureAwait(false);

                Console.WriteLine("SDS will interpolate a value for each index asked for (5,14,23,32):");
                foreach (var value in retrievedInterpolated)
                {
                    Console.WriteLine(value.ToString());
                }

                Console.WriteLine();

                // Step 10
                // We will retrieve events filtered to only get the ones where the radians are less than 50.  Note, this can be done on index properties too.
                var retrievedInterpolatedFiltered = await dataService.GetWindowFilteredValuesAsync <WaveData>(stream.Id, "0", "180", SdsBoundaryType.ExactOrCalculated, "Radians lt 50").ConfigureAwait(false);

                Console.WriteLine("SDS will only return the values where the radians are less than 50:");
                foreach (var value in retrievedInterpolatedFiltered)
                {
                    Console.WriteLine(value.ToString());
                }

                Console.WriteLine();

                // Step 11
                // We will retrieve a sample of our data
                Console.WriteLine("SDS can return a sample of your data population to show trends.");
                Console.WriteLine("Getting Sampled Values:");
                var sampledValues = await dataService.GetSampledValuesAsync <WaveData>(stream.Id, "0",
                                                                                       "40", 4, new[] { nameof(WaveData.Sin) }).ConfigureAwait(false);

                foreach (var sample in sampledValues)
                {
                    Console.WriteLine(sample);
                }

                Console.WriteLine();

                // Step 12
                // create a Discrete stream PropertyOverride indicating that we do not want Sds to calculate a value for Radians and update our stream
                var propertyOverride = new SdsStreamPropertyOverride()
                {
                    SdsTypePropertyId = "Radians",
                    InterpolationMode = SdsInterpolationMode.Discrete,
                };
                var propertyOverrides = new List <SdsStreamPropertyOverride>()
                {
                    propertyOverride
                };

                // update the stream
                stream.PropertyOverrides = propertyOverrides;
                await metadataService.CreateOrUpdateStreamAsync(stream).ConfigureAwait(false);

                retrieved = await dataService
                            .GetRangeValuesAsync <WaveData>(stream.Id, "1", 3, SdsBoundaryType.ExactOrCalculated).ConfigureAwait(false);

                Console.WriteLine("We can override this behavior on a property by property basis, here we override the Radians property instructing Sds not to interpolate.");
                Console.WriteLine("SDS will now return the default value for the data type:");

                foreach (var value in retrieved)
                {
                    Console.WriteLine(value.ToString());
                }

                Console.WriteLine();

                // Step 13
                // StreamViews
                Console.WriteLine("SdsStreamViews");

                // create target types
                var targetType = SdsTypeBuilder.CreateSdsType <WaveDataTarget>();
                targetType.Id = targetTypeId;

                var targetIntType = SdsTypeBuilder.CreateSdsType <WaveDataInteger>();
                targetIntType.Id = targetIntTypeId;

                await metadataService.GetOrCreateTypeAsync(targetType).ConfigureAwait(false);

                await metadataService.GetOrCreateTypeAsync(targetIntType).ConfigureAwait(false);

                // create StreamViews
                var autoStreamView = new SdsStreamView()
                {
                    Id           = autoStreamViewId,
                    SourceTypeId = typeId,
                    TargetTypeId = targetTypeId,
                };

                // create explicit mappings
                var vp1 = new SdsStreamViewProperty()
                {
                    SourceId = "Order", TargetId = "OrderTarget"
                };
                var vp2 = new SdsStreamViewProperty()
                {
                    SourceId = "Sin", TargetId = "SinInt"
                };
                var vp3 = new SdsStreamViewProperty()
                {
                    SourceId = "Cos", TargetId = "CosInt"
                };
                var vp4 = new SdsStreamViewProperty()
                {
                    SourceId = "Tan", TargetId = "TanInt"
                };

                var manualStreamView = new SdsStreamView()
                {
                    Id           = manualStreamViewId,
                    SourceTypeId = typeId,
                    TargetTypeId = targetIntTypeId,
                    Properties   = new List <SdsStreamViewProperty>()
                    {
                        vp1, vp2, vp3, vp4
                    },
                };

                await metadataService.CreateOrUpdateStreamViewAsync(autoStreamView).ConfigureAwait(false);

                await metadataService.CreateOrUpdateStreamViewAsync(manualStreamView).ConfigureAwait(false);

                Console.WriteLine("Here is some of our data as it is stored on the server:");
                foreach (var evnt in retrieved)
                {
                    Console.WriteLine($"Sin: {evnt.Sin}, Cos: {evnt.Cos}, Tan {evnt.Tan}");
                }

                Console.WriteLine();

                // get autoStreamView data
                var autoStreamViewData = await dataService.GetRangeValuesAsync <WaveDataTarget>(stream.Id, "1", 3, SdsBoundaryType.ExactOrCalculated, autoStreamViewId).ConfigureAwait(false);

                Console.WriteLine("Specifying a StreamView with an SdsType of the same shape returns values that are automatically mapped to the target SdsType's properties:");

                foreach (var value in autoStreamViewData)
                {
                    Console.WriteLine($"SinTarget: {value.SinTarget} CosTarget: {value.CosTarget} TanTarget: {value.TanTarget}");
                }

                Console.WriteLine();

                // get manualStreamView data
                Console.WriteLine("SdsStreamViews can also convert certain types of data, here we return integers where the original values were doubles:");
                var manualStreamViewData = await dataService.GetRangeValuesAsync <WaveDataInteger>(stream.Id, "1", 3, SdsBoundaryType.ExactOrCalculated, manualStreamViewId).ConfigureAwait(false);

                foreach (var value in manualStreamViewData)
                {
                    Console.WriteLine($"SinInt: {value.SinInt} CosInt: {value.CosInt} TanInt: {value.TanInt}");
                }

                Console.WriteLine();

                // get SdsStreamViewMap
                Console.WriteLine("We can query SDS to return the SdsStreamViewMap for our SdsStreamView, here is the one generated automatically:");
                var autoStreamViewMap = await metadataService.GetStreamViewMapAsync(autoStreamViewId).ConfigureAwait(false);

                PrintStreamViewMapProperties(autoStreamViewMap);

                Console.WriteLine("Here is our explicit mapping, note SdsStreamViewMap will return all properties of the Source Type, even those without a corresponding Target property:");
                var manualStreamViewMap = await metadataService.GetStreamViewMapAsync(manualStreamViewId).ConfigureAwait(false);

                PrintStreamViewMapProperties(manualStreamViewMap);

                // Step 14
                // Update Stream Type based on SdsStreamView
                Console.WriteLine("We will now update the stream type based on the stream view");

                var firstVal = await dataService.GetFirstValueAsync <WaveData>(stream.Id).ConfigureAwait(false);

                await metadataService.UpdateStreamTypeAsync(stream.Id, autoStreamViewId).ConfigureAwait(false);

                var newStream = await metadataService.GetStreamAsync(stream.Id).ConfigureAwait(false);

                var firstValUpdated = await dataService.GetFirstValueAsync <WaveDataTarget>(stream.Id).ConfigureAwait(false);

                Console.WriteLine($"The new type id {newStream.TypeId} compared to the original one {stream.TypeId}.");
                Console.WriteLine($"The new type value {firstValUpdated} compared to the original one {firstVal}.");
                Console.WriteLine();

                // Step 15
                // Show filtering on Type, works the same as filtering on Streams
                var types = await metadataService.GetTypesAsync().ConfigureAwait(false);

                var typesFiltered = await metadataService.GetTypesAsync("Id:*Target*").ConfigureAwait(false);

                Console.WriteLine($"The number of types returned without filtering: {types.Count()}.  With filtering {typesFiltered.Count()}.");
                Console.WriteLine();

                // Step 16
                // tags and metadata
                Console.WriteLine("Let's add some Tags and Metadata to our stream:");
                var tags = new List <string> {
                    "waves", "periodic", "2018", "validated"
                };
                var metadata = new Dictionary <string, string>()
                {
                    { "Region", "North America" }, { "Country", "Canada" }, { "Province", "Quebec" }
                };

                await metadataService.UpdateStreamTagsAsync(streamId, tags).ConfigureAwait(false);

                await metadataService.UpdateStreamMetadataAsync(streamId, metadata).ConfigureAwait(false);

                tags = (List <string>) await metadataService.GetStreamTagsAsync(streamId).ConfigureAwait(false);

                Console.WriteLine();
                Console.WriteLine($"Tags now associated with {streamId}:");
                foreach (var tag in tags)
                {
                    Console.WriteLine(tag);
                }

                Console.WriteLine();
                Console.WriteLine($"Metadata now associated with {streamId}:");
                Console.WriteLine("Metadata key Region: " + await metadataService.GetStreamMetadataValueAsync(streamId, "Region").ConfigureAwait(false));
                Console.WriteLine("Metadata key Country: " + await metadataService.GetStreamMetadataValueAsync(streamId, "Country").ConfigureAwait(false));
                Console.WriteLine("Metadata key Province: " + await metadataService.GetStreamMetadataValueAsync(streamId, "Province").ConfigureAwait(false));
                Console.WriteLine();

                // Step 17
                // update metadata
                Console.WriteLine("Let's make some changes to the Metadata on our stream:");
                var patch = new MetadataPatchDocument();
                patch.Remove("Region");
                patch.Replace("Province", "Ontario");
                patch.Add("City", "Toronto");
                await metadataService.PatchStreamMetadataAsync(streamId, patch).ConfigureAwait(false);

                Console.WriteLine();
                Console.WriteLine($"Metadata now associated with {streamId}:");
                Console.WriteLine("Metadata key Country: " + await metadataService.GetStreamMetadataValueAsync(streamId, "Country").ConfigureAwait(false));
                Console.WriteLine("Metadata key Province: " + await metadataService.GetStreamMetadataValueAsync(streamId, "Province").ConfigureAwait(false));
                Console.WriteLine("Metadata key City: " + await metadataService.GetStreamMetadataValueAsync(streamId, "City").ConfigureAwait(false));
                Console.WriteLine();

                // Step 18
                // delete values
                Console.WriteLine("Deleting values from the SdsStream");

                // delete one event
                await dataService.RemoveValueAsync(stream.Id, 0).ConfigureAwait(false);

                // delete all events
                await dataService.RemoveWindowValuesAsync(stream.Id, 1, 200).ConfigureAwait(false);

                retrieved = await dataService.GetWindowValuesAsync <WaveData>(stream.Id, "0", "200").ConfigureAwait(false);

                if (retrieved.ToList().Count == 0)
                {
                    Console.WriteLine("All values deleted successfully!");
                }

                Console.WriteLine();

                // Step 19
                // Adding a new stream with a secondary index.
                Console.WriteLine("Adding a stream with a secondary index.");

                SdsStreamIndex measurementIndex = new SdsStreamIndex()
                {
                    SdsTypePropertyId = type.Properties.First(p => p.Id.Equals("Radians", StringComparison.OrdinalIgnoreCase)).Id,
                };

                SdsStream secondary = new SdsStream()
                {
                    Id      = streamIdSecondary,
                    TypeId  = type.Id,
                    Indexes = new List <SdsStreamIndex>()
                    {
                        measurementIndex,
                    },
                };

                secondary = await metadataService.GetOrCreateStreamAsync(secondary).ConfigureAwait(false);

                Console.WriteLine($"Secondary indexes on streams. {stream.Id}:{stream.Indexes?.Count}. {secondary.Id}:{secondary.Indexes.Count}. ");
                Console.WriteLine();

                // Modifying an existing stream with a secondary index.
                Console.WriteLine("Modifying a stream to have a secondary index.");

                stream = await metadataService.GetStreamAsync(stream.Id).ConfigureAwait(false);

                type = await metadataService.GetTypeAsync(stream.TypeId).ConfigureAwait(false);

                SdsStreamIndex measurementTargetIndex = new SdsStreamIndex()
                {
                    SdsTypePropertyId = type.Properties.First(p => p.Id.Equals("RadiansTarget", StringComparison.OrdinalIgnoreCase)).Id,
                };
                stream.Indexes = new List <SdsStreamIndex>()
                {
                    measurementTargetIndex
                };

                await metadataService.CreateOrUpdateStreamAsync(stream).ConfigureAwait(false);

                stream = await metadataService.GetStreamAsync(stream.Id).ConfigureAwait(false);

                // Modifying an existing stream to remove the secondary index
                Console.WriteLine("Removing a secondary index from a stream.");

                secondary.Indexes = null;
                await metadataService.CreateOrUpdateStreamAsync(secondary).ConfigureAwait(false);

                secondary = await metadataService.GetStreamAsync(secondary.Id).ConfigureAwait(false);

                Console.WriteLine($"Secondary indexes on streams. {stream.Id}:{stream.Indexes?.Count}. {secondary.Id}:{secondary.Indexes?.Count}. ");
                Console.WriteLine();

                // Step 20
                // Adding Compound Index Type
                Console.WriteLine("Creating an SdsType with a compound index");
                SdsType typeCompound = SdsTypeBuilder.CreateSdsType <WaveDataCompound>();
                typeCompound.Id = compoundTypeId;
                typeCompound    = await metadataService.GetOrCreateTypeAsync(typeCompound).ConfigureAwait(false);

                // create an SdsStream
                Console.WriteLine("Creating an SdsStream off of type with compound index");
                var streamCompound = new SdsStream
                {
                    Id          = streamIdCompound,
                    Name        = "Wave Data Sample",
                    TypeId      = typeCompound.Id,
                    Description = "This is a sample SdsStream for storing WaveData type measurements",
                };
                streamCompound = await metadataService.GetOrCreateStreamAsync(streamCompound).ConfigureAwait(false);

                // Step 21
                // insert compound data
                Console.WriteLine("Inserting data");
                await dataService.InsertValueAsync(streamCompound.Id, GetWaveMultiplier(1, 10)).ConfigureAwait(false);

                await dataService.InsertValueAsync(streamCompound.Id, GetWaveMultiplier(2, 2)).ConfigureAwait(false);

                await dataService.InsertValueAsync(streamCompound.Id, GetWaveMultiplier(3, 1)).ConfigureAwait(false);

                await dataService.InsertValueAsync(streamCompound.Id, GetWaveMultiplier(10, 3)).ConfigureAwait(false);

                await dataService.InsertValueAsync(streamCompound.Id, GetWaveMultiplier(10, 8)).ConfigureAwait(false);

                await dataService.InsertValueAsync(streamCompound.Id, GetWaveMultiplier(10, 10)).ConfigureAwait(false);

                var latestCompound = await dataService.GetLastValueAsync <WaveDataCompound>(streamCompound.Id).ConfigureAwait(false);

                var firstCompound = await dataService.GetFirstValueAsync <WaveDataCompound>(streamCompound.Id).ConfigureAwait(false);

                var data = await dataService.GetWindowValuesAsync <WaveDataCompound, int, int>(streamCompound.Id, Tuple.Create(2, 1), Tuple.Create(10, 8)).ConfigureAwait(false);

                Console.WriteLine($"First data: {firstCompound}.  Latest data: {latestCompound}.");

                Console.WriteLine();

                Console.WriteLine("Window Data:");

                foreach (var evnt in data)
                {
                    Console.WriteLine(evnt.ToString());
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                _toThrow = ex;
            }
            finally
            {
                // Step 22
                Console.WriteLine();
                Console.WriteLine("Cleaning up");

                // Delete the stream, types and streamViews making sure
                Console.WriteLine("Deleting stream");
                await RunInTryCatch(metadataService.DeleteStreamAsync, streamId).ConfigureAwait(false);
                await RunInTryCatch(metadataService.DeleteStreamAsync, streamIdSecondary).ConfigureAwait(false);
                await RunInTryCatch(metadataService.DeleteStreamAsync, streamIdCompound).ConfigureAwait(false);

                Console.WriteLine("Deleting stream views");
                await RunInTryCatch(metadataService.DeleteStreamViewAsync, autoStreamViewId).ConfigureAwait(false);
                await RunInTryCatch(metadataService.DeleteStreamViewAsync, manualStreamViewId).ConfigureAwait(false);

                Console.WriteLine("Deleting types");
                await RunInTryCatch(metadataService.DeleteTypeAsync, typeId).ConfigureAwait(false);
                await RunInTryCatch(metadataService.DeleteTypeAsync, compoundTypeId).ConfigureAwait(false);
                await RunInTryCatch(metadataService.DeleteTypeAsync, targetTypeId).ConfigureAwait(false);
                await RunInTryCatch(metadataService.DeleteTypeAsync, targetIntTypeId).ConfigureAwait(false);

                Console.WriteLine("Complete!");
                if (!test)
                {
                    Console.ReadKey();
                }
            }

            if (test && _toThrow != null)
            {
                throw _toThrow;
            }
            return(_toThrow == null);
        }
        public static async Task <bool> MainAsync(bool test = false)
        {
            IConfigurationBuilder builder = new ConfigurationBuilder()
                                            .SetBasePath(Directory.GetCurrentDirectory())
                                            .AddJsonFile("appsettings.json")
                                            .AddJsonFile("appsettings.test.json", optional: true);
            IConfiguration configuration = builder.Build();

            string tenantId    = configuration["TenantId"];
            string namespaceId = configuration["NamespaceId"];
            string resource    = configuration["Resource"];
            string clientId    = configuration["ClientId"];
            string clientKey   = configuration["ClientKey"];
            string apiVersion  = configuration["ApiVersion"];

            string ResourcePrefix = "UomSample";
            string TypeId         = $"{ResourcePrefix} Uom";
            string StreamWithPropertyOverriden    = $"{ResourcePrefix} UomPropertyOverridden";
            string StreamWithoutPropertyOverriden = $"{ResourcePrefix} UomNoPropertyOverridden";

            // Step 1
            AuthenticationHandler authenticationHandler = new AuthenticationHandler(new Uri(resource), clientId, clientKey);
            SdsService            service = new SdsService(new Uri(resource), authenticationHandler);

            ISdsMetadataService MetadataService = service.GetMetadataService(tenantId, namespaceId);
            ISdsDataService     DataService     = service.GetDataService(tenantId, namespaceId);

            try
            {
                /*
                 * The following code provides an implementation for getting all the SdsUom ID's for each quantity.
                 * If you are not aware with which SdsUom ID to use, you can uncomment the below code and find out the
                 * uom id.
                 *
                 * e.g. I am using degree_fahrenheit and  degree_celsius UOMS for temperature SdsQuantity.
                 *
                 *
                 * IEnumerable<SdsUomQuantity> sdsUomQuantities = await MetadataService.GetQuantitiesAsync();
                 * IEnumerable<SdsUom> sdsUoms = null;
                 *
                 * foreach (SdsUomQuantity sdsUomQuantity in sdsUomQuantities)
                 * {
                 *  sdsUoms = await MetadataService.GetQuantityUomsAsync(sdsUomQuantity.Id);
                 *
                 *  foreach (SdsUom sdsUom in sdsUoms)
                 *  {
                 *      Console.WriteLine(sdsUom.Id);
                 *  }
                 * }
                 */

                // Step 2
                // Creating a Sdstype
                SdsType sdsType = SdsTypeBuilder.CreateSdsType <Widget>();
                sdsType.Id = TypeId;

                sdsType = await MetadataService.GetOrCreateTypeAsync(sdsType);

                // Step 3
                //Creating a Stream overriding the distance property.
                SdsStream sdsStreamOne = new SdsStream()
                {
                    Id                = StreamWithPropertyOverriden,
                    TypeId            = TypeId,
                    Name              = "UomStreamSourceWithPropertyOverridden",
                    PropertyOverrides = new List <SdsStreamPropertyOverride>()
                };

                //Overriding the UOM of the distance property to be kilometer instead of mile.
                sdsStreamOne.PropertyOverrides.Add(new SdsStreamPropertyOverride()
                {
                    Uom = "kilometer",
                    SdsTypePropertyId = "Distance"
                });

                sdsStreamOne = await MetadataService.GetOrCreateStreamAsync(sdsStreamOne);

                // Step 4
                //Creating a Stream without overriding properties.
                SdsStream sdsStreamTwo = new SdsStream()
                {
                    Id     = StreamWithoutPropertyOverriden,
                    TypeId = TypeId,
                    Name   = "UomStreamSourceWithNoPropertyOverridden",
                };

                sdsStreamTwo = await MetadataService.GetOrCreateStreamAsync(sdsStreamTwo);

                // Step 5
                // Generating data
                IList <Widget> data = new List <Widget>();
                for (int i = 0; i < 10; i++)
                {
                    Widget widget = new Widget();
                    widget.Time        = DateTime.UtcNow.AddSeconds(i);
                    widget.Temperature = Random.Next(1, 100);
                    widget.Distance    = Random.Next(1, 100);
                    data.Add(widget);
                }

                /* In stream one, the temperature value will be inserted as Fahrenheit since we have defined the
                 * default uom as Fahrenheit for Temperature in the Widget class. The distance value will be
                 * inserted as kilometer, as we have overridden the Distance property in stream one,
                 * regardless of the default uom for Distance in the Widget class.
                 */
                await DataService.InsertValuesAsync <Widget>(sdsStreamOne.Id, data);

                /* In stream two, the temperature value will be inserted as Fahrenheit and the distance will be inserted as mile.
                 *
                 */
                await DataService.InsertValuesAsync <Widget>(sdsStreamTwo.Id, data);

                // Step 6

                /*
                 * The last value stored in stream one.
                 */
                Widget widgetFromStreamOne = await DataService.GetLastValueAsync <Widget>(sdsStreamOne.Id);

                Console.WriteLine($"In stream one, the distance is {widgetFromStreamOne.Distance} kilometers and the temperature is {widgetFromStreamOne.Temperature} degrees fahrenheit");
                Console.WriteLine();

                /*
                 * The last value stored in stream two.
                 */
                Widget widgetFromStreamTwo = await DataService.GetLastValueAsync <Widget>(sdsStreamTwo.Id);

                Console.WriteLine($"In stream two, the distance is {widgetFromStreamTwo.Distance} miles and the temperature is {widgetFromStreamTwo.Temperature} degrees fahrenheit");
                Console.WriteLine();

                // Step 7

                /*
                 * If you want your data to be in specified uom, you can override your properties while making a call.
                 * In the following, I want the temperature to be in Celsius, and the distance to be in feet.
                 *
                 * Then you can pass IList<SdsStreamPropertyOverride> to DataService while getting values.
                 *
                 */
                IList <SdsStreamPropertyOverride> requestOverrides = new List <SdsStreamPropertyOverride>();

                requestOverrides.Add(new SdsStreamPropertyOverride()
                {
                    Uom = "degree celsius",
                    SdsTypePropertyId = "Temperature"
                });

                requestOverrides.Add(new SdsStreamPropertyOverride()
                {
                    Uom = "foot",
                    SdsTypePropertyId = "Distance"
                });

                /*
                 * In the following call, data will be converted from Fahrenheit to Celsius for the temperature property,
                 * and from kilometer to foot for the distance property.
                 *
                 * Uoms in Stream one (Temperature : Fahrenheit, Distance : Kilometer)
                 *
                 */
                widgetFromStreamOne = await DataService.GetLastValueAsync <Widget>(sdsStreamOne.Id, requestOverrides);

                Console.WriteLine($"In stream one, the distance is {widgetFromStreamOne.Distance} foot and the temperature is {widgetFromStreamOne.Temperature} degrees celsius");
                Console.WriteLine();

                /*
                 * In the following call, data will be converted from Fahrenheit to Celsius for the temperature property,
                 * and from mile to foot for the distance property.
                 *
                 * Uoms in Stream two (Temperature : Fahrenheit, Distance : Mile)
                 *
                 */
                widgetFromStreamTwo = await DataService.GetLastValueAsync <Widget>(sdsStreamTwo.Id, requestOverrides);

                Console.WriteLine($"In stream two, the distance is {widgetFromStreamTwo.Distance} foot and the temperature is {widgetFromStreamTwo.Temperature} degrees celsius");
                Console.WriteLine();
            }
            catch (Exception ex)
            {
                success = false;
                Console.WriteLine(ex.Message);
                toThrow = ex;
            }
            finally
            {
                // Step 8
                Console.WriteLine("Deleting");
                RunInTryCatch(MetadataService.DeleteStreamAsync, StreamWithPropertyOverriden);
                RunInTryCatch(MetadataService.DeleteStreamAsync, StreamWithoutPropertyOverriden);
                RunInTryCatch(MetadataService.DeleteTypeAsync, TypeId);
                if (!test)
                {
                    Console.ReadLine();
                }
            }

            if (test && !success)
            {
                throw toThrow;
            }
            return(success);
        }