/// <summary>
        /// Helper to wait for all tasks in the list.
        /// </summary>
        /// <param name="what">Text for Trace Information</param>
        /// <param name="tasks">List of tasks</param>
        /// <param name="stopWatch">Stop watch of tasks</param>
        public static async Task WhenAllTasks(string what, List <Task> tasks, Stopwatch stopWatch)
        {
            // wait for all query tasks to finish and count statistics
            int successfullTasks = 0;
            int failedTasks      = 0;
            int cancelledTasks   = 0;
            int otherTasks       = 0;

            await Task.WhenAll(tasks.ToArray());

            foreach (Task task in tasks)
            {
                switch (task.Status)
                {
                case TaskStatus.RanToCompletion:
                    successfullTasks++;
                    break;

                case TaskStatus.Canceled:
                    cancelledTasks++;
                    break;

                case TaskStatus.Faulted:
                    failedTasks++;
                    break;

                default:
                    otherTasks++;
                    break;
                }
            }

            RDXTrace.TraceInformation("WhenAllTasks {5} finished after {0}ms: Succeeded:{1} Failed:{2} Cancel:{3} Other:{4}",
                                      stopWatch.ElapsedMilliseconds, successfullTasks, failedTasks, cancelledTasks, otherTasks, what);
        }
Exemple #2
0
 /// <summary>
 /// Query for latest event of the given node and
 /// given OPC UA server from the given time and date
 /// </summary>
 /// <param name="endTime">Time to start from querying in the past</param>
 /// <param name="appUri">The OPC UA server application Uri</param>
 /// <param name="nodeId">The node id in the OPC UA server namespace</param>
 public async Task <double> GetLatestQuery(DateTime endTime, string appUri, string nodeId)
 {
     try
     {
         Stopwatch stopwatch = new Stopwatch();
         stopwatch.Start();
         Task <IEnumerable <IEvent> > lastTask  = GetLatestEvent(endTime, appUri, nodeId);
         IEnumerable <IEvent>         lastEvent = await lastTask;
         stopwatch.Stop();
         RDXTrace.TraceInformation("GetLatestQuery took {0} ms", stopwatch.ElapsedMilliseconds);
         double last;
         try
         {
             last = GetValueOfProperty <double>(lastEvent.First <IEvent>(), OpcMonitoredItemValue);
         }
         catch
         {
             last = GetValueOfProperty <long>(lastEvent.First <IEvent>(), OpcMonitoredItemValue);
         }
         return(last);
     }
     catch (Exception e)
     {
         RDXTrace.TraceError("GetLatestQuery Exception {0}", e.Message);
         return(0);
     }
 }
Exemple #3
0
        /// <summary>
        /// Query to subtract the oldest value of a OPC UA node
        /// from the newest value in the search span
        /// </summary>
        /// <param name="searchSpan">Date and time span for the query</param>
        /// <param name="appUri">The OPC UA server application Uri</param>
        /// <param name="nodeId">The node id in the OPC UA server namespace</param>
        /// <returns>The difference</returns>
        public async Task <double> DiffQuery(DateTimeRange searchSpan, string appUri, string nodeId)
        {
            try
            {
                PredicateStringExpression expression = new PredicateStringExpression(
                    String.Format(OpcServerNodePredicate, appUri, nodeId));

                BaseLimitClause firstMember = new TopLimitClause(1,
                                                                 new[]
                {
                    new SortClause(new BuiltInPropertyReferenceExpression(BuiltInProperty.Timestamp),
                                   SortOrderType.Asc)
                });
                BaseLimitClause lastMember = new TopLimitClause(1,
                                                                new[]
                {
                    new SortClause(new BuiltInPropertyReferenceExpression(BuiltInProperty.Timestamp),
                                   SortOrderType.Desc)
                });

                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();
                Task <IEnumerable <IEvent> > firstTask = RDXQueryClient.GetEventsAsync(
                    searchSpan,
                    expression,
                    firstMember,
                    _cancellationToken);
                Task <IEnumerable <IEvent> > lastTask = RDXQueryClient.GetEventsAsync(
                    searchSpan,
                    expression,
                    lastMember,
                    _cancellationToken);
                await Task.WhenAll(new Task[] { firstTask, lastTask });

                IEnumerable <IEvent> firstEvent = firstTask.Result;
                IEnumerable <IEvent> lastEvent  = lastTask.Result;
                stopwatch.Stop();
                RDXTrace.TraceInformation("DiffQuery queries took {0} ms", stopwatch.ElapsedMilliseconds);

                long first = GetValueOfProperty <long>(firstEvent.First <IEvent>(), OpcMonitoredItemValue);
                long last  = GetValueOfProperty <long>(lastEvent.First <IEvent>(), OpcMonitoredItemValue);
                return(last - first);
            }
            catch (Exception e)
            {
                RDXTrace.TraceError("DiffQuery Exception {0}", e.Message);
                return(0);
            }
        }
