/// <param name="baseAddress">The OSIsoft Cloud Services HTTP endpoint.</param>
 /// <param name="accountId">The Guid account Id.</param>
 public IngressManagementClient(string baseAddress, string accountId, QiSecurityHandler qiSecurityHandler)
 {
     _accountId = accountId;
     qiSecurityHandler.InnerHandler = new HttpClientHandler();
     _client             = new HttpClient(qiSecurityHandler);
     _client.BaseAddress = new Uri(baseAddress);
 }
Ejemplo n.º 2
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 behaviorId      = "SampleBehavior";
            string targetTypeId    = "SampleType_Target";
            string targetIntTypeId = "SampleType_TargetInt";
            string autoViewId      = "SampleAutoView";
            string manualViewId    = "SampleManualView";

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

            QiService qiService       = new QiService(new Uri(address), securityHandler);
            var       metadataService = qiService.GetMetadataService(tenantId, namespaceId);
            var       dataService     = qiService.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($"Qi endpoint at {address}");
            Console.WriteLine();

            try
            {
                // create a QiType
                Console.WriteLine("Creating a QiType");
                QiType type = QiTypeBuilder.CreateQiType <WaveData>();
                type.Id = typeId;
                type    = await metadataService.GetOrCreateTypeAsync(type);

                // create a QiStream
                Console.WriteLine("Creating a QiStream");
                var stream = new QiStream
                {
                    Id          = streamId,
                    Name        = "Wave Data Sample",
                    TypeId      = type.Id,
                    Description = "This is a sample QiStream 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();

                // Stream behaviors
                Console.WriteLine("QiStreamBehaviors determine whether Qi interpolates or extrapolates data at the requested index location");
                Console.WriteLine();

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

                Console.WriteLine("Default (Continuous) stream behavior, requesting data starting at index location '1', Qi will interpolate this value:");
                foreach (var value in retrieved)
                {
                    Console.WriteLine($"Order: {value.Order}, Radians: {value.Radians}");
                }
                Console.WriteLine();

                // create a Discrete stream behavior
                var behavior = new QiStreamBehavior
                {
                    Id   = behaviorId,
                    Mode = QiStreamMode.Discrete
                };
                behavior = await metadataService.GetOrCreateBehaviorAsync(behavior);

                // update the stream
                stream.BehaviorId = behavior.Id;
                await metadataService.CreateOrUpdateStreamAsync(stream);

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

                Console.WriteLine("Discrete stream behavior, Qi does not interpolate and returns the data starting at the next index location containing data:");
                foreach (var value in retrieved)
                {
                    Console.WriteLine($"Order: {value.Order}, Radians: {value.Radians}");
                }
                Console.WriteLine();

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

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

                var targetIntType = QiTypeBuilder.CreateQiType <WaveDataInteger>();
                targetIntType.Id = targetIntTypeId;

                await metadataService.CreateOrUpdateTypeAsync(targetType);

                await metadataService.CreateOrUpdateTypeAsync(targetIntType);

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

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

                var manualView = new QiView()
                {
                    Id           = manualViewId,
                    SourceTypeId = typeId,
                    TargetTypeId = targetIntTypeId,
                    Properties   = new List <QiViewProperty>()
                    {
                        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, QiBoundaryType.ExactOrCalculated, autoViewId);

                Console.WriteLine("Specifying a view with a QiType of the same shape returns values that are automatically mapped to the target QiType'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("QiViews 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, QiBoundaryType.ExactOrCalculated, manualViewId);

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

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

                PrintViewMapProperties(autoViewMap);

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

                PrintViewMapProperties(manualViewMap);

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

                // 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, views and behavior
                Console.WriteLine("Deleteing stream");
                await metadataService.DeleteStreamAsync(streamId);

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

                await metadataService.DeleteTypeAsync(targetTypeId);

                await metadataService.DeleteTypeAsync(targetIntTypeId);

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

                await metadataService.DeleteViewAsync(manualViewId);

                Console.WriteLine("Deleteing behavior");
                await metadataService.DeleteBehaviorAsync(behaviorId);

                Console.WriteLine("done");
                Console.ReadKey();
            }
        }
Ejemplo n.º 3
0
        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 QiSecurityHandler(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) QiStream.
                // The structure of each stream is defined by an OCS QiType. We create this QiType
                // by posting an OSIsoft Message Format(OMF) type message to OCS.
                // PI point value types need to translate to OCS QiTypes. We create a limited number
                // of QiTypes in OCS and then map PI point value types to those QiTypes.
                // A mapping between PI point value types and the Ids of the QiType 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 QiStream 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 QiStream.
                    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);
        }
Ejemplo n.º 4
0
        /// <summary>
        ///     This will return a QiSecurityHandler using the values held in Constants
        /// </summary>
        /// <returns></returns>
        protected static QiSecurityHandler GetQiSecurityHandler()
        {
            QiSecurityHandler qiSecurityHandler = new QiSecurityHandler(Constants.SecurityResource, Constants.TenantId, Constants.SecurityAppId, Constants.SecurityAppKey);

            return qiSecurityHandler;
        }