/// <summary>
        /// This method splits a point list into severall smaller lists and perform bulk calls on each list
        /// In parallel.  
        /// </summary>
        private void GetRecordedValuesBulkParrallel(DataQuery query, AFTimeRange timeRange, int bulkPageSize, int maxDegOfParallel, int bulkParallelChunkSize, CancellationToken cancelToken)
        {
            _logger.WarnFormat("QUERY (BULK-P) # {5} - TAGS: {6} - PERIOD: {3} to {4} - MAX DEG. PAR. {0}, TAG_CHUNK_SIZE {1}, TAG_PAGE_SIZE {2},", maxDegOfParallel, bulkParallelChunkSize, bulkPageSize, timeRange.StartTime,timeRange.EndTime, query.QueryId, query.PiPoints.Count);

            // PARALLEL bulk
            var pointListList = query.PiPoints.ToList().ChunkBy(bulkParallelChunkSize);
            Parallel.ForEach(pointListList, new ParallelOptions { MaxDegreeOfParallelism = maxDegOfParallel,CancellationToken = cancelToken },
                (pts,state,index) =>
                {
                   var stats=new StatisticsInfo();
                    stats.Stopwatch.Start();

                    PIPagingConfiguration pagingConfiguration = new PIPagingConfiguration(PIPageType.TagCount, bulkPageSize);
                    PIPointList pointList = new PIPointList(pts);

                    try
                    {
                       // _logger.InfoFormat("Bulk query");
                        IEnumerable<AFValues> bulkData = pointList.RecordedValues(timeRange,
                            AFBoundaryType.Inside, String.Empty, false, pagingConfiguration).ToList();

                        if (_enableWrite)
                        {
                            var writeInfo=new WriteInfo()
                            {
                                Data = bulkData,
                                StartTime = timeRange.StartTime,
                                EndTime = timeRange.EndTime,
                                ChunkId = query.ChunkId,
                                SubChunkId= index
                            };

                            _dataWriter.DataQueue.Add(writeInfo, cancelToken);
                        }

                        stats.EventsCount = bulkData.Sum(s=>s.Count);
                        stats.Stopwatch.Stop();
                        stats.EventsInWritingQueue = _dataWriter.DataQueue.Count;
                        Statistics.StatisticsQueue.Add(stats, cancelToken);

                    }
                    catch (OperationCanceledException ex)
                    {
                        _logger.Error(pagingConfiguration.Error);
                    }
                    catch (Exception ex)
                    {

                        _logger.Error(ex);

                    }

                });
        }
        static void PrintInterpolated(AFDatabase database, string meterName, string startTime, string endTime, TimeSpan timeSpan)
        {
            Console.WriteLine(string.Format("Print Interpolated Values - Meter: {0}, Start: {1}, End: {2}", meterName, startTime, endTime));

            AFAttribute attr = AFAttribute.FindAttribute(@"\Meters\" + meterName + @"|Energy Usage", database);

            AFTime      start     = new AFTime(startTime);
            AFTime      end       = new AFTime(endTime);
            AFTimeRange timeRange = new AFTimeRange(start, end);

            AFTimeSpan interval = new AFTimeSpan(timeSpan);

            AFValues vals = attr.Data.InterpolatedValues(
                timeRange: timeRange,
                interval: interval,
                desiredUOM: null,
                filterExpression: null,
                includeFilteredValues: false);

            foreach (AFValue val in vals)
            {
                Console.WriteLine("Timestamp (Local): {0}, Value: {1:0.00} {2}", val.Timestamp.LocalTime, val.Value, val?.UOM.Abbreviation);
            }
            Console.WriteLine();
        }
        private static AFTimeRange checkAFTimeRange(string start, string end)
        {
            AFTimeRange aftr;

            aftr = new AFTimeRange(start, end);
            return(aftr);
        }
Example #4
0
        private void GetArchive_Click(object sender, EventArgs e)
        {
            ValuesListView.Items.Clear();
            if (TagList.SelectedIndices.Count < 1)
            {
                MessageBox.Show("Please select one tag from list");
                return;
            }
            string tagname = TagList.SelectedItems[0].Text;

            TagNameLabel.Text = tagname;
            PIPoint     pt        = PIPoint.FindPIPoint(myPIServer, tagname);
            AFTimeRange timerange = new AFTimeRange();

            timerange.StartTime = new AFTime(StartTimeTextBox.Text);
            timerange.EndTime   = new AFTime(EndTimeTextBox.Text);
            AFValues vals = pt.RecordedValues(timerange, OSIsoft.AF.Data.AFBoundaryType.Inside, "", true);

            foreach (AFValue val in vals)
            {
                string[] displayvalues = new string[2];
                displayvalues[0] = val.Timestamp.LocalTime.ToString();
                displayvalues[1] = val.Value.ToString();
                ListViewItem lvi = new ListViewItem(displayvalues);
                ValuesListView.Items.Add(lvi);
            }
        }
Example #5
0
        public override AFValues GetValues(object context, AFTimeRange timeRange, int numberOfValues, AFAttributeList inputAttributes, AFValues[] inputValues)
        {
            //retrieve base list of values
            AFValues baseReturnVals = base.GetValues(context, timeRange, numberOfValues, inputAttributes, inputValues);

            return(filterValues(baseReturnVals));
        }
        static void PrintHourlyAverage(AFDatabase database, string meterName, string startTime, string endTime)
        {
            Console.WriteLine(string.Format("Print Hourly Average - Meter: {0}, Start: {1}, End: {2}", meterName, startTime, endTime));

            AFAttribute attr = AFAttribute.FindAttribute(@"\Meters\" + meterName + @"|Energy Usage", database);

            AFTime      start     = new AFTime(startTime);
            AFTime      end       = new AFTime(endTime);
            AFTimeRange timeRange = new AFTimeRange(start, end);

            IDictionary <AFSummaryTypes, AFValues> vals = attr.Data.Summaries(
                timeRange: timeRange,
                summaryDuration: new AFTimeSpan(TimeSpan.FromHours(1)),
                summaryType: AFSummaryTypes.Average,
                calcBasis: AFCalculationBasis.TimeWeighted,
                timeType: AFTimestampCalculation.EarliestTime);


            foreach (AFValue val in vals[AFSummaryTypes.Average])
            {
                Console.WriteLine("Timestamp (Local): {0:yyyy-MM-dd HH\\h}, Value: {1:0.00} {2}", val.Timestamp.LocalTime, val.Value, val?.UOM.Abbreviation);
            }

            Console.WriteLine();
        }
Example #7
0
        private static IEnumerable <AFTimeRange> GetSubrangesForward(AFTimeRange timeRange, int count)
        {
            if (count == 1)
            {
                yield return(timeRange);
            }

            if (count < 2)
            {
                yield break;
            }

            var totalSeconds       = timeRange.Span.TotalSeconds;
            var secondsPerSubrange = (totalSeconds / count) + 1; // to be safe

            var rangeStart = timeRange.StartTime;

            while (rangeStart <= timeRange.EndTime)
            {
                var rangeEnd = new AFTime(rangeStart.UtcSeconds + secondsPerSubrange);
                if (rangeEnd > timeRange.EndTime)
                {
                    rangeEnd = timeRange.EndTime;
                }
                yield return(new AFTimeRange(rangeStart, rangeEnd));

                rangeStart = rangeEnd.AddPITicks(1);
            }
        }
Example #8
0
        private static void readData(String serverName, Func <String> tagNameSupplier, int[] daysToReadList)
        {
            PIServer server = new PIServers()
                              .Where(s => s.Name.Contains(serverName))
                              .First();

            server.Connect();

            foreach (int daysToRead in daysToReadList)
            {
                Console.Write("[{0} {1}d] ", serverName, daysToRead);

                Stopwatch roundTripStopwatch = Stopwatch.StartNew();
                PIPoint   tag = PIPoint.FindPIPoint(server, tagNameSupplier.Invoke());
                roundTripStopwatch.Stop();

                AFTimeRange timeRange = new AFTimeRange(new AFTime(readStart), new AFTime(readStart.Add(new TimeSpan(daysToRead, 0, 0, 0))));

                try {
                    Stopwatch readStopwatch = Stopwatch.StartNew();
                    AFValues  values        = tag.RecordedValues(timeRange, AFBoundaryType.Outside, "", true, 0);
                    readStopwatch.Stop();

                    Console.WriteLine("Read {0:n0} samples in {1:0.000}s (1m samples in {2:0.000}s) EstimatedRoundTripTime: {3}ms",
                                      values.Count,
                                      readStopwatch.ElapsedMilliseconds / 1000.0,
                                      ((double)readStopwatch.ElapsedMilliseconds) / values.Count * 1000.0,
                                      roundTripStopwatch.ElapsedMilliseconds);
                } catch (Exception e) {
                    Console.WriteLine("Exception: {0}", e.ToString());
                }
            }

            server.Disconnect();
        }
        public static void PrintHistorical(AFDatabase database, string meterName, string startTime, string endTime)
        {
            if (database == null)
            {
                throw new ArgumentNullException(nameof(database));
            }
            Console.WriteLine(string.Format("Print Historical Values - Meter: {0}, Start: {1}, End: {2}", meterName, startTime, endTime));

            AFAttribute attr = AFAttribute.FindAttribute(@"\Meters\" + meterName + @"|Energy Usage", database);

            AFTime      start     = new AFTime(startTime);
            AFTime      end       = new AFTime(endTime);
            AFTimeRange timeRange = new AFTimeRange(start, end);
            AFValues    vals      = attr.Data.RecordedValues(
                timeRange: timeRange,
                boundaryType: AFBoundaryType.Inside,
                desiredUOM: database.PISystem.UOMDatabase.UOMs["kilojoule"],
                filterExpression: null,
                includeFilteredValues: false);

            foreach (AFValue val in vals)
            {
                Console.WriteLine("Timestamp (UTC): {0}, Value (kJ): {1}", val.Timestamp.UtcTime, val.Value);
            }

            Console.WriteLine();
        }
Example #10
0
        private static IEnumerable <AFTimeRange> GetSubrangesBackward(AFTimeRange timeRange, int count)
        {
            if (count == 1)
            {
                yield return(timeRange);
            }

            if (count < 2)
            {
                yield break;
            }

            var totalSeconds       = timeRange.Span.TotalSeconds;
            var secondsPerSubrange = (totalSeconds / count) + 1; // to be safe

            var rangeStart = timeRange.StartTime;

            // This differs from Forward in that we flip + for - and < for >
            while (rangeStart >= timeRange.EndTime)
            {
                var rangeEnd = new AFTime(rangeStart.UtcSeconds - secondsPerSubrange);
                if (rangeEnd < timeRange.EndTime)
                {
                    rangeEnd = timeRange.EndTime;
                }
                yield return(new AFTimeRange(rangeStart, rangeEnd));

                rangeStart = rangeEnd.AddPITicks(-1);
            }
        }
        static void PrintInterpolated(AFDatabase database, string meterName, string startTime, string endTime, TimeSpan timeSpan)
        {
            Console.WriteLine(string.Format("Print Interpolated Values - Meter: {0}, Start: {1}, End: {2}", meterName, startTime, endTime));

            AFAttribute attr = AFAttribute.FindAttribute(@"\Meters\" + meterName + @"|Energy Usage", database);

            AFTime start = new AFTime(startTime);
            AFTime end = new AFTime(endTime);
            AFTimeRange timeRange = new AFTimeRange(start, end);

            AFTimeSpan interval = new AFTimeSpan(timeSpan);

            AFValues vals = attr.Data.InterpolatedValues(
                timeRange: timeRange,
                interval: interval,
                desiredUOM: null,
                filterExpression: null,
                includeFilteredValues: false);

            foreach (AFValue val in vals)
            {
                Console.WriteLine("Timestamp (Local): {0}, Value (kWh): {1}", val.Timestamp.LocalTime, val.Value);
            }
            Console.WriteLine();
        }
        public static async Task<IList<IDictionary<AFSummaryTypes, AFValue>>> GetSummariesAsync(AFAttributeList attributeList)
        {
            Console.WriteLine("Calling GetSummariesAsync\n");

            Task<IDictionary<AFSummaryTypes, AFValue>>[] tasks = attributeList
                // Do not make the call if async is not supported
                .Where(attr => (attr.SupportedDataMethods & AFDataMethods.Asynchronous) == AFDataMethods.Asynchronous)
                .Select(async attr =>
                {
                    try
                    {
                        AFSummaryTypes mySummaries = AFSummaryTypes.Minimum | AFSummaryTypes.Maximum | AFSummaryTypes.Average | AFSummaryTypes.Total;
                        AFTimeRange timeRange = new AFTimeRange(new AFTime("*-1d"), new AFTime("*"));
                    
                        return await attr.Data.SummaryAsync(
                            timeRange: timeRange,
                            summaryType: mySummaries,
                            calculationBasis: AFCalculationBasis.TimeWeighted,
                            timeType: AFTimestampCalculation.Auto);
                    }
                    catch (AggregateException ae)
                    {
                        Console.WriteLine("{0}: {1}", attr.Name, ae.Flatten().InnerException.Message);
                        return null;
                    }
                })
                .ToArray();

            return await Task.WhenAll(tasks);
        }
Example #13
0
        public IEnumerable <AFTime> GetEvents(AFTimeRange timeRange)
        {
            // Access values for each inputs and use their timestamps to determine the "trigger times"
            var events = new HashSet <AFTime>();

            foreach (var input in _inputs)
            {
                // Assumptions (need more logic to handle other cases)
                // 1. All inputs are used as trigger inputs for the analysis
                // 2. Each input supports RecordedValues
                // 3. OK to get values for the entire timerange
                // 4. This is not optimal, as retrieved values are only being used for timestamps. For calculation, we are
                // accessing the values again.
                var values = input.Data.RecordedValues(timeRange, OSIsoft.AF.Data.AFBoundaryType.Inside, null, null, false);
                foreach (var value in values)
                {
                    events.Add(value.Timestamp);
                }
            }

            var uniqueEvents = events.ToList();

            uniqueEvents.Sort();

            foreach (var ev in uniqueEvents)
            {
                if (ev >= timeRange.StartTime && ev <= timeRange.EndTime)
                {
                    yield return(ev);
                }
            }
        }
Example #14
0
        public static async Task <IList <IDictionary <AFSummaryTypes, AFValue> > > GetSummariesAsyncWithTimeout(AFAttributeList attributeList, int timeoutInMilliseconds)
        {
            // Use a "competing tasks" pattern to place timeout on multiple async requests

            Console.WriteLine("Calling GetSummariesAsyncWithTimeout");

            CancellationTokenSource cts           = new CancellationTokenSource();
            CancellationToken       token         = cts.Token;
            CancellationTokenSource ctsForTimer   = new CancellationTokenSource();
            CancellationToken       tokenForTimer = ctsForTimer.Token;

            Task <IDictionary <AFSummaryTypes, AFValue> >[] tasks = attributeList
                                                                    // Do not make the call if async is not supported
                                                                    .Where(attr => (attr.SupportedDataMethods & AFDataMethods.Asynchronous) == AFDataMethods.Asynchronous)
                                                                    .Select(async attr =>
            {
                try
                {
                    AFSummaryTypes mySummaries = AFSummaryTypes.Minimum | AFSummaryTypes.Maximum | AFSummaryTypes.Average | AFSummaryTypes.Total;
                    AFTimeRange timeRange      = new AFTimeRange(new AFTime("*-1d"), new AFTime("*"));

                    return(await attr.Data.SummaryAsync(
                               timeRange: timeRange,
                               summaryType: mySummaries,
                               calculationBasis: AFCalculationBasis.TimeWeighted,
                               timeType: AFTimestampCalculation.Auto,
                               cancellationToken: token));
                }
                catch (AggregateException ae)
                {
                    Console.WriteLine("{0}: {1}", attr.Element.Name, ae.Flatten().InnerException.Message);
                    return(null);
                }
                catch (OperationCanceledException oe)
                {
                    Console.WriteLine("{0}: {1}", attr.Element.Name, oe.Message);
                    return(null);
                }
            })
                                                                    .ToArray();

            // Define a task that completes when all subtasks are complete
            Task <IDictionary <AFSummaryTypes, AFValue>[]> task = Task.WhenAll(tasks);

            // Asychronously wait for either the summaries or timer task to complete
            if (await Task.WhenAny(task, Task.Delay(timeoutInMilliseconds, tokenForTimer)) == task)
            {
                // Cancel the timer task
                ctsForTimer.Cancel();
                // Return summaries result
                return(task.Result);
            }
            else
            {
                // Cancel the summaries task if timeout
                cts.Cancel();
                throw new TimeoutException("The operation has timed out.");
            }
        }
Example #15
0
        private AFValue GetDistance(IList <AFValues> vals, AFTimeRange timeRange)
        {
            var timedGeo = GetTimedGeo(vals);

            var differences = GetDifference(timedGeo);

            return(new AFValue(differences.Sum(x => x.Value), timeRange.EndTime, _uomMeter));
        }
 /// <summary>
 /// Constructor of a Log Input
 /// </summary>
 /// <param name="key"> unique variable name</param>
 /// <param name="element"> element name</param>
 /// <param name="attribute"> attribute name</param>
 /// <param name="path"> element path</param>
 /// <param name="timeRange"> AFTimeRange</param>
 public LogInput(string key, string element, string attribute, string path, AFTimeRange timeRange)
 {
     keyVariableName = key;
     this.element    = element;
     this.attribute  = attribute;
     elementPath     = path;
     Range           = timeRange;
     timespaceFormat = "ts";
 }
        public static List <AbstractRetrievePoints> LoadTagClassesRecorded(
            String inputFilePath,
            String outputDirectory,
            String timeResolution,
            int numYears,
            int pageSize,
            PIServer piServer,
            PISystem piSystem,
            int numParallelTasks,
            Logger logger)
        {
            var inputLines = LoadInputFile(inputFilePath);

            List <AbstractRetrievePoints> tagClasses = new List <AbstractRetrievePoints>();

            Parallel.ForEach(
                inputLines,
                new ParallelOptions {
                MaxDegreeOfParallelism = numParallelTasks
            },
                (String[] line) =>
            {
                string tagName         = line[0];
                string startTimeString = line[1];
                string endTimeString   = line[2];

                PIPoint tag;
                try
                {
                    tag = PIPoint.FindPIPoint(piServer, tagName);
                    AFTime startTime      = new AFTime(startTimeString);
                    AFTime endTime        = new AFTime(endTimeString);
                    AFTimeRange timeRange = new AFTimeRange(startTime, endTime);

                    string startTimeStamp = startTime.UtcTime.ToString("yyyy/MM/dd HH:mm:ss");
                    string endTimeStamp   = endTime.UtcTime.ToString("yyyy/MM/dd HH:mm:ss");
                    lock (logger) { logger.Log($"{startTimeStamp} : {endTimeStamp}, {tagName}"); }
                    lock (tagClasses)
                    {
                        tagClasses.Add(new PIRecordedPointRetrievalClass(
                                           tag,
                                           timeRange,
                                           outputDirectory,
                                           PIRandomFunctionsUtil.ParseTimeResolutionString(timeResolution),
                                           numYears,
                                           pageSize,
                                           logger));
                    }
                }
                catch (Exception e)
                {
                    logger.Log("Exception: could not FindPiPoint: " + e.ToString());
                }
            });

            return(tagClasses);
        }
        private void btnQueue_Click(object sender, EventArgs e)
        {
            AFAnalysisService service   = afDatabasePicker.PISystem.AnalysisService;
            AFTime            startTime = new AFTime(tbStartTime.Text);
            AFTime            endTime   = new AFTime(tbEndTime.Text);
            AFTimeRange       timeRange = new AFTimeRange(startTime, endTime);

            queueBackfill(service, timeRange);
        }
        public PIValuesList GetRecordedValues(string startTime, string endTime)
        {
            AFTime                 start        = new AFTime(startTime);
            AFTime                 end          = new AFTime(endTime);
            AFTimeRange            timeRange    = new AFTimeRange(start, end);
            IEnumerable <AFValues> valueResults = pointList.RecordedValues(timeRange, AFBoundaryType.Inside, string.Empty, false, new PIPagingConfiguration(PIPageType.TagCount, 100));

            return(new PIValuesList(valueResults));
        }
Example #20
0
 /// <summary>
 /// Constructor of a Log Input
 /// </summary>
 /// <param name="key"> unique variable name</param>
 /// <param name="element"> element name</param>
 /// <param name="attribute"> attribute name</param>
 /// <param name="path"> element path</param>
 /// <param name="timeRange"> AFTimeRange</param>
 public LogInput(string key, string element, string attribute, string path, AFTimeRange timeRange)
 {
     keyVariableName = key;
     this.element = element;
     this.attribute = attribute;
     elementPath = path;
     Range = timeRange;
     timespaceFormat = "ts";
 }
        /// <summary>
        /// Writes data based on the form out to Excel
        /// </summary>
        /// <param name="outputFileLocation">String output file location to store Excel file</param>
        private void WriteDataToExcel(string outputFileLocation)
        {
            AFTimeRange timeRange = GetTimeRangeFromDatePickers();

            AFElement element = afTreeView.AFSelection as AFElement;

            // initialize dictionary to capture attribute name and the values for the attribute
            Dictionary <string, List <ReportCellValue> > values = new Dictionary <string, List <ReportCellValue> >();

            // initialize excel work book using ClosedXml
            XLWorkbook   wb = new XLWorkbook();
            IXLWorksheet ws = wb.Worksheets.Add("AF Export");

            // export data from children or single element
            if (cbReportOnChildren.Checked)
            {
                int elementcount = 0;

                // loop through children elements
                foreach (AFElement children in element.Elements)
                {
                    // reset the values dictionary for the new child element and counter
                    values.Clear();
                    int tmpelementcount = 0;

                    foreach (AFAttribute attribute in children.Attributes)
                    {
                        // if the attribute is checked and the attribute supports the recorded values data retrieval method, get the values
                        if (cbChilrenAttributes.CheckedItems.Contains(attribute.Name) && attribute.SupportedDataMethods.HasFlag(AFDataMethods.RecordedValues))
                        {
                            // using Linq to do a Where AFValue is Good filter, convert these AFValues to ReportCellValue class for exporting to JSON or Excel
                            values[attribute.Name] = ConvertAFValuesToReportCellValues(attribute.Data.RecordedValues(timeRange, AFBoundaryType.Interpolated, null, null, true).Where(v => v.IsGood), attribute.Name);
                            tmpelementcount       += values[attribute.Name].Count();
                        }
                    }

                    WriteDataToExcelWorksheet(ws, children.Name, values, elementcount);
                    elementcount += tmpelementcount;
                }
            }
            else
            {
                // if the attribute is checked, get the values
                foreach (AFAttribute attribute in cbParentAttributes.CheckedItems)
                {
                    // using Linq to do a Where AFValue is Good filter, convert these AFValues to ReportCellValue class for exporting to JSON or Excel
                    values[attribute.Name] = ConvertAFValuesToReportCellValues(attribute.Data.RecordedValues(timeRange, AFBoundaryType.Interpolated, null, null, true).Where(v => v.IsGood), attribute.Name);
                }

                WriteDataToExcelWorksheet(ws, element.Name, values, 0);
            }

            // save the excel file to the specified location
            wb.SaveAs(outputFileLocation);
            lblExcelCompleted.Visible = true;
        }
Example #22
0
        public GraphQlAfAttribute(AFAttribute aAfAttribute, Field afAttributesField = null, Field tsPlotValuesField = null)
        {
            AFValue aAfValue = aAfAttribute.GetValue();

            name            = aAfAttribute.Name;
            ThisAfAttribute = aAfAttribute;
            value           = aAfValue.Value?.ToString();
            uom             = aAfAttribute.DisplayUOM?.Abbreviation;

            if (aAfAttribute.DataReference?.Name == "PI Point")
            {
                timeStamp = aAfValue.Timestamp.UtcTime.ToString("yyyy-MM-ddTHH:mm:ssZ");
            }

            if (afAttributesField != null)
            {
                var afAttributesNameFilterStrings = GraphQlHelpers.GetArgumentStrings(afAttributesField, "nameFilter");
                var afAttributesChildField        = GraphQlHelpers.GetFieldFromSelectionSet(afAttributesField, "afAttributes");

                var returnAttributesObject = new ConcurrentBag <GraphQlAfAttribute>();
                var afAttributeList        = aAfAttribute.Attributes.ToList <AFAttribute>();
                Parallel.ForEach(afAttributeList, aAfChildAttribute =>
                {
                    if (afAttributesNameFilterStrings.Count == 0 || afAttributesNameFilterStrings.Contains(aAfChildAttribute.Name))
                    {
                        returnAttributesObject.Add(new GraphQlAfAttribute(aAfAttribute, afAttributesChildField));
                    }
                });
                afAttributes = returnAttributesObject.OrderBy(x => x.name).ToList();
            }

            if (tsPlotValuesField != null)
            {
                if (aAfAttribute.DataReference?.Name == "PI Point")
                {
                    var plotDensity   = GraphQlHelpers.GetArgumentDouble(tsPlotValuesField, "plotDensity");
                    var startDateTime = GraphQlHelpers.GetArgumentDateTime(tsPlotValuesField, "startDateTime");
                    var endDateTime   = GraphQlHelpers.GetArgumentDateTime(tsPlotValuesField, "endDateTime");

                    var timeRange = new AFTimeRange(startDateTime, endDateTime);

                    AFValues asdf = ThisAfAttribute.GetValues(timeRange, (int)plotDensity, null);

                    var returnObject = new ConcurrentBag <GraphQlTsValue>();
                    foreach (AFValue aAfTsValue in asdf)
                    {
                        returnObject.Add(new GraphQlTsValue()
                        {
                            timeStamp = aAfTsValue.Timestamp.UtcTime.ToString("yyyy-MM-ddTHH:mm:ssZ"),
                            value     = aAfTsValue.Value.ToString()
                        });
                    }
                    tsPlotValues = returnObject.OrderBy(x => x.timeStamp).ToList();
                }
            }
        }
Example #23
0
        /// <summary>
        /// Provides an IEnumerable of AFTimeRange over a period specified by start time and end time.  The last value will never be greater than EndTime.
        /// </summary>
        /// <param name="startTime">Period beginning</param>
        /// <param name="endTime">Period End</param>
        /// <param name="days">The time range will be chunked into the number of days specified.</param>
        public static IEnumerable<AFTimeRange> EachNDay(DateTime startTime, DateTime endTime, int days)
        {
            for (var day = startTime.Date; day.Date <= endTime.Date; day = day.AddDays(days))
            {
                var et = day.AddDays(days) <= endTime ? day.AddDays(days) : endTime;
                var res = new AFTimeRange(day, et);

                yield return res;
            }
        }
Example #24
0
        static void PrintInterpolated(AFDatabase database, string meterName, string start, string end, TimeSpan interval)
        {
            AFTime      startTime = new AFTime(start);
            AFTime      endTime   = new AFTime(end);
            AFTimeRange timeRange = new AFTimeRange(startTime, endTime);

            AFAttribute att         = AFAttribute.FindAttribute(@"\Meters\" + meterName + @"|Energy Usage", database);
            AFTimeSpan  intervalNew = new AFTimeSpan(interval);
            AFValues    values      = att.Data.InterpolatedValues(timeRange: timeRange, interval: intervalNew, desiredUOM: null, filterExpression: null, includeFilteredValues: false);
        }
Example #25
0
        public IEnumerable <AFTime> GetEvents(AFTimeRange timeRange)
        {
            AFTime lastTime = timeRange.StartTime - TimeSpan.FromTicks(1);

            while (lastTime < timeRange.EndTime)
            {
                lastTime = _timeRule.GetNextEvent(lastTime).EndTime;
                yield return(lastTime);
            }
        }
        public PIValuesList GetInterpolatedValues(string startTime, string endTime, string interval)
        {
            AFTime                 start        = new AFTime(startTime);
            AFTime                 end          = new AFTime(endTime);
            AFTimeRange            timeRange    = new AFTimeRange(start, end);
            AFTimeSpan             timeSpan     = AFTimeSpan.Parse(interval);
            IEnumerable <AFValues> valueResults = pointList.InterpolatedValues(timeRange, timeSpan, string.Empty, false, new PIPagingConfiguration(PIPageType.TagCount, 100));

            return(new PIValuesList(valueResults));
        }
Example #27
0
        private AFValue GetMinSpeed(IList <AFValues> vals, AFTimeRange timeRange)
        {
            var timedGeo = GetTimedGeo(vals);

            var speedArray = GetSpeedArray(timedGeo);

            double max = speedArray.Min(x => x.Value);

            return(new AFValue(max, timeRange.EndTime, _uomMPH));
        }
        /// <summary>
        /// This method splits a point list into severall smaller lists and perform bulk calls on each list
        /// In parallel.
        /// </summary>
        private void GetRecordedValuesBulkParrallel(DataQuery query, AFTimeRange timeRange, int bulkPageSize, int maxDegOfParallel, int bulkParallelChunkSize, CancellationToken cancelToken)
        {
            _logger.WarnFormat("QUERY (BULK-P) # {5} - TAGS: {6} - PERIOD: {3} to {4} - MAX DEG. PAR. {0}, TAG_CHUNK_SIZE {1}, TAG_PAGE_SIZE {2},", maxDegOfParallel, bulkParallelChunkSize, bulkPageSize, timeRange.StartTime, timeRange.EndTime, query.QueryId, query.PiPoints.Count);

            // PARALLEL bulk
            var pointListList = query.PiPoints.ToList().ChunkBy(bulkParallelChunkSize);

            Parallel.ForEach(pointListList, new ParallelOptions {
                MaxDegreeOfParallelism = maxDegOfParallel, CancellationToken = cancelToken
            },
                             (pts, state, index) =>
            {
                var stats = new StatisticsInfo();
                stats.Stopwatch.Start();

                PIPagingConfiguration pagingConfiguration = new PIPagingConfiguration(PIPageType.TagCount, bulkPageSize);
                PIPointList pointList = new PIPointList(pts);

                try
                {
                    // _logger.InfoFormat("Bulk query");
                    IEnumerable <AFValues> bulkData = pointList.RecordedValues(timeRange,
                                                                               AFBoundaryType.Inside, String.Empty, false, pagingConfiguration).ToList();


                    if (_enableWrite)
                    {
                        var writeInfo = new WriteInfo()
                        {
                            Data       = bulkData,
                            StartTime  = timeRange.StartTime,
                            EndTime    = timeRange.EndTime,
                            ChunkId    = query.ChunkId,
                            SubChunkId = index
                        };

                        _dataWriter.DataQueue.Add(writeInfo, cancelToken);
                    }


                    stats.EventsCount = bulkData.Sum(s => s.Count);
                    stats.Stopwatch.Stop();
                    stats.EventsInWritingQueue = _dataWriter.DataQueue.Count;
                    Statistics.StatisticsQueue.Add(stats, cancelToken);
                }
                catch (OperationCanceledException ex)
                {
                    _logger.Error(pagingConfiguration.Error);
                }
                catch (Exception ex)
                {
                    _logger.Error(ex);
                }
            });
        }
Example #29
0
        private void cbUOM_SelectedIndexChanged(object sender, EventArgs e)
        {
            UOM selectedUOM = cbUOM.SelectedItem as UOM;
            AFAttribute selectedAttribute = afTreeView2.AFSelection as AFAttribute;

            //Clear list of values
            lbData.Items.Clear();

            //Convert current value to new UoM
            lbCurrentVal.Text = selectedAttribute.GetValue().Convert(selectedUOM).ToString();
            lbTimestamp.Text = selectedAttribute.GetValue().Timestamp.ToString();

            //Clear chart
            afDataChart.Series["dataSeries"].Points.Clear();

            if (lbData.Items != null)
            {
                //Get time-range
                AFTime startTime = new AFTime(afStartDate.Text);
                AFTime endTime = new AFTime(afEndTime.Text);
                AFTimeRange timeRange = new AFTimeRange(startTime, endTime);

                AFValues vals;

                if (selectedAttribute != null)
                {
                    //Convert to new Uom if required
                    if (selectedAttribute.DefaultUOM != null)
                    {
                        vals = selectedAttribute.GetValues(timeRange, 0, null).Convert(selectedUOM);
                    }
                    else
                    {
                        vals = selectedAttribute.GetValues(timeRange, 0, null);
                    }

                    //Fill out list and chart
                    foreach (AFValue val in vals)
                    {
                        string s = String.Format("{0} \t {1} {2}", val.Timestamp.LocalTime, val.Value,
                                                    selectedUOM != null ? selectedUOM.Abbreviation : null);
                        lbData.Items.Add(s);

                        try
                        {
                            afDataChart.Series["dataSeries"].Points.AddXY(val.Timestamp.ToString(), val.Value);
                        }
                        catch (System.ArgumentException)
                        {
                            continue;
                        }
                    }
                }
            }
        }
        static void Main(string[] args)
        {
            var AFServerName   = "SKYPI05";
            var AFDatabaseName = "MBMC";
            var user           = @"skypi\lbowling";
            var password       = @"OFNxBKOZmG1w";

            NetworkCredential credential = new NetworkCredential(user, password);
            var piSystem = (new PISystems())[AFServerName];

            piSystem.Connect(credential);
            var afdb = piSystem.Databases[AFDatabaseName];


            foreach (var item in afdb.Elements)
            {
                Console.WriteLine(item.Name);
            }

            var selectedElementName = "Element1";
            var selectedElement     = afdb.Elements[selectedElementName];

            Console.WriteLine("Attributes for " + selectedElementName);

            foreach (var attribute in selectedElement.Attributes)
            {
                Console.WriteLine(attribute.Name);
            }

            var selectedAttributeName = "Attribute1";
            var selectedAttribute     = selectedElement.Attributes[selectedAttributeName];


            var start     = DateTime.Today.AddDays(-1);
            var end       = start.AddDays(1);
            var timeRange = new AFTimeRange(start, end);
            var span      = AFTimeSpan.Parse("1h");


            //var afValues = selectedAttribute.Data.PlotValues(timeRange, 24, null);
            //var afValues = selectedAttribute.Data.InterpolatedValues(timeRange, span, null, null, false);
            var afValues = selectedAttribute.Data.RecordedValues(timeRange, OSIsoft.AF.Data.AFBoundaryType.Inside, null, null, false);


            foreach (var afValue in afValues)
            {
                Console.WriteLine("Time: {0} \t Value: {1} \t", afValue.Timestamp, afValue.Value.ToString());
            }

            Console.WriteLine("Count: " + afValues.Count);



            Console.ReadKey();
        }
        protected override void DoTask(CancellationToken cancelToken)
        {
            foreach (var query in QueriesQueue.GetConsumingEnumerable(cancelToken))
            {
                var timeRange = new AFTimeRange(query.StartTime, query.EndTime);
                GetRecordedValuesBulkParrallel(query, timeRange, _dataReaderSettings.BulkPageSize, _dataReaderSettings.MaxDegreeOfParallelism, _dataReaderSettings.BulkParallelChunkSize, cancelToken);
            }

            // tell that we are done with adding new data to the data processor.  The task will complete after that
            _dataWriter.DataQueue.CompleteAdding();
        }
Example #32
0
 override public void GetNextResult()
 {
     nextEndTime = timeSpan.Multiply(nextStartTime, pageSize);
     if (nextEndTime > timeRange.EndTime)
     {
         nextEndTime   = timeRange.EndTime;
         fetchNextPage = false;
     }
     pageTimeRange = new AFTimeRange(nextStartTime, nextEndTime);
     valuesTask    = tag.InterpolatedValuesAsync(pageTimeRange, timeSpan, filterExpression: null, includeFilteredValues: false);
 }
Example #33
0
        /// <summary>
        /// Provides an IEnumerable of AFTimeRange over a period specified by start time and end time.  The last value will never be greater than EndTime.
        /// </summary>
        /// <param name="startTime">Period beginning</param>
        /// <param name="endTime">Period End</param>
        /// <param name="days">The time range will be chunked into the number of days specified.</param>
        public static IEnumerable <AFTimeRange> EachNDay(DateTime startTime, DateTime endTime, int days)
        {
            for (var day = startTime.Date; day.Date <= endTime.Date; day = day.AddDays(days))
            {
                var et = day.AddDays(days) <= endTime?day.AddDays(days) : endTime;

                var res = new AFTimeRange(day, et);

                yield return(res);
            }
        }
        /// <summary>
        /// Assert results of an analysis output.
        /// </summary>
        public static void AssertResults(AFAttribute output, AFTimeRange timeRange, IList <AFValue> expectedValues)
        {
            Contract.Requires(expectedValues != null);

            var actualValues = output.GetRecordedValues(timeRange);

            Assert.Equal(actualValues.Count, expectedValues.Count);
            for (int i = 0; i < actualValues.Count; i++)
            {
                Assert.Equal(actualValues[i], expectedValues[i]);
            }
        }
        protected override void DoTask(CancellationToken cancelToken)
        {
            foreach (var query in QueriesQueue.GetConsumingEnumerable(cancelToken))
            {
                var timeRange = new AFTimeRange(query.StartTime,query.EndTime);
                GetRecordedValuesBulkParrallel(query, timeRange, _dataReaderSettings.BulkPageSize, _dataReaderSettings.MaxDegreeOfParallelism, _dataReaderSettings.BulkParallelChunkSize, cancelToken);

            }

            // tell that we are done with adding new data to the data processor.  The task will complete after that
               _dataWriter.DataQueue.CompleteAdding();
        }
        public static void SwapValues(AFDatabase database, string meter1, string meter2, string startTime, string endTime)
        {
            Console.WriteLine(string.Format("Swap values for meters: {0}, {1} between {2} and {3}", meter1, meter2, startTime, endTime));

            // NOTE: This method does not ensure that there is no data loss if there is failure.
            // Persist the data first in case you need to rollback.
            AFAttribute attr1 = AFAttribute.FindAttribute(@"\Meters\" + meter1 + @"|Energy Usage", database);
            AFAttribute attr2 = AFAttribute.FindAttribute(@"\Meters\" + meter2 + @"|Energy Usage", database);

            AFTime      start     = new AFTime(startTime);
            AFTime      end       = new AFTime(endTime);
            AFTimeRange timeRange = new AFTimeRange(start, end);

            // Get values to delete for meter1
            AFValues valsToRemove1 = attr1.Data.RecordedValues(
                timeRange: timeRange,
                boundaryType: AFBoundaryType.Inside,
                desiredUOM: null,
                filterExpression: null,
                includeFilteredValues: false);

            // Get values to delete for meter2
            AFValues valsToRemove2 = attr2.Data.RecordedValues(
                timeRange: timeRange,
                boundaryType: AFBoundaryType.Inside,
                desiredUOM: null,
                filterExpression: null,
                includeFilteredValues: false);

            if (valsToRemove1.Count == 0 && valsToRemove2.Count == 0)
            {
                throw new Exception("There are no values to swap.");
            }

            List <AFValue> valsToRemove = valsToRemove1.ToList();

            valsToRemove.AddRange(valsToRemove2.ToList());

            // Remove the values
            AFListData.UpdateValues(valsToRemove, AFUpdateOption.Remove);

            // Create new AFValues from the other meter and assign them to this meter
            List <AFValue> valsToAdd1 = valsToRemove2.Select(v => new AFValue(attr1, v.Value, v.Timestamp)).ToList();
            List <AFValue> valsToAdd2 = valsToRemove1.Select(v => new AFValue(attr2, v.Value, v.Timestamp)).ToList();

            List <AFValue> valsCombined = valsToAdd1;

            valsCombined.AddRange(valsToAdd2);

            AFListData.UpdateValues(valsCombined, AFUpdateOption.Insert);
            Console.WriteLine();
        }
        public static IDictionary<AFAttribute, AFValues> GetTotalsAsync(AFDatabase afDb)
        {
            AFAttributeList attributeList = GetAttributes(afDb);

            // Beginning of current month to beginning of today.
            Dictionary<AFAttribute, AFValues> totals = new Dictionary<AFAttribute, AFValues>();
            AFTimeRange timeRange = new AFTimeRange(new AFTime(string.Format("{0}-{1}-01", DateTime.Now.Year, DateTime.Now.Month)), new AFTime("T"));
            AFTimeSpan dayInterval = new AFTimeSpan(0, 0, 1);
            List<Task<IDictionary<AFSummaryTypes, AFValues>>> processingList = new List<Task<IDictionary<AFSummaryTypes, AFValues>>>();
            foreach (AFAttribute attribute in attributeList)
            {
                // Do not make the call if async is not supported
                if ((attribute.SupportedDataMethods & AFDataMethods.Asynchronous) == 0)
                    continue;

                try
                {
                    processingList.Add(
                    attribute.Data.SummariesAsync(timeRange, dayInterval, AFSummaryTypes.Total, AFCalculationBasis.TimeWeighted, AFTimestampCalculation.Auto));

                    // periodically evaluate
                    if (processingList.Count > Environment.ProcessorCount * 2)
                    {
                        Task.WhenAll(processingList.ToArray());
                        foreach (var item in processingList)
                        {
                            WriteSummaryItem(item.Result[AFSummaryTypes.Total]);
                        }

                        processingList = new List<Task<IDictionary<AFSummaryTypes, AFValues>>>();
                    }
                }
                catch (AggregateException ae)
                {
                    //if (ae.Flatten().InnerExceptions.Count == 1)
                    Console.WriteLine("{0}: {1}", attribute.Name, ae.Flatten().InnerException.Message);
                }
            }

            if (processingList.Count > 0)
            {
                Task.WhenAll(processingList.ToArray());
                foreach (var item in processingList)
                {
                    WriteSummaryItem(item.Result[AFSummaryTypes.Total]);
                }
            }

            return totals;
        }
        public static IDictionary<AFAttribute, AFValues> GetTotalsBulk(AFDatabase afDb)
        {
            AFAttributeList attributeList = GetAttributes(afDb);

            Dictionary<AFAttribute, AFValues> totals = new Dictionary<AFAttribute, AFValues>();
            AFTimeRange timeRange = new AFTimeRange(new AFTime(string.Format("{0}-{1}-01", DateTime.Now.Year, DateTime.Now.Month)), new AFTime("T"));
            AFTimeSpan dayInterval = new AFTimeSpan(0, 0, 1);

            PIPagingConfiguration pageConfig = new PIPagingConfiguration(PIPageType.TagCount, Environment.ProcessorCount * 2);
            foreach (var item in attributeList.Data.Summaries(timeRange, dayInterval, AFSummaryTypes.Total, AFCalculationBasis.TimeWeighted, AFTimestampCalculation.Auto,
                pageConfig))
            {
                WriteSummaryItem(item[AFSummaryTypes.Total]);
            }

            return totals;
        }
        public void PrintHistorical(string meterName, string startTime, string endTime)
        {
            AFAttribute attr = AFAttribute.FindAttribute(@"\Meters\" + meterName + @"|Energy Usage", _database);

            AFTime start = new AFTime(startTime);
            AFTime end = new AFTime(endTime);
            AFTimeRange timeRange = new AFTimeRange(start, end);
            AFValues vals = attr.Data.RecordedValues(
                timeRange: timeRange, 
                boundaryType: AFBoundaryType.Inside, 
                desiredUOM: _database.PISystem.UOMDatabase.UOMs["kilojoule"],
                filterExpression: null,
                includeFilteredValues: false);

            foreach (AFValue val in vals)
            {
                Console.WriteLine("Timestamp (UTC): {0}, Value (kJ): {1}", val.Timestamp.UtcTime, val.Value);
            }
        }
        public static async Task<IList<IDictionary<AFSummaryTypes, AFValue>>> GetSummariesAsyncThrottled(AFAttributeList attributeList, int numConcurrent)
        {
            // Use "asynchronous semaphore" pattern (e.g. SemaphoreSlim.WaitAsync()) to throttle the calls

            Console.WriteLine("Calling GetSummariesAsyncThrottled");

            // Example: Limit to numConcurrent concurrent async I/O operations.
            SemaphoreSlim throttler = new SemaphoreSlim(initialCount: numConcurrent);

            Task<IDictionary<AFSummaryTypes, AFValue>>[] tasks = attributeList
                // Do not make the call if async is not supported
                .Where(attr => (attr.SupportedDataMethods & AFDataMethods.Asynchronous) == AFDataMethods.Asynchronous)
                .Select(async attr =>
                {
                    // asychronously try to acquire the semaphore
                    await throttler.WaitAsync();

                    try
                    {
                        AFSummaryTypes mySummaries = AFSummaryTypes.Minimum | AFSummaryTypes.Maximum | AFSummaryTypes.Average | AFSummaryTypes.Total;
                        AFTimeRange timeRange = new AFTimeRange(new AFTime("*-1d"), new AFTime("*"));

                        return await attr.Data.SummaryAsync(
                            timeRange: timeRange,
                            summaryType: mySummaries,
                            calculationBasis: AFCalculationBasis.TimeWeighted,
                            timeType: AFTimestampCalculation.Auto);
                    }
                    catch (AggregateException ae)
                    {
                        Console.WriteLine("{0}: {1}", attr.Name, ae.Flatten().InnerException.Message);
                        return null;
                    }
                    finally
                    {
                        // release the resource
                        throttler.Release();
                    }
                })
                .ToArray();

            return await Task.WhenAll(tasks);
        }
        public void PrintHourlyAverage(string meterName, string startTime, string endTime)
        {
            AFAttribute attr = AFAttribute.FindAttribute(@"\Meters\" + meterName + @"|Energy Usage", _database);

            AFTime start = new AFTime(startTime);
            AFTime end = new AFTime(endTime);
            AFTimeRange timeRange = new AFTimeRange(start, end);

            IDictionary<AFSummaryTypes, AFValues> vals = attr.Data.Summaries(
                timeRange: timeRange,
                summaryDuration: new AFTimeSpan(TimeSpan.FromHours(1)),
                summaryType: AFSummaryTypes.Average,
                calcBasis: AFCalculationBasis.TimeWeighted,
                timeType: AFTimestampCalculation.EarliestTime);


            foreach (AFValue val in vals[AFSummaryTypes.Average])
            {
                Console.WriteLine("Timestamp (Local): {0}, Value (kilowatt hour): {1}", val.Timestamp.LocalTime, val.Value);
            }
        }
        private void btnRetrieveData_Click(object sender, EventArgs e)
        {
            try
            {
                lbData.Items.Clear();
                AFAttribute selectedAttribute = lbAttributes.SelectedItem as AFAttribute;
                AFTimeRange timeRange = new AFTimeRange(tbStartTime.Text, tbEndTime.Text);
                AFValues vals = null;
                switch (cbDataMethod.Text)
                {
                    case "Recorded Values":
                        vals = selectedAttribute.Data.RecordedValues(timeRange, OSIsoft.AF.Data.AFBoundaryType.Inside, null, String.Empty, false, 0);
                        break;
                    case "Interpolated Values":
                        var interval = new AFTimeSpan();
                        AFTimeSpan.TryParse(tbInterval.Text, out interval);
                        vals = selectedAttribute.Data.InterpolatedValues(timeRange, interval, null, String.Empty, false);
                        break;
                    case "Snapshot":
                        vals = new AFValues();
                        vals.Add(selectedAttribute.Data.EndOfStream(null));
                        break;
                    default:
                        throw new InvalidExpressionException();

                }

                var valStrings = vals.Select(afv => String.Format("{0}\t{1}\t{2}\t{3}"
                    , afv.Attribute.Name
                    , afv.Timestamp.LocalTime
                    , afv.Value
                    , afv.UOM == null ? string.Empty : afv.UOM.Name));

                lbData.Items.AddRange(valStrings.ToArray());
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error getting PI Data: " + ex.Message);
            }
        }
Example #43
0
        private AFValue GetMinSpeed(IList<AFValues> vals, AFTimeRange timeRange)
        {
            var timedGeo = GetTimedGeo(vals);

            var speedArray = GetSpeedArray(timedGeo);

            double max = speedArray.Min(x => x.Value);

            return new AFValue(max, timeRange.EndTime, _uomMPH);
        }
Example #44
0
        private AFValue GetDistance(IList<AFValues> vals, AFTimeRange timeRange)
        {
            var timedGeo = GetTimedGeo(vals);

            var differences = GetDifference(timedGeo);

            return new AFValue(differences.Sum(x => x.Value), timeRange.EndTime, _uomMeter);
        }
Example #45
0
 // Return all values (converted to AFValues) over a specific time interval
 public override AFValues GetValues(object context, AFTimeRange timeRange, int numberOfValues, AFAttributeList inputAttributes, AFValues[] inputValues)
 {
     AFValues values = new AFValues();
     DateTime startTime = timeRange.StartTime.LocalTime;
     DateTime endTime = timeRange.EndTime.LocalTime;
     using (SqlDataReader reader = SQLHelper.GetSQLData(SQLName, DBName, TableName, startTime, endTime))
     {
         while (reader.Read())
         {
             AFValue newVal = new AFValue();
             newVal.Timestamp = AFTime.Parse(reader["pi_time"].ToString());
             newVal.Value = reader["pi_value"];
             values.Add(newVal);
         }
     }
     return values;
 }
        /// <summary>
        /// This method gets a collection of AFValue objects for an attribute based upon the data reference configuration within the specified AFTimeRange context.
        /// <see cref="https://techsupport.osisoft.com/Documentation/PI-AF-SDK/html/M_OSIsoft_AF_Asset_AFDataReference_GetValues.htm"/>
        /// </summary>
        public override AFValues GetValues(object context, AFTimeRange timeRange, int numberOfValues, AFAttributeList inputAttributes,
            AFValues[] inputValues)
        {
            var afvalues = new AFValues();

            //insert logic to generate af values here
            afvalues.Add(new AFValue()
            {
                Value = 0,
                Timestamp = new AFTime(timeRange.StartTime)
            });

            afvalues.Add(new AFValue()
            {
                Value = 1,
                Timestamp = new AFTime(timeRange.EndTime)
            });

            return afvalues;
        }
Example #47
0
        /// <summary>
        ///  Resulting Action of editting the List View.
        ///  1) Edit the List Item
        ///  2) Re-log the item 
        ///  3) Edit the Matlab workspace.
        /// </summary>
        /// <param name="name"> The new variable name. (or the same)</param>
        /// <param name="start"> The new start time. (or the same)</param>
        /// <param name="end"> The new end time. (or the same)</param>
        /// <param name="previous"></param>
        public void EditLog(string name, string start, string end, LogInput previous)
        {
            MatlabAccess.removeMatlabVariable(previous.getKeyVariableName());
            if(previous.getKeyVariableName() != name)
                name = MatlabAccess.modifyMatlabName(name);
            if (name == string.Empty)
                name = previous.getKeyVariableName();
            string attName = previous.getAttribute();
            string path = previous.getPath();
            string elem = previous.getElement();

            //EDIT LIST VIEW
            if (AFAccess.isAbsoluteTimeString(start, end, previous))
            {
                lv_LogDialog.SelectedItems[0].SubItems[3].Text = start + " = " + end;
                lv_LogDialog.SelectedItems[0].Text = name;
            }
            else
            {
                AFTimeRange range = new AFTimeRange(start, end);
                lv_LogDialog.SelectedItems[0].SubItems[3].Text = range.ToString();
                lv_LogDialog.SelectedItems[0].Text = name;
            }

            //EDIT ACTUAL LOG
            LogSystem.removeLogInput(previous.getKeyVariableName(), previous.getServerDatabase());

            //Workspace Edit - remove variable, getNewData
            if (path == "PI.Point")
            {
                string[] info = Regex.Split(previous.getServerDatabase(), "'");
                control.getPIData(attName, info[1], name, start, end,false);
            }
            else
                control.getAFData(previous.getServerDatabase(), attName, name, path, start, end, false);
        }
Example #48
0
 public void setAFTimeRange(AFTimeRange aftr)
 {
     this.Range = aftr;
 }
Example #49
0
		public void RecordedValues(WAFTime start, WAFTime end, WAFBoundryType boundryType, string filterExp, bool includeFiltVals, int maxCount, ref WAFValue[] output)
		{
			List<WAFValue> retVals = null;

			try
			{
				AFTimeRange range = new AFTimeRange(start.ToString(), end.ToString());
				AFBoundaryType opt = (AFBoundaryType)Enum.ToObject(typeof(AFBoundaryType), (byte)boundryType);

				AFValues vals = m_Pipt.RecordedValues(range, AFBoundaryType.Interpolated, filterExp, includeFiltVals, maxCount);

				retVals = vals.ConvertAll(new Converter<AFValue, WAFValue>(WAFValue.AFValueToWAFValue));
			}
			catch (Exception ex)
			{
				System.Console.WriteLine(ex.Message);
			}

			output = retVals.ToArray();
			return;
		}
Example #50
0
 private void GetArchive_Click(object sender, EventArgs e)
 {
     ValuesListView.Items.Clear();
     if (TagList.SelectedIndices.Count < 1)
     {
         MessageBox.Show("Please select one tag from list");
         return;
     }
     string tagname = TagList.SelectedItems[0].Text;
     TagNameLabel.Text = tagname;
     PIPoint pt = PIPoint.FindPIPoint(myPIServer, tagname);
     AFTimeRange timerange = new AFTimeRange();
     timerange.StartTime = new AFTime(StartTimeTextBox.Text);
     timerange.EndTime = new AFTime(EndTimeTextBox.Text);
     AFValues vals = pt.RecordedValues(timerange, OSIsoft.AF.Data.AFBoundaryType.Inside, "", true);
     foreach (AFValue val in vals)
     {
         string[] displayvalues = new string[2];
         displayvalues[0] = val.Timestamp.LocalTime.ToString();
         displayvalues[1] = val.Value.ToString();
         ListViewItem lvi = new ListViewItem(displayvalues);
         ValuesListView.Items.Add(lvi);
     }
 }
Example #51
0
        /// <summary>
        ///  Common calls of getting Data from AFServer
        /// </summary>
        /// <param name="server_database">String representing the server and database</param>
        /// <param name="AttributeName"> name of the attribute</param>
        /// <param name="MatlabName">variable name for the Matlab Workspace</param>
        /// <param name="start">Start time of data collection.</param>
        /// <param name="end">End time of the data collection.</param>
        /// <param name="afobject"> AF object - AFAttribute, AFEventFrame, or PIPoint</param>
        /// <param name="addToListView"> Whether to add to the Listview (generally true)</param>
        public static void getData(string server_database,string AttributeName, string MatlabName, string start, string end, Object afobject, bool addToListView)
        {
            MatlabName = MatlabAccess.modifyMatlabName(MatlabName);
                LogInput logInput = null;
                AFValues Values = new AFValues();
                AFAttribute attribute;

                object[] vals;
                double[] dbVals;
                double[] timestamps = null;
                int[] statuses;
                int baddata;

                //TIME RANGE
                AFTimeRange aftr;
                try { aftr = checkAFTimeRange(start, end); }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                    throw ex;
                }

                /// Get Object
                Type type = afobject.GetType();
                string typestring = type.ToString();

                //  LogInput logInput = new LogInput(MatlabName, Element.Name, attribute.Name, path, aftr);
                switch (type.ToString())
                {
                    case "OSIsoft.AF.Asset.AFAttribute":
                        attribute = (AFAttribute)afobject;
                        logInput = new LogInput(MatlabName, attribute.Element.Name, attribute.Name, attribute.Element.GetPath(), aftr);
                        if (attribute.PIPoint == null) // Constant Value
                        {
                            aftr = new AFTimeRange("*", "*");
                            logInput.setAFTimeRange(aftr);
                            Values = attribute.GetValues(aftr, dataPref, null);
                        }
                        else // PI Point - Time Matters!!
                        {
                            /* Summary: Attribute.GetValues - Important Call!
                             * Parameter Int32 => DataPref
                             *          = 0 :  All Values returned
                             *          < 0 :  Evenly spaced values, including start and end
                             *          > 0 :  # of intervals, for each interval 5 points are given (first, last, high, low, and exceptional)
                             */
                            Values = attribute.GetValues(aftr, dataPref, null); // FULLY UNDERSTAND THIS !!! Important Call!!!!
                        }
                        break;
                    case "OSIsoft.AF.EventFrame.AFEventFrame":
                        AFEventFrame frame = (AFEventFrame)afobject;
                        logInput = new LogInput(MatlabName, frame.Name, frame.Attributes[AttributeName].Name, frame.GetPath(), aftr);
                        attribute = frame.Attributes[AttributeName];
                        logInput.setAFTimeRange(aftr);
                        AFValue val = attribute.GetValue(aftr);
                        Values = new AFValues() { val };
                        break;
                    case "OSIsoft.AF.PI.PIPoint":
                        PIPoint piPoint = (PIPoint)afobject;
                        string path = piPoint.GetPath();
                        logInput = new LogInput(MatlabName, "PI.Point", piPoint.Name, "PI.Point", aftr);
                        Values = piPoint.RecordedValues(aftr, AFBoundaryType.Interpolated, "", true, Int32.MaxValue);
                        break;

                }

                //Determine if AFTIME Absolute String
                isAbsoluteTimeString(start, end, logInput);

                logInput.setAttributeGetValueFormat(dataPref);
                logInput.setTimespaceFormat(dataFormat);

                ConvertAFValues.GetValuesArray(Values, false, out vals, out timestamps, out statuses, out baddata);
                try
                {
                    MatlabAccess.sendDataToMatlab(MatlabName, AFValuesToArray(vals));

                    if (Timestamp)
                        MatlabAccess.sendDataToMatlab(MatlabName + "Time", timestamps);
                }
                catch
                {
                    logInput.setServerDatabase(server_database);
                    LogSystem.addLogInput(server_database, logInput, addToListView);
                    throw new NullReferenceException();
                }

                logInput.setServerDatabase(server_database);
                LogSystem.addLogInput(server_database, logInput, addToListView);
        }
Example #52
0
 // AFTime Range
 /// <summary>
 /// Confirm that a valid start and end time were entered by the user.
 /// </summary>
 /// <param name="start">string of the Start Time.</param>
 /// <param name="end">string of End Time.</param>
 /// <returns> The AFTimeRange created using the start and end strings.</returns>
 private static AFTimeRange checkAFTimeRange(string start, string end)
 {
     AFTimeRange aftr;
     aftr = new AFTimeRange(start, end);
     return aftr;
 }
Example #53
0
        private void DeleteEvents_Click(object sender, EventArgs e)
        {
            //Check TagList's count
            Int32 pointnumber = TagList.Items.Count;
            if (pointnumber < 1)
            {
                MessageBox.Show("Please search PI tags");
                return;
            }
            else
            {
                if (radioButton_AllEvents.Checked)
                {
                    //Delete all events
                    for (int i = 1; i <= pointnumber; ++i)
                    {
                        PIPoint pt = PIPoint.FindPIPoint(myPIServer, TagList.Items[i - 1].Text);
                        AFTimeRange timerange = new AFTimeRange();
                        timerange.StartTime = new AFTime(StartTimeTextBox.Text);
                        timerange.EndTime = new AFTime(EndTimeTextBox.Text);
                        AFValues vals = pt.RecordedValues(timerange, OSIsoft.AF.Data.AFBoundaryType.Inside, "", true);

                        //delete all events within AFValues object
                        if (vals.Count > 0)
                        {
                            pt.UpdateValues(vals, AFUpdateOption.Remove);
                            string[] displayvalues = new string[3];
                            displayvalues[0] = pt.Name;
                            displayvalues[1] = timerange.ToString();
                            displayvalues[2] = "-";
                            ListViewItem lvi = new ListViewItem(displayvalues);
                            UpdateView.Items.Add(lvi);
                        }
                    }
                }
                else
                {
                    //Delete specific events
                    String deletestring = DeleteValueTextBox.Text.ToString();
                    for (int i = 1; i <= pointnumber; ++i)
                    {
                        PIPoint pt = PIPoint.FindPIPoint(myPIServer, TagList.Items[i - 1].Text);
                        AFTimeRange timerange = new AFTimeRange();
                        timerange.StartTime = new AFTime(StartTimeTextBox.Text);
                        timerange.EndTime = new AFTime(EndTimeTextBox.Text);
                        AFValues vals = pt.RecordedValues(timerange, OSIsoft.AF.Data.AFBoundaryType.Inside, "", true);
                        String Checktime = null;
                        String Checkvalue = null;
                        foreach (AFValue val in vals)
                        {
                            Checktime = val.Timestamp.LocalTime.ToString();
                            Checkvalue = ReadPIValueString(val);
                            if (Checkvalue == deletestring)
                            {
                                pt.UpdateValue(val, AFUpdateOption.Remove);
                                string[] displayvalues = new string[3];
                                displayvalues[0] = pt.Name;
                                displayvalues[1] = Checktime;
                                displayvalues[2] = Checkvalue;
                                ListViewItem lvi = new ListViewItem(displayvalues);
                                UpdateView.Items.Add(lvi);
                            }
                        }
                    }
                }
            }
        }
        private void AddTrendButton_Click(object sender, EventArgs e)
        {
            if (EFListView.SelectedItems.Count >= 1 && EFAttrView.SelectedItems.Count >= 1)
            {
                for (int i = 0; i < EFListView.SelectedItems.Count; i++)
                {
                    for (int j = 0; j < EFAttrView.SelectedItems.Count; j++)
                    {
                        ListViewItem SelectedEF = EFListView.SelectedItems[i];
                        ListViewItem SelectedEFattr = EFAttrView.SelectedItems[j];
                        //Get EFName and Attribute Name
                        String EFName = SelectedEF.SubItems[0].Text;
                        String EFattrName = SelectedEFattr.SubItems[0].Text;
                        String title = num+ "_" + EFName + " : " + EFattrName;
                        
                        //Create GUID for Selected Event Frame
                        Guid myEFGUID = Guid.Empty;
                        String myguidstring = SelectedEF.SubItems[4].Text;
                        try { myEFGUID = Guid.Parse(myguidstring); }
                        catch { MessageBox.Show("Cannot convert GUID"); }

                        //Find Selected Event Frame
                        AFEventFrame myEF = AFEventFrame.FindEventFrame(myAFServer, myEFGUID);
                        AFTime startTime = myEF.StartTime;
                        AFTime endTime = myEF.EndTime;
                        //Set endtime as now for not finishing event frame
                        if (endTime > new AFTime("2099/1/1"))
                        {
                            endTime = DateTime.Now;
                        }
                        AFTimeRange timerange = new AFTimeRange(startTime, endTime);
                        //Find Selected Attribute
                        AFAttribute myEFAttr = myEF.Attributes[SelectedEFattr.Text];

                        DateTime trendstarttime = new DateTime(0);
                        //Check time difference between start time and end time
                        TimeSpan timedif = endTime - startTime;
                        try
                        {
                            AFValues vals = myEFAttr.Data.PlotValues(timerange, 100, null);
                            Int32 chk = 0;
                            foreach (AFValue val in vals)
                            {
                                //Sometimes System.InvalidOperationException happens.
                                Type t = val.Value.GetType();
                                if (t.FullName != "System.InvalidOperationException")
                                {
                                    if (chk == 0)
                                    {
                                        //Add line to chart1
                                        try
                                        {
                                            //add trend to chart1
                                            chart1.Series.Add(title);
                                            chart1.Series[title].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
                                            chart1.Series[title].BorderWidth = 2; // Line width
                                            chart1.Series[title].ToolTip = "#SERIESNAME\r\nValue : #VALY{N2}\r\nTime : #VALX{N0}";
                                            chart1.ChartAreas[0].AxisX.Title = "Seconds";
                                            //Zoom function
                                            chart1.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;
                                            chart1.ChartAreas[0].CursorY.IsUserSelectionEnabled = true;
                                        }
                                        catch (Exception ex)
                                        {
                                            MessageBox.Show(ex.Message);
                                            return;
                                        }
                                        trendstarttime = val.Timestamp.LocalTime;
                                    }
                                    timedif = val.Timestamp.LocalTime - trendstarttime;
                                    
                                    //Displaying EnumerationSet value as number
                                    if (t.FullName == "OSIsoft.AF.Asset.AFEnumerationValue")
                                    {
                                        //Errchk = 1;
                                        AFEnumerationValue MyEnumerationValue = (AFEnumerationValue)val.Value;
                                        // last value will be returned as 248. So ignore it
                                        if (MyEnumerationValue.Value != 248)
                                        {
                                            chart1.Series[title].Points.AddXY(timedif.TotalSeconds, MyEnumerationValue.Value.ToString());
                                        }
                                    }
                                    else
                                    {
                                        chart1.Series[title].Points.AddXY(timedif.TotalSeconds, val.Value.ToString());
                                    }
                                    chk = 1; 
                                }
                                else
                                {
                                    Errchk = 1;
                                    //Write code for System.InvalidOperationException - Currently ignore it
                                    //AFValue val2 = myEFAttr.GetValue(endTime,null);
                                    //timedif = endTime - startTime;
                                    //chart1.Series[title].Points.AddXY(0, val2.Value.ToString());
                                    //chart1.Series[title].Points.AddXY(timedif.TotalSeconds, val2.Value.ToString());
                                }
                            }
                        }
                        catch
                        {
                            Errchk = 1;
                            //If error happens, write code - Currently ignore it
                            //AFValue val = myEFAttr.GetValue(endTime,null);
                            //chart1.Series[title].Points.AddXY(0, val.Value.ToString());
                            //chart1.Series[title].Points.AddXY(timedif.TotalSeconds, val.Value.ToString());
                        }
                        if (Errchk == 0)
                        {
                            //If there is no error, Set minimum and maximum time
                            chart1.ChartAreas[0].AxisX.Minimum = 0;
                            if (maxtimedif > timedif)
                            {
                                chart1.ChartAreas[0].AxisX.Maximum = maxtimedif.TotalSeconds;
                            }
                            else
                            {
                                chart1.ChartAreas[0].AxisX.Maximum = timedif.TotalSeconds;
                                maxtimedif = timedif;
                            }
                            ++num;
                        }
                        Errchk = 0;                       
                    }
                }
            }
        }
Example #55
0
		public void InterpolatedValues(WAFTime start, WAFTime end, string interval, string filterExp, bool includeFiltVals, ref WAFValue[] values)
		{
			List<WAFValue> retVals = null;

			try
			{
				AFTimeRange range = new AFTimeRange(start.ToString(), end.ToString());
				
				AFTimeSpan span = new AFTimeSpan();
				if (!AFTimeSpan.TryParse(interval, out span))
				{
					span = AFTimeSpan.Parse("1h");
				}

				AFValues vals = m_Pipt.InterpolatedValues(range, span, filterExp, includeFiltVals);

				retVals = vals.ConvertAll(new Converter<AFValue, WAFValue>(WAFValue.AFValueToWAFValue));
			}
			catch (Exception ex)
			{
				
				System.Console.WriteLine(ex.Message);
			}

			values = retVals.ToArray();
			return;
		}
        public static async Task<IList<IDictionary<AFSummaryTypes, AFValue>>> GetSummariesAsyncWithTimeout(AFAttributeList attributeList, int timeoutInMilliseconds)
        {
            // Use a "competing tasks" pattern to place timeout on multiple async requests

            Console.WriteLine("Calling GetSummariesAsyncWithTimeout");

            CancellationTokenSource cts = new CancellationTokenSource();
            CancellationToken token = cts.Token;
            CancellationTokenSource ctsForTimer = new CancellationTokenSource();
            CancellationToken tokenForTimer = ctsForTimer.Token;

            Task<IDictionary<AFSummaryTypes, AFValue>>[] tasks = attributeList
                // Do not make the call if async is not supported
                .Where(attr => (attr.SupportedDataMethods & AFDataMethods.Asynchronous) == AFDataMethods.Asynchronous)
                .Select(async attr =>
                {
                    try
                    {
                        AFSummaryTypes mySummaries = AFSummaryTypes.Minimum | AFSummaryTypes.Maximum | AFSummaryTypes.Average | AFSummaryTypes.Total;
                        AFTimeRange timeRange = new AFTimeRange(new AFTime("*-1d"), new AFTime("*"));

                        return await attr.Data.SummaryAsync(
                            timeRange: timeRange,
                            summaryType: mySummaries,
                            calculationBasis: AFCalculationBasis.TimeWeighted,
                            timeType: AFTimestampCalculation.Auto,
                            cancellationToken: token);
                    }
                    catch (AggregateException ae)
                    {
                        Console.WriteLine("{0}: {1}", attr.Element.Name, ae.Flatten().InnerException.Message);
                        return null;
                    }
                    catch (OperationCanceledException oe)
                    {
                        Console.WriteLine("{0}: {1}", attr.Element.Name, oe.Message);
                        return null;
                    }
                })
                .ToArray();

            // Define a task that completes when all subtasks are complete
            Task<IDictionary<AFSummaryTypes, AFValue>[]> task = Task.WhenAll(tasks);

            // Asychronously wait for either the summaries or timer task to complete
            if (await Task.WhenAny(task, Task.Delay(timeoutInMilliseconds, tokenForTimer)) == task)
            {
                // Cancel the timer task
                ctsForTimer.Cancel();
                // Return summaries result
                return task.Result;
            }
            else
            {
                // Cancel the summaries task if timeout
                cts.Cancel();
                throw new TimeoutException("The operation has timed out.");
            }
        }
        static void PrintDailyAverageEnergyUsage(AFDatabase database, string startTime, string endTime)
        {
            Console.WriteLine(string.Format("Print Daily Energy Usage - Start: {0}, End: {1}", startTime, endTime));

            AFAttributeList attrList = GetAttributes(database, "MeterBasic", "Energy Usage");

            AFTime start = new AFTime(startTime);
            AFTime end = new AFTime(endTime);
            AFTimeRange timeRange = new AFTimeRange(start, end);

            // Ask for 100 PI Points at a time
            PIPagingConfiguration pagingConfig = new PIPagingConfiguration(PIPageType.TagCount, 100);

            IEnumerable<IDictionary<AFSummaryTypes, AFValues>> summaries = attrList.Data.Summaries(
                timeRange: timeRange,
                summaryDuration: new AFTimeSpan(TimeSpan.FromDays(1)),
                summaryTypes: AFSummaryTypes.Average,
                calculationBasis: AFCalculationBasis.TimeWeighted,
                timeType: AFTimestampCalculation.EarliestTime,
                pagingConfig: pagingConfig);

            // Loop through attributes
            foreach (IDictionary<AFSummaryTypes, AFValues> dict in summaries)
            {
                AFValues values = dict[AFSummaryTypes.Average];
                Console.WriteLine("Averages for Meter: {0}", values.Attribute.Element.Name);

                // Loop through values per attribute
                foreach (AFValue val in dict[AFSummaryTypes.Average])
                {
                    Console.WriteLine("Timestamp (Local): {0}, Avg. Value (kWh): {1}",
                        val.Timestamp.LocalTime,
                        val.Value);
                }
                Console.WriteLine();

            }
            Console.WriteLine();
        }
        public static int UpdateAttributeData(AFDatabase database)
        {
            AFAttributeList attributeList = GetAttributes(database);

            AFTimeRange timeRange = new AFTimeRange(new AFTime(string.Format("{0}-{1}-01", DateTime.Now.Year, DateTime.Now.Month)), new AFTime("T"));
            AFTimeSpan hourInterval = new AFTimeSpan(0, 0, 0, 1);

            // Question: What risk is run by the following code?
            List<Task<AFErrors<AFValue>>> processWrites = new List<Task<AFErrors<AFValue>>>();
            foreach (AFAttribute attribute in attributeList)
            {
                AFValues vals = GenerateValueSequence(attribute, timeRange.StartTime, timeRange.EndTime, hourInterval);
                processWrites.Add(attribute.Data.UpdateValuesAsync(vals, AFUpdateOption.Insert, AFBufferOption.DoNotBuffer));
            }

            int errorcount = 0;
            try
            {
                Task.WaitAll(processWrites.ToArray());
                foreach (var item in processWrites)
                {
                    AFErrors<AFValue> errors = item.Result;
                    // Count PIPoint errors
                    if (errors != null && errors.HasErrors)
                        errorcount += errors.Errors.Count;

                    // Report PI Server, AF Server errors
                }
            }
            catch (AggregateException ae)
            {
            }

            return errorcount;
        }
        static void SwapValues(AFDatabase database, string meter1, string meter2, string startTime, string endTime)
        {
            Console.WriteLine(string.Format("Swap values for meters: {0}, {1} between {2} and {3}", meter1, meter2, startTime, endTime));

            // NOTE: This method does not ensure that there is no data loss if there is failure.
            // Persist the data first in case you need to rollback.

            AFAttribute attr1 = AFAttribute.FindAttribute(@"\Meters\" + meter1 + @"|Energy Usage", database);
            AFAttribute attr2 = AFAttribute.FindAttribute(@"\Meters\" + meter2 + @"|Energy Usage", database);

            AFTime start = new AFTime(startTime);
            AFTime end = new AFTime(endTime);
            AFTimeRange timeRange = new AFTimeRange(start, end);

            // Get values to delete for meter1
            AFValues valsToRemove1 = attr1.Data.RecordedValues(
                timeRange: timeRange,
                boundaryType: AFBoundaryType.Inside,
                desiredUOM: null,
                filterExpression: null,
                includeFilteredValues: false);

            // Get values to delete for meter2
            AFValues valsToRemove2 = attr2.Data.RecordedValues(
                timeRange: timeRange,
                boundaryType: AFBoundaryType.Inside,
                desiredUOM: null,
                filterExpression: null,
                includeFilteredValues: false);

            List<AFValue> valsToRemove = valsToRemove1.ToList();
            valsToRemove.AddRange(valsToRemove2.ToList());

            // Remove the values
            AFListData.UpdateValues(valsToRemove, AFUpdateOption.Remove);

            // Create new AFValues from the other meter and assign them to this meter
            List<AFValue> valsToAdd1 = valsToRemove2.Select(v => new AFValue(attr1, v.Value, v.Timestamp)).ToList();
            List<AFValue> valsToAdd2 = valsToRemove1.Select(v => new AFValue(attr2, v.Value, v.Timestamp)).ToList();

            List<AFValue> valsCombined = valsToAdd1;
            valsCombined.AddRange(valsToAdd2);

            AFListData.UpdateValues(valsCombined, AFUpdateOption.Insert);
            Console.WriteLine();
        }