Exemple #4
0
 /// <summary>
 /// Start the topology worker threads
 /// </summary>
 public void StartWorker(CancellationToken token)
 {
     try
     {
         // create a worker thread for each aggregation task
         ContosoTopologyNode root = _topology.GetRootNode();
         for (int aggregateIndex = 0; aggregateIndex < root.Aggregations.Count; aggregateIndex++)
         {
             // need a local copy of aggregateIndex to asynchronously start worker properly
             int workerIndex = aggregateIndex;
             _workers.Add(Task.Run(async() => await Worker(workerIndex, token)));
         }
     }
     catch (Exception e)
     {
         RDXTrace.TraceError("Start aggregation workers failed: {0}:", e.Message);
     }
 }
Exemple #5
0
 /// <summary>
 /// Query for latest event of the given node and given OPC UA server
 /// from the given time and date to find the display name for the nodeId.
 /// </summary>
 /// <param name="endTime">Time to start from querying in the past</param>
 /// <param name="appUri">The OPC UA server application Uri</param>
 /// <param name="nodeId">The node id in the OPC UA server namespace</param>
 /// <returns>The DisplayName of the nodeId</returns>
 public async Task <string> QueryDisplayName(DateTime endTime, string appUri, string nodeId)
 {
     try
     {
         Stopwatch stopwatch = new Stopwatch();
         stopwatch.Start();
         Task <IEnumerable <IEvent> > lastTask  = GetLatestEvent(endTime, appUri, nodeId);
         IEnumerable <IEvent>         lastEvent = await lastTask;
         stopwatch.Stop();
         string name = GetValueOfProperty <string>(lastEvent.First <IEvent>(), OpcDisplayName);
         RDXTrace.TraceInformation("GetDisplayName took {0} ms", stopwatch.ElapsedMilliseconds);
         return(name);
     }
     catch (Exception e)
     {
         RDXTrace.TraceError("GetDisplayName Exception {0}", e.Message);
     }
     return(nodeId);
 }
Exemple #6
0
        /// <summary>
        /// Test the explorer url for all stations
        /// </summary>
        /// <param name="topology"></param>
        public static void TestExplorerViews(ContosoTopology topology)
        {
            List <string> stations = topology.GetAllChildren(topology.TopologyRoot.Key, typeof(Station));
            int           count    = 0;
            DateTime      Now      = DateTime.UtcNow;
            string        url;

            url = GetExplorerDefaultView(Now.Subtract(TimeSpan.FromHours(8)), Now, false);
            RDXTrace.TraceInformation(url);
            url = GetExplorerDefaultView(Now.Subtract(TimeSpan.FromDays(1)), Now, true);
            RDXTrace.TraceInformation(url);
            foreach (string appUri in stations)
            {
                url = GetExplorerStationView(Now.Subtract(TimeSpan.FromHours(1)), Now,
                                             appUri, (count & 1) == 0, (count & 2) == 0);
                RDXTrace.TraceInformation(url);
                count++;
            }
        }
Exemple #7
0
        /// <summary>
        /// Query for aggregation of count, min, max, sum and average for all active
        /// nodes of the given OPC UA server in the given search span as time histogram
        /// with interval
        /// </summary>
        /// <param name="searchSpan">Date and time span for the query</param>
        /// <param name="appUri">The OPC UA server application Uri</param>
        /// <param name="interval">Interval for Date Time Histogram</param>
        /// <returns>The aggregated nodes</returns>
        public async Task <AggregateResult> GetAllAggregatedNodesWithInterval(DateTimeRange searchSpan, string appUri, string nodeId, TimeSpan interval)
        {
            try
            {
                string id = TimeSpanToId(interval);

                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();

                PredicateStringExpression predicate = new PredicateStringExpression(
                    String.Format(OpcServerNodePredicate, appUri, nodeId));
                Aggregate aggregate = new Aggregate(
                    Expression.UniqueValues(OpcMonitoredItemId, PropertyType.String, opcMaxMonitoredItemId),
                    new Aggregate(Expression.DateHistogram(BuiltInProperty.Timestamp, IntervalSize.FromId(id)),
                                  new Aggregate(
                                      Expression.Count(),
                                      Expression.Min(OpcMonitoredItemValue, PropertyType.Double),
                                      Expression.Max(OpcMonitoredItemValue, PropertyType.Double),
                                      Expression.Average(OpcMonitoredItemValue, PropertyType.Double),
                                      Expression.Sum(OpcMonitoredItemValue, PropertyType.Double)
                                      )));

                AggregatesResult aggregateResults = await RDXQueryClient.GetAggregatesAsync(
                    searchSpan,
                    predicate,
                    new[] { aggregate },
                    _cancellationToken);

                // Since there was 1 top level aggregate in request, there is 1 aggregate result.
                AggregateResult aggregateResult = aggregateResults[0];

                stopwatch.Stop();
                RDXTrace.TraceInformation("GetAllAggregatedNodes query took {0} ms", stopwatch.ElapsedMilliseconds);

                return(aggregateResult);
            }
            catch (Exception e)
            {
                RDXTrace.TraceError("GetAllAggregatedNodes: Exception {0}", e.Message);
                return(null);
            }
        }
Exemple #8
0
        /// <summary>
        /// Query for aggregation of count, min, max, sum and average for all active
        /// nodes of all OPC UA servers in the given search span
        /// </summary>
        /// <param name="searchSpan">Date and time span for the query</param>
        /// <returns>The aggregated servers and nodes</returns>
        public async Task <AggregateResult> GetAllAggregatedStationsAndNodes(DateTimeRange searchSpan)
        {
            try
            {
                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();

                Aggregate aggregate = new Aggregate(
                    Expression.UniqueValues(OpcServerUri, PropertyType.String, opcMaxServerUri),
                    new Aggregate(
                        Expression.UniqueValues(OpcMonitoredItemId, PropertyType.String, opcMaxMonitoredItemId),
                        new Aggregate(
                            Expression.Count(),
                            Expression.Min(OpcMonitoredItemValue, PropertyType.Double),
                            Expression.Max(OpcMonitoredItemValue, PropertyType.Double),
                            Expression.Average(OpcMonitoredItemValue, PropertyType.Double),
                            Expression.Sum(OpcMonitoredItemValue, PropertyType.Double)
                            )
                        )
                    );

                AggregatesResult aggregateResults = await RDXQueryClient.GetAggregatesAsync(
                    searchSpan,
                    null,
                    new[] { aggregate },
                    _cancellationToken);

                // Since there was 1 top level aggregate in request, there is 1 aggregate result.
                _getAllAggregatedStationsAndNodesResult = aggregateResults[0];

                stopwatch.Stop();
                RDXTrace.TraceInformation("GetAllAggregatedStationsAndNodes query took {0} ms", stopwatch.ElapsedMilliseconds);

                return(_getAllAggregatedStationsAndNodesResult);
            }
            catch (Exception e)
            {
                RDXTrace.TraceError("GetAllAggregatedStationsAndNodes: Exception {0}", e.Message);
                return(null);
            }
        }
        /// <summary>
        /// Search cached query for all active nodeId of all servers in topology.
        /// Add new nodes to station topology.
        /// </summary>
        /// <param name="fullQuery">A cached query</param>
        /// <param name="topologyStations">List of stations in topology</param>
        private async Task AddNewNodes(RDXCachedAggregatedQuery fullQuery, List <string> topologyStations)
        {
            List <string> opcUaServers = fullQuery.GetActiveServerList();

            foreach (string opcUaServer in opcUaServers)
            {
                ContosoOpcUaServer topologyNode = _topology[opcUaServer.ToLower()] as ContosoOpcUaServer;
                if (topologyNode != null)
                {
                    List <string> topologyNodeIdList = topologyNode.NodeList.Select(x => x.NodeId).ToList();
                    List <string> activeNodeIdList   = fullQuery.GetActiveNodeIdList(opcUaServer);
                    var           newNodeIdList      = activeNodeIdList.Except(topologyNodeIdList, StringComparer.InvariantCultureIgnoreCase);
                    if (newNodeIdList.Count() > 0)
                    {
                        RDXOpcUaQueries opcUaQueries = new RDXOpcUaQueries(CancellationToken.None);
                        foreach (string newNodeId in newNodeIdList)
                        {
                            RDXTrace.TraceInformation("RDX Worker adding node {0} to server {1}", newNodeId, opcUaServer);
                            string symbolicName = await opcUaQueries.QueryDisplayName(DateTime.UtcNow, opcUaServer, newNodeId);

                            topologyNode.AddOpcServerNode(
                                newNodeId,
                                symbolicName,
                                null,
                                ContosoOpcNodeOpCode.Avg,
                                null,
                                true,
                                null,
                                null,
                                null,
                                null,
                                null,
                                null,
                                null
                                );
                        }
                    }
                }
            }
        }
