/// <summary> /// Timespan delegate. Returns the timespan as value. /// </summary> public static Task Timespan(RDXOeeKpiQuery rdxQuery, ContosoOpcUaNode opcUaNode) { TimeSpan timespan = rdxQuery.SearchSpan.To.Subtract(rdxQuery.SearchSpan.From); switch (opcUaNode.Units) { case "h": opcUaNode.Last.Value = timespan.TotalHours; break; case "m": opcUaNode.Last.Value = timespan.TotalMinutes; break; case "s": opcUaNode.Last.Value = timespan.TotalSeconds; break; case "t": opcUaNode.Last.Value = timespan.Ticks; break; case "ms": default: opcUaNode.Last.Value = timespan.TotalMilliseconds; break; } opcUaNode.Last.Time = rdxQuery.SearchSpan.To; opcUaNode.UpdateRelevance(rdxQuery.TopologyNode); return(Task.CompletedTask); }
/// <summary> /// Diff delegate. Calculate the difference between first value and last value in timespan. /// </summary> public static async Task Diff(RDXOeeKpiQuery rdxQuery, ContosoOpcUaNode opcUaNode) { opcUaNode.Last.Value = await rdxQuery.OpcUaQueries.DiffQuery(rdxQuery.SearchSpan, rdxQuery.AppUri, opcUaNode.NodeId); opcUaNode.Last.Time = rdxQuery.SearchSpan.To; opcUaNode.UpdateRelevance(rdxQuery.TopologyNode); }
/// <summary> /// Query for a single value. /// </summary> private static async Task Aggregate(RDXOeeKpiQuery rdxQuery, ContosoOpcUaNode opcUaNode, AggregateExpression expression) { opcUaNode.Last.Value = await rdxQuery.OpcUaQueries.GetAggregatedNode(rdxQuery.SearchSpan, rdxQuery.AppUri, opcUaNode.NodeId, expression); opcUaNode.Last.Time = rdxQuery.SearchSpan.To; opcUaNode.UpdateRelevance(rdxQuery.TopologyNode); }
/// <summary> /// Const delegate. Returns a constant value defined in the Node Id field. /// </summary> public static Task Const(RDXOeeKpiQuery rdxQuery, ContosoOpcUaNode opcUaNode) { opcUaNode.Last.Value = (double)opcUaNode.ConstValue; opcUaNode.Last.Time = DateTime.MinValue; opcUaNode.UpdateRelevance(rdxQuery.TopologyNode); return(Task.CompletedTask); }
/// <summary> /// Calls the delegate for an operator. /// </summary> /// <param name="opCode">Operation to execute</param> /// <param name="rdxQuery">Query info.</param> /// <param name="opcUaNode">Topology node.</param> public static Task CallOperator(ContosoOpcNodeOpCode opCode, RDXOeeKpiQuery rdxQuery, ContosoOpcUaNode opcUaNode) { RDXOpCode opCodeFunc = GetOperator(opCode); if (opCodeFunc == null) { return(Task.CompletedTask); } return(opCodeFunc(rdxQuery, opcUaNode)); }
/// <summary> /// SubMaxMin delegate. Subtracts min from max value in a timespan. /// </summary> /// <param name="rdxQuery"></param> /// <param name="opcUaNode"></param> public static async Task SubMaxMin(RDXOeeKpiQuery rdxQuery, ContosoOpcUaNode opcUaNode) { double max = await rdxQuery.OpcUaQueries.GetAggregatedNode(rdxQuery.SearchSpan, rdxQuery.AppUri, opcUaNode.NodeId, RDXOpcUaQueries.MaxValues()); double min = await rdxQuery.OpcUaQueries.GetAggregatedNode(rdxQuery.SearchSpan, rdxQuery.AppUri, opcUaNode.NodeId, RDXOpcUaQueries.MinValues()); opcUaNode.Last.Value = max - min; opcUaNode.Last.Time = rdxQuery.SearchSpan.To; opcUaNode.UpdateRelevance(rdxQuery.TopologyNode); }
/// <summary> /// Call the operator on a query and node. /// Gets value from cache if operation is supported, /// if not forwards the call to single query engine. /// </summary> /// <param name="opCode">Operation to get the value</param> /// <param name="rdxQuery">The query</param> /// <param name="opcUaNode">The Topology node </param> public async Task CallOperator(ContosoOpcNodeOpCode opCode, RDXOeeKpiQuery rdxQuery, ContosoOpcUaNode opcUaNode) { if (RDXUtils.IsAggregatedOperator(opCode) && _task != null) { _result = await _task; if (_result != null) { double?value = GetValue(opCode, rdxQuery.AppUri, opcUaNode.NodeId); if (value != null) { opcUaNode.Last.Value = (double)value; opcUaNode.Last.Time = rdxQuery.SearchSpan.To; opcUaNode.UpdateRelevance(rdxQuery.TopologyNode); } } return; } // issue a single query if the operator can not be handled from aggregated values await RDXContosoOpCodes.CallOperator(opCode, rdxQuery, opcUaNode); }
/// <summary> /// NOP delegate. Just updates the time, value is not updated. /// </summary> public static Task Nop(RDXOeeKpiQuery rdxQuery, ContosoOpcUaNode opcUaNode) { opcUaNode.Last.Time = DateTime.UtcNow; return(Task.CompletedTask); }
/// <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); } } } } } } }
/// <summary> /// Count delegate. Returns number of events in a timespan. /// </summary> public static Task Count(RDXOeeKpiQuery rdxQuery, ContosoOpcUaNode opcUaNode) { return(Aggregate(rdxQuery, opcUaNode, Expression.Count())); }
/// <summary> /// Max delegate. Returns largest value in a timespan. /// </summary> public static Task Max(RDXOeeKpiQuery rdxQuery, ContosoOpcUaNode opcUaNode) { return(Aggregate(rdxQuery, opcUaNode, RDXOpcUaQueries.MaxValues())); }