예제 #1
0
        /// <summary>
        /// Calc the aggregation range for given aggregation histogram
        /// </summary>
        /// <param name="aggregatedTimeSpan">Aggregated Histogram</param>
        /// <param name="toTime">End time of aggregation</param>
        /// <returns>The aggregation range</returns>
        public static DateTimeRange CalcAggregationRange(ContosoAggregatedOeeKpiHistogram aggregatedTimeSpan, DateTime toTime)
        {
            TimeSpan      intervalTimeSpan = aggregatedTimeSpan[0].IntervalTimeSpan;
            DateTimeRange aggregateSpan    = new DateTimeRange(
                (aggregatedTimeSpan.Intervals.Count == 1) ?
                toTime.Subtract(intervalTimeSpan) :
                RDXUtils.RoundDateTimeToTimeSpan(
                    toTime.Subtract(TimeSpan.FromSeconds(1)),
                    intervalTimeSpan),
                toTime
                );

            return(aggregateSpan);
        }
예제 #2
0
        /// <summary>
        /// Calc the search range for given aggregation histogram
        /// </summary>
        /// <param name="aggregatedTimeSpan">Aggregated Histogram</param>
        /// <returns>The search range</returns>
        public static DateTimeRange TotalSearchRangeFromNow(ContosoAggregatedOeeKpiHistogram aggregatedTimeSpan)
        {
            DateTime endTime = RDXUtils.RoundDateTimeToTimeSpan(
                DateTime.UtcNow,
                aggregatedTimeSpan.UpdateTimeSpan);
            DateTime startTime = endTime.Subtract(aggregatedTimeSpan.TotalTimeSpan);

            if (aggregatedTimeSpan.UpdateTimeSpan != aggregatedTimeSpan.IntervalTimeSpan)
            {
                if (aggregatedTimeSpan.TotalTimeSpan > aggregatedTimeSpan.IntervalTimeSpan)
                {
                    // The full search range is totaltime + interval
                    startTime = startTime.Subtract(aggregatedTimeSpan.IntervalTimeSpan);
                }

                startTime = startTime.Subtract(aggregatedTimeSpan.UpdateTimeSpan);
            }
            return(new DateTimeRange(startTime, endTime));
        }
예제 #3
0
        /// <summary>
        /// Return the time series for a given OPC UA server and a given node
        /// </summary>
        /// <param name="station">The OPC UA server</param>
        /// <param name="node">The OPC UA node Id</param>
        /// <param name="aggregationView">The hourly, daily or weekly view</param>
        /// <param name="getCount">Get event Count aggregate</param>
        public static async Task <AggregatedTimeSeriesResult> AggregatedNodeId(
            Station station,
            ContosoOpcUaNode node,
            ContosoTopologyNode.AggregationView aggregationView,
            bool getCount = false)
        {
            int             aggregateIndex = (int)aggregationView;
            RDXOpcUaQueries opcUaQuery     = new RDXOpcUaQueries(CancellationToken.None);
            ContosoAggregatedOeeKpiHistogram aggregatedTimeSpan = station[aggregateIndex];
            DateTimeRange searchSpan    = RDXUtils.TotalSearchRangeFromNow(aggregatedTimeSpan);
            DateTimeRange aggregateSpan = new DateTimeRange(
                searchSpan.To.Subtract(aggregatedTimeSpan.TotalTimeSpan),
                searchSpan.To
                );
            double roundFactor = 100.0;

            int index = (int)RDXOpcUaQueries.AggregateIndex.Count;

            if (!getCount)
            {
                if (!IsAggregatedOperator(node.OpCode))
                {
                    throw new Exception("Unsupported Operator for aggregation");
                }

                // handle special case for SubMaxMin, result is derived by substraction
                if (node.OpCode == ContosoOpcNodeOpCode.SubMaxMin)
                {
                    index = (int)RDXOpcUaQueries.AggregateIndex.Max;
                }
                else
                {
                    index = AggregatedOperatorIndex(node.OpCode);
                }
            }

            int resultDimension = aggregatedTimeSpan.Intervals.Count;

            DateTime [] dateTimeResult = CreateAggregateDateTimeArray(aggregateSpan.From, aggregatedTimeSpan.IntervalTimeSpan, resultDimension);

            AggregateResult queryResult = await opcUaQuery.GetAllAggregatedNodesWithInterval(
                aggregateSpan,
                station.Key,
                node.NodeId,
                aggregatedTimeSpan.IntervalTimeSpan
                );

            int count = queryResult.Aggregate.Dimension.Count;
            AggregatedTimeSeriesResult result = new AggregatedTimeSeriesResult(resultDimension, aggregateSpan.To, aggregatedTimeSpan.IntervalTimeSpan, node.Units);

            if (queryResult != null)
            {
                for (int i = 0; i < count; i++)
                {
                    double?value = queryResult.Aggregate.Aggregate.Measures.TryGetPropertyMeasure <double?>(new int[] { 0, i, index });
                    if (value != null)
                    {
                        // find matching date/time slot for value
                        var dateTime    = queryResult.Aggregate.Dimension[i];
                        int resultIndex = Array.IndexOf(dateTimeResult, dateTime);
                        if (resultIndex >= 0)
                        {
                            result.YValues[resultIndex] = roundFactor * (double)value;
                            if (node.OpCode == ContosoOpcNodeOpCode.SubMaxMin)
                            {
                                value = queryResult.Aggregate.Aggregate.Measures.TryGetPropertyMeasure <double?>(new int[] { 0, i, (int)RDXOpcUaQueries.AggregateIndex.Min });
                                if (value != null)
                                {
                                    result.YValues[resultIndex] -= roundFactor * (double)value;
                                }
                            }
                            result.YValues[resultIndex] = Math.Round(result.YValues[resultIndex]) / roundFactor;
                        }
                        else
                        {
                            throw new Exception("DateTime not found in aggregated query array");
                        }
                    }
                }
            }
            return(result);
        }
        /// <summary>
        /// Queries all intervals, servers and nodes in a topology for new values.
        /// Updates all relevances and checks for alerts.
        /// Returns list of task.
        /// Caller must wait for tasks to run to completion before topology is fully up to date.
        /// </summary>
        /// <param name="totalSearchSpan">Search span of query</param>
        /// <param name="topology">Topology to update</param>
        /// <param name="fullQuery">The cached query for the search span</param>
        /// <param name="opcUaServers">List of OPC UA servers to query for values</param>
        /// <param name="tasks">List of async tasks processing queries</param>
        /// <param name="aggregateIndex">The index of the aggregate in the topology nodes</param>
        public async Task ScheduleAllOeeKpiQueries(
            DateTimeRange totalSearchSpan,
            ContosoTopology topology,
            RDXCachedAggregatedQuery fullQuery,
            List <string> opcUaServers,
            List <Task> tasks,
            int aggregateIndex)
        {
            RDXAggregatedQueryCache queryCache = new RDXAggregatedQueryCache();

            queryCache.List.Add(fullQuery);

            foreach (string appUri in opcUaServers)
            {
                Station station = topology[appUri] as Station;
                if (station != null)
                {
                    ContosoAggregatedOeeKpiHistogram oeeKpiHistogram = station[aggregateIndex];
                    DateTime toTime    = totalSearchSpan.To;
                    int      intervals = oeeKpiHistogram.Intervals.Count;
                    oeeKpiHistogram.EndTime         = toTime;
                    station[aggregateIndex].EndTime = toTime;

                    foreach (ContosoAggregatedOeeKpiTimeSpan oeeKpiTimeSpan in oeeKpiHistogram.Intervals)
                    {
                        DateTime fromTime;

                        // first interval is current time
                        if (totalSearchSpan.To != toTime || intervals == 1)
                        {
                            fromTime = toTime.Subtract(oeeKpiTimeSpan.IntervalTimeSpan);
                        }
                        else
                        {
                            fromTime = RDXUtils.RoundDateTimeToTimeSpan(toTime.Subtract(TimeSpan.FromSeconds(1)), oeeKpiTimeSpan.IntervalTimeSpan);
                        }

                        DateTimeRange intervalSearchSpan = new DateTimeRange(fromTime, toTime);

                        if (toTime <= oeeKpiTimeSpan.EndTime)
                        {
                            // The interval is still up to date from a previous query, skip
                            toTime = fromTime;
                            continue;
                        }

                        oeeKpiTimeSpan.EndTime = toTime;
                        toTime = fromTime;

                        // find a cached query, if not try to cache the aggregation for this search span
                        RDXCachedAggregatedQuery aggregatedQuery = queryCache.Find(intervalSearchSpan);
                        if (aggregatedQuery == null)
                        {
                            RDXCachedAggregatedQuery newQuery = new RDXCachedAggregatedQuery(_opcUaQueries);
                            queryCache.List.Add(newQuery);
                            tasks.Add(newQuery.Execute(intervalSearchSpan));
                            aggregatedQuery = queryCache.Find(intervalSearchSpan);
                        }

                        RDXOeeKpiQuery oeeKpiQuery = new RDXOeeKpiQuery(_opcUaQueries, intervalSearchSpan, appUri, oeeKpiTimeSpan);

                        await oeeKpiQuery.QueryAllNodes(tasks, station.NodeList, oeeKpiHistogram.AwaitTasks, aggregatedQuery);

                        if (oeeKpiHistogram.CheckAlerts)
                        {
                            station.Status = ContosoPerformanceStatus.Good;
                            foreach (ContosoOpcUaNode node in station.NodeList)
                            {
                                if (node.Minimum != null || node.Maximum != null)
                                {
                                    station.Status |= await CheckAlert(intervalSearchSpan, appUri, station, node);
                                }
                            }
                        }
                    }
                }
            }
        }
예제 #5
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);
                }
            }
        }