Exemple #10
0
        /// <summary>
        /// Query for aggregation of a given measure for the
        /// given node and given OPC UA server in the given search span
        /// </summary>
        /// <param name="searchSpan">Date and time span for the query</param>
        /// <param name="appUri">The OPC UA server application Uri</param>
        /// <param name="nodeId">The node id in the OPC UA server namespace</param>
        /// <param name="measure"></param>
        public async Task <double> GetAggregatedNode(DateTimeRange searchSpan, string appUri, string nodeId, AggregateExpression measure)
        {
            try
            {
                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();

                PredicateStringExpression predicate = new PredicateStringExpression(
                    String.Format(OpcServerNodePredicate, appUri, nodeId));
                Aggregate aggregate = new Aggregate(
                    measures: new AggregateExpression[] { measure }
                    );

                AggregatesResult aggregateResults = await RDXQueryClient.GetAggregatesAsync(
                    searchSpan,
                    predicate,
                    new[] { aggregate },
                    _cancellationToken);

                // Since there was 1 top level aggregate in request, there is 1 aggregate result.
                AggregateResult aggregateResult = aggregateResults[0];

                stopwatch.Stop();
                RDXTrace.TraceInformation("AggregateQuery query took {0} ms", stopwatch.ElapsedMilliseconds);

                if (aggregateResult.Measures == null)
                {
                    return(0.00);
                }

                return((double)aggregateResult.Measures[0]);
            }
            catch (Exception e)
            {
                RDXTrace.TraceError("AggregateQuery: Exception {0}", e.Message);
                return(0);
            }
        }
Exemple #11
0
        /// <summary>
        /// Worker to query one aggregate of a topology
        /// </summary>
        /// <param name="aggregateIndex">The aggregation view to update by the worker</param>
        /// <param name="token">CancellationToken</param>
        public async Task Worker(int aggregateIndex, CancellationToken token)
        {
            RDXOpcUaQueries  opcUaQueries  = new RDXOpcUaQueries(token);
            RDXOeeKpiQueries oeeKpiQueries = new RDXOeeKpiQueries(opcUaQueries, token);

            RDXTrace.TraceInformation("RDX Worker {0} started", aggregateIndex);

            // give app some time to start before updating queries
            await Task.Delay(10000 *(aggregateIndex + 1));

            while (!token.IsCancellationRequested)
            {
                Stopwatch stopWatch         = new Stopwatch();
                DateTime  nextUpdate        = DateTime.MaxValue;
                bool      resetStartDelayed = false;

                stopWatch.Start();

                Interlocked.Increment(ref _busyWorkers);

                // the station and node list is updated, other workers are delayed
                while (_workerStartDelayed != 0)
                {
                    RDXTrace.TraceInformation("RDX Worker {0} delayed", aggregateIndex);
                    await Task.Delay(1000);
                }

                try
                {
                    List <Task>         tasks    = new List <Task>();
                    ContosoTopologyNode rootNode = _topology.GetRootNode();
                    ContosoAggregatedOeeKpiHistogram aggregatedTimeSpan = rootNode[aggregateIndex];
                    DateTimeRange searchSpan = RDXUtils.TotalSearchRangeFromNow(aggregatedTimeSpan);

                    RDXTrace.TraceInformation("RDX Worker {0} updating Range {1} to {2}",
                                              aggregateIndex, searchSpan.From, searchSpan.To);

                    // calc next update. To time is already rounded for update time span.
                    nextUpdate = searchSpan.To + rootNode[aggregateIndex].UpdateTimeSpan;

                    // query all stations in topology and find all active servers in timespan
                    Task <StringDimensionResult> aggServerTask = opcUaQueries.AggregateServers(searchSpan);

                    // always get an aggregate of all activity for the latest interval, use it as a cache
                    TimeSpan                 intervalTimeSpan = aggregatedTimeSpan[0].IntervalTimeSpan;
                    DateTimeRange            aggregateSpan    = RDXUtils.CalcAggregationRange(aggregatedTimeSpan, searchSpan.To);
                    RDXCachedAggregatedQuery fullQuery        = new RDXCachedAggregatedQuery(opcUaQueries);
                    Task aggServerAndNodesTask = fullQuery.Execute(aggregateSpan);

                    // wait for all outstanding aggregates
                    tasks.Add(aggServerTask);
                    tasks.Add(aggServerAndNodesTask);
                    await RDXUtils.WhenAllTasks("Aggregates", tasks, stopWatch);

                    List <string> topologyStations = _topology.GetAllChildren(_tree.TopologyRoot.Key, typeof(Station));
                    List <string> opcUaServers     = await aggServerTask;

                    // intersect list of active servers and schedule all queries
                    tasks.Clear();
                    List <string> opcUaServersToQuery = opcUaServers.Intersect(topologyStations, StringComparer.InvariantCultureIgnoreCase).ToList();
                    await oeeKpiQueries.ScheduleAllOeeKpiQueries(searchSpan, _topology, fullQuery, opcUaServersToQuery, tasks, aggregateIndex);

                    // wait for all outstanding queries
                    await RDXUtils.WhenAllTasks("Queries", tasks, stopWatch);

                    // Update the topology Oee and KPI values
                    _topology.UpdateAllKPIAndOEEValues(aggregateIndex);

                    // one worker issues the Browser update
                    if (aggregatedTimeSpan.UpdateBrowser)
                    {
                        // Push updates to dashboard
                        BrowserUpdate();
                        RDXTrace.TraceInformation("BrowserUpdate finished after {0}ms",
                                                  stopWatch.ElapsedMilliseconds);
                    }

                    // add new stations and nodes to topology
                    if (aggregatedTimeSpan.UpdateTopology)
                    {
                        // delay other workers
                        Interlocked.Exchange(ref _workerStartDelayed, 1);
                        resetStartDelayed = true;
                        // only add stations and nodes if no other worker is busy yet
                        if (Interlocked.Increment(ref _busyWorkers) == 2)
                        {
                            AddNewServers(fullQuery, topologyStations);
                            await AddNewNodes(fullQuery, topologyStations);

                            RDXTrace.TraceInformation("Add New Server and Nodes finished after {0}ms",
                                                      stopWatch.ElapsedMilliseconds);
                        }
                    }
                }
                catch (Exception e)
                {
                    RDXTrace.TraceError("Exception {0} in Worker after {1}ms",
                                        e.Message, stopWatch.ElapsedMilliseconds);
                }
                finally
                {
                    Interlocked.Decrement(ref _busyWorkers);
                    if (resetStartDelayed)
                    {
                        Interlocked.Decrement(ref _busyWorkers);
                        Interlocked.Exchange(ref _workerStartDelayed, 0);
                    }
                }

                RDXTrace.TraceInformation("RDX Worker {0} schedule next update for {1}",
                                          aggregateIndex, nextUpdate);

                TimeSpan delay = nextUpdate.Subtract(DateTime.UtcNow);
                if (delay.TotalMilliseconds > 0)
                {
                    await Task.Delay(delay, token);
                }
            }
        }
        /// <summary>
        /// Create the singleton instance for the RDX C# client SDK
        /// Deletes singleton if the access to the specified environment fails
        /// to prevent subsequent access of worker threads
        /// </summary>
        public static async void Create()
        {
            while (true)
            {
                RDXTrace.TraceInformation("Start RDX Query Client");

                try
                {
                    ServicePointManager.DefaultConnectionLimit = 1000;
                    CommonLogger.SetWriter(TraceLogWriter.Instance);

                    // Authenticate with the RDX service, depending on configuration one option is used
                    // to create the OAuth2 bearer tokens
                    IClientAuthenticator clientAuthenticator;
                    string    tenantId = ConfigurationProvider.GetConfigurationSettingValue(rdxAuthenticationTenantId);
                    IccString clientCertificateThumbprint = new IccString(ConfigurationProvider.GetConfigurationSettingValue(rdxAuthenticationClientCertificateThumbprint));
                    if (clientCertificateThumbprint.IsNullOrWhiteSpace())
                    {
                        string clientId     = ConfigurationProvider.GetConfigurationSettingValue(rdxAuthenticationClientId);
                        string clientSecret = ConfigurationProvider.GetConfigurationSettingValue(rdxAuthenticationClientSecret);
                        if (String.IsNullOrWhiteSpace(clientSecret))
                        {
                            Uri redirectUri = new Uri(ConfigurationProvider.GetConfigurationSettingValue(rdxAuthenticationRedirectUri));
                            clientAuthenticator = new UserClientAuthenticator(tenantId, clientId, redirectUri, BaseClientAuthenticator.AzureTimeSeriesResource);
                        }
                        else
                        {
                            clientAuthenticator = new ClientCredentialAuthenticator(tenantId, clientId, clientSecret, BaseClientAuthenticator.AzureTimeSeriesResource);
                        }
                    }
                    else
                    {
                        string applicationClientId = ConfigurationProvider.GetConfigurationSettingValue(rdxAuthenticationClientApplicationId);
                        clientAuthenticator = new ApplicationCertificateClientAuthenticator(applicationClientId, clientCertificateThumbprint, tenantId, BaseClientAuthenticator.AzureTimeSeriesResource);
                    }

                    // Create the RDX client with authenticator and DNS resolver
                    _rdxDNSName = ConfigurationProvider.GetConfigurationSettingValue(rdxDnsName);
                    IccString rdxIccDnsName = new IccString("api." + _rdxDNSName);
                    string    solutionName  = ConfigurationProvider.GetConfigurationSettingValue(rdxApplicationName);
                    _rdxGlobalQueryClient = new RdxGlobalQueryClient(rdxIccDnsName, solutionName, clientAuthenticator);

                    // Test if our environment exists and is accessible
                    _rdxEnvironmentId = new IccString(ConfigurationProvider.GetConfigurationSettingValue(rdxEnvironmentId));
                    GetEnvironmentsOutput environments = await GetEnvironmentsAsync(CancellationToken.None);

                    Trace.TraceInformation("Got {0} environments: ", environments.Environments.Count);
                    bool foundEnvironment = false;
                    foreach (var env in environments.Environments)
                    {
                        Trace.TraceInformation("  {0} {1}", env.EnvironmentId, env.DisplayName);
                        if (env.EnvironmentId == _rdxEnvironmentId)
                        {
                            foundEnvironment    = true;
                            _rdxEnvironmentName = env.DisplayName;
                            _rdxEnvironmentFqdn = env.EnvironmentFqdn;
                            break;
                        }
                    }

                    if (!foundEnvironment)
                    {
                        throw new Exception(String.Format("RDX Environment {0} not found.", _rdxEnvironmentId.ToString()));
                    }

                    _rdxEnvironmentQueryClient = new RdxEnvironmentQueryClient(
                        _rdxEnvironmentFqdn,
                        solutionName,
                        clientAuthenticator);

                    Trace.TraceInformation("..... RDXQueryClient started .....");

                    return;
                }
                catch (Exception e)
                {
                    RDXTrace.TraceError("RDX CreateQueryClient failed: {0}", e.ExceptionToString());
                    _rdxGlobalQueryClient      = null;
                    _rdxEnvironmentQueryClient = null;
                }

                RDXTrace.TraceError("Fatal: RDX environment not found. Retry in 60s.");
                await Task.Delay(60000);
            }
        }