/// <param name="baseAddress">The OSIsoft Cloud Services HTTP endpoint.</param> /// <param name="accountId">The Guid account Id.</param> public IngressManagementClient(string baseAddress, string accountId, SdsSecurityHandler sdsSecurityHandler) { _accountId = accountId; sdsSecurityHandler.InnerHandler = new HttpClientHandler(); _client = new HttpClient(sdsSecurityHandler); _client.BaseAddress = new Uri(baseAddress); }
static int Main(string[] args) { var options = new Options(); var errors = new List <Error>(); var result = Parser.Default.ParseArguments <Options>(args); result.WithParsed(opts => options = opts).WithNotParsed(errs => errors = errs.ToList()); if (errors.Any()) { foreach (var error in errors) { Console.WriteLine(error.Tag); } return(1); } NameValueCollection appSettings = ConfigurationManager.AppSettings; string accountId = appSettings["accountId"]; string namespaceId = appSettings["namespaceId"]; string clusterAddress = appSettings["address"]; string ingressServiceUrl = clusterAddress + @"/api/omf"; // Use a client secret, retrieved from the OSIsoft Cloud Services portal for your account, to // create a SecurityHandler used to authenticate this app. string resource = appSettings["resource"]; string clientId = appSettings["clientId"]; string clientSecret = appSettings["clientSecret"]; var securityHandler = new SdsSecurityHandler(resource, accountId, clientId, clientSecret); // Create a client to manage OSIsoft Cloud Services Ingress resources. using (var managementClient = new IngressManagementClient(clusterAddress, accountId, securityHandler)) { // Connect to a PI server and select PI points for which to move data to OCS. var piServerName = appSettings["PIDataArchive"]; var piServer = new PIServers()[piServerName]; var points = PIPoint.FindPIPoints(piServer, options.TagMask).ToList(); if (!points.Any()) { Console.WriteLine($"No PI points found matching the tagMask query!"); return(1); } // Create OCS data ingress objects. string publisherName = appSettings["publisherName"]; string topicName = appSettings["topicName"]; string subscriptionName = appSettings["subscriptionName"]; Console.WriteLine("Setting up OSIsoft Cloud Services OMF ingress objects."); string publisherId = managementClient.GetOrCreatePublisherAsync(publisherName).GetAwaiter().GetResult(); string producerToken = managementClient.GetOrCreateToken(publisherId).GetAwaiter().GetResult(); string topicId = managementClient.GetOrCreateTopic(topicName, publisherId).GetAwaiter().GetResult(); string subscriptionId = managementClient.GetOrCreateSubscription(subscriptionName, topicId, namespaceId).GetAwaiter().GetResult(); // Each PI point type will be written to an OSIsoft Cloud Services(OCS) SDSStream. // The structure of each stream is defined by an OCS SDSType. We create this SDSType // by posting an OSIsoft Message Format(OMF) type message to OCS. // PI point value types need to translate to OCS SDSTypes. We create a limited number // of SDSTypes in OCS and then map PI point value types to those SDSTypes. // A mapping between PI point value types and the Ids of the SDSType that represents // them in OCS is shown below. Dictionary <OmfTypeCode, string> typeIdsByOmfType = new Dictionary <OmfTypeCode, string>(); typeIdsByOmfType.Add(OmfTypeCode.Number, "numberValueAndTimestamp"); typeIdsByOmfType.Add(OmfTypeCode.Integer, "integerValueAndTimestamp"); typeIdsByOmfType.Add(OmfTypeCode.String, "stringValueAndTimestamp"); typeIdsByOmfType.Add(OmfTypeCode.Time, "timeValueAndTimestamp"); typeIdsByOmfType.Add(OmfTypeCode.ByteArray, "byteArrayValueAndTimestamp"); using (var client = new IngressClient(ingressServiceUrl, producerToken) { UseCompression = true }) { // Create and send OMF Type messages. Console.WriteLine("Creating basic types in OCS to represent the format of PI points."); List <OmfType> types = GetOmfTypes(typeIdsByOmfType); var omfTypeMessageContent = new OmfTypeMessageContent() { Types = types }; client.SendMessageAsync(omfTypeMessageContent.ToByteArray(), MessageType.Type, MessageAction.Create).GetAwaiter().GetResult(); // Generate containers for each of the point with the correct OMF message type. List <OmfContainer> containers = GetOmfContainers(points, typeIdsByOmfType); if (options.WriteMode == Options.DataWriteMode.clearExistingData) { // Deleting the OMF container deletes the underlying SDSStream and its data. Console.WriteLine("Deleting OMF containers corresponding to the selected PI points that existed before the sample was run."); var omfContainerMessageContent = new OmfContainerMessageContent() { Containers = containers }; client.SendMessageAsync(omfContainerMessageContent.ToByteArray(), MessageType.Container, MessageAction.Delete).GetAwaiter().GetResult(); } Console.WriteLine("Creating corresponding containers for the PI points whose data will be written to OCS."); // OSIsoft Cloud Services' OMF Ingress sets a size limit on the request accepted by its external endpoint. We may need to split, or chunk, // containers into multiple OMF messages sent to the endpoint. for (int chunkStartIndex = 0; chunkStartIndex < containers.Count; chunkStartIndex += MaxChunkSize) { int numberOfContainersToSendInThisChunk = Math.Min(containers.Count - chunkStartIndex, MaxChunkSize); var containersToSendInThisChunk = containers.GetRange(chunkStartIndex, numberOfContainersToSendInThisChunk).ToList(); var omfContainerMessageContent = new OmfContainerMessageContent() { Containers = containersToSendInThisChunk }; client.SendMessageAsync(omfContainerMessageContent.ToByteArray(), MessageType.Container, MessageAction.Create).GetAwaiter().GetResult(); } // Write data from each PI point to a SDSStream. foreach (PIPoint point in points) { Console.WriteLine($"Writing PI point data for point {point.Name} to OCS."); string containerId = GetContainerId(point); AFValues values = point.RecordedValues(new AFTimeRange(options.StartTime, options.EndTime), AFBoundaryType.Inside, null, true); // OSIsoft Cloud Services' OMF Ingress sets a size limit on the request accepted by its external endpoint. We may need to split, or chunk, // events into multiple OMF messages sent to the endpoint. for (int chunkStartIndex = 0; chunkStartIndex < values.Count; chunkStartIndex += MaxChunkSize) { int numberOfEventsToReadForThisChunk = Math.Min(values.Count - chunkStartIndex, MaxChunkSize); // If there are multiple events at a single timestamp for the PI point, the most recently added event will be written to OCS. List <AFValue> distinctValuesInChunk = values.GetRange(chunkStartIndex, numberOfEventsToReadForThisChunk).GroupBy(value => value.Timestamp).Select(valuesAtTimestamp => valuesAtTimestamp.Last()).ToList(); List <PIData> piDataEvents = GetPIData(distinctValuesInChunk, ToOmfTypeCode(point.PointType)); OmfDataMessageContent omfDataMessageContent = new OmfDataMessageContent(containerId, piDataEvents); Console.WriteLine($"Sending PI point data from index {distinctValuesInChunk.First().Timestamp} to index {distinctValuesInChunk.Last().Timestamp} to OCS ({distinctValuesInChunk.Count} values)."); client.SendMessageAsync(omfDataMessageContent.ToByteArray(), MessageType.Data, MessageAction.Create).GetAwaiter().GetResult(); } } } // Delete OCS data ingress objects. if (options.DeleteIngressObjects) { Console.WriteLine($"Deleting subscription with Id {subscriptionId}."); managementClient.DeleteSubscription(subscriptionId).GetAwaiter().GetResult(); Console.WriteLine($"Deleting topic with Id {topicId}."); managementClient.DeleteTopicAsync(topicId).GetAwaiter().GetResult(); Console.WriteLine($"Deleting publisher with Id {publisherId}."); managementClient.DeletePublisherAsync(publisherId).GetAwaiter().GetResult(); } } return(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 }
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(); } }
static async Task MainAsync() { string resourcePrefix = ConfigurationManager.AppSettings["ResourcePrefix"]; string tenantId = ConfigurationManager.AppSettings["Tenant"]; string namespaceId = ConfigurationManager.AppSettings["Namespace"]; string address = ConfigurationManager.AppSettings["Address"]; string resource = ConfigurationManager.AppSettings["Resource"]; string appId = ConfigurationManager.AppSettings["ClientId"]; string appKey = ConfigurationManager.AppSettings["ClientSecret"]; // Acquire OSIsoft provided SDS clients w/ appropriate security delegating handler SdsSecurityHandler securityHandler = new SdsSecurityHandler(resource, tenantId, appId, appKey); SdsService service = new SdsService(new Uri(address), securityHandler); ISdsMetadataService config = service.GetMetadataService(tenantId, namespaceId); // Acquire an HTTP Client, including the Azure Active Directory client credential authentication handler // and associated pipeline. // // The authentication Handler performs the same function as the SDS Security Handler above. It acquires // a token from Azure Active Directory and attaches it as a header to every request sent out of the client. // // An HttpClient pipeline has to end with a handler that can send requests to appropriate endpoints. // HttpClientHandler is that appropriate handler; it is the end of the client pipeline. Note that we // do not allow auto - reidrects.We do so because the HttpClientHandler, like man clients, strips security // related headers on redirect. This results in 401 - unauthorized status indicating that the request // failed to authenticate. We will manage redirects manually. // // We take some shortcuts with disposable objects. Because the program ends quickly, we don't bother // cleaning up disposables. ApplicationAuthenticationHandler authenticationHandler = new ApplicationAuthenticationHandler(resource, tenantId, "https://login.windows.net/{0}/oauth2/token", appId, appKey) { InnerHandler = new HttpClientHandler { AllowAutoRedirect = false, UseCookies = false } }; HttpClient httpClient = new HttpClient(authenticationHandler) { BaseAddress = new Uri($"{address}/api/Tenants/{tenantId}/Namespaces/{namespaceId}/") }; try { // As we encounter performance counter categories, we will create a bunch of funcs to update individual events // If we were concerned aobut performance, we would cache events and update in bulk List <Func <Task> > updates = new List <Func <Task> >(); foreach (string categoryName in Categories) { PerformanceCounterCategory category = PerformanceCounterCategory.GetCategories() .First(cat => cat.CategoryName == categoryName); // Interrogate the category and create an SdsType based on the counters. This // strategy will not work for categories with instances that have different // performance counters, like Process. For those we would need a different strategy. SdsType type = new SdsType() { Id = $"{resourcePrefix}{category.CategoryName}", Name = $"{category.CategoryName}", SdsTypeCode = SdsTypeCode.Object, Properties = new List <SdsTypeProperty>() }; type.Properties.Add(new SdsTypeProperty() { Id = "Time", Name = "Time", IsKey = true, SdsType = new SdsType() { SdsTypeCode = SdsTypeCode.DateTime } }); PerformanceCounter[] counters; switch (category.CategoryType) { case PerformanceCounterCategoryType.MultiInstance: counters = category.GetCounters(category.GetInstanceNames().First()); break; case PerformanceCounterCategoryType.SingleInstance: counters = category.GetCounters(); break; default: throw new InvalidOperationException($"Invalid counter category type, {category.CategoryName}"); } foreach (PerformanceCounter counter in counters) { // Note that the identifier name is cleaned. Counters often use characters incompatible with SDS identifiers. type.Properties.Add(new SdsTypeProperty() { Id = CleanIdentifier(counter.CounterName), Name = counter.CounterName, SdsType = new SdsType() { SdsTypeCode = SdsTypeCode.Int64 } }); } type = await config.GetOrCreateTypeAsync(type); // Create a stream and an update func for each instance in the category switch (category.CategoryType) { case PerformanceCounterCategoryType.MultiInstance: foreach (string instanceName in category.GetInstanceNames()) { SdsStream stream = new SdsStream() { Id = $"{resourcePrefix}{category.CategoryName}-{instanceName}", Name = $"{category.CategoryName} {instanceName}", TypeId = type.Id }; stream = await config.GetOrCreateStreamAsync(stream); counters = category.GetCounters(instanceName); updates.Add(GetUpdate(httpClient, counters, stream)); } break; case PerformanceCounterCategoryType.SingleInstance: { SdsStream stream = new SdsStream() { Id = $"{category.CategoryName}", Name = $"{category.CategoryName}", TypeId = type.Id }; stream = await config.GetOrCreateStreamAsync(stream); counters = category.GetCounters(); updates.Add(GetUpdate(httpClient, counters, stream)); } break; } } DateTime start = DateTime.UtcNow; for (int i = 0; i < 10; i++) { await Task.Delay(TimeSpan.FromSeconds(10)); List <Task> tasks = new List <Task>(); foreach (Func <Task> update in updates) { tasks.Add(update()); } await Task.WhenAll(tasks); } DateTime end = DateTime.UtcNow; Console.WriteLine(); foreach (string categoryName in Categories) { PerformanceCounterCategory category = PerformanceCounterCategory.GetCategories() .FirstOrDefault(cat => cat.CategoryName == categoryName); foreach (string instanceName in category.GetInstanceNames()) { string streamId = $"{category.CategoryName}-{instanceName}"; HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, $"Streams/{streamId}/Data/GetWindowValues?startIndex={start.ToString("o")}&endIndex={end.ToString("o")}"); HttpResponseMessage response = await httpClient.SendAsync(request); if (response.IsSuccessStatusCode) { Console.WriteLine(); Console.WriteLine(); Console.WriteLine(streamId); string json = await response.Content.ReadAsStringAsync(); Dictionary <string, string>[] values = JsonConvert.DeserializeObject <Dictionary <string, string>[]>(json); foreach (Dictionary <string, string> value in values) { Console.Write("\t"); foreach (KeyValuePair <string, string> property in value) { Console.Write($"{property.Key}:{property.Value}, "); } Console.WriteLine(); } } else { Console.WriteLine($"Failed retrieving data from {streamId}, {response.ReasonPhrase}:{response.StatusCode}"); if (response.Content != null && response.Content.Headers.ContentLength > 0) { Console.WriteLine(await response.Content.ReadAsStringAsync()); } } } } Console.WriteLine(); } catch (Exception e) { Console.WriteLine(e); } finally { //TODO can we search for types IEnumerable <string> typeIds = from t in (await config.GetTypesAsync()) where Categories.Any(cat => t.Id.StartsWith(cat)) select t.Id; foreach (string typeId in typeIds) { int offset = 0; //TODO actually search for streams w/ the matching type IEnumerable <SdsStream> streams = await config.GetStreamsAsync(skip : offset, count : 10000); while (streams.Count() > 0) { offset += streams.Count(); try { IEnumerable <string> streamIds = from s in streams where s.TypeId.Equals(typeId, StringComparison.InvariantCultureIgnoreCase) select s.Id; foreach (string streamId in streamIds) { await config.DeleteStreamAsync(streamId); --offset; } } catch (Exception e) { Console.WriteLine(e); } streams = await config.GetStreamsAsync(skip : offset, count : 100); } try { await config.DeleteTypeAsync(typeId); } catch (Exception e) { Console.WriteLine(e); } } } }