1152 lines · 50.1 KB · C#
RawDownload
//******************************************************************************************************//  CompletenessReportGenerator.cs - Gbtc////  Copyright © 2014, Grid Protection Alliance.  All Rights Reserved.////  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See//  the NOTICE file distributed with this work for additional information regarding copyright ownership.//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may//  not use this file except in compliance with the License. You may obtain a copy of the License at:////      http://www.opensource.org/licenses/MIT////  Unless agreed to in writing, the subject software distributed under the License is distributed on an//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the//  License for the specific language governing permissions and limitations.////  Code Modification History://  ----------------------------------------------------------------------------------------------------//  02/11/2014 - Stephen C. Wills//       Generated original version of source code.////****************************************************************************************************** using System;using System.Collections.Generic;using System.Drawing;using System.Drawing.Imaging;using System.IO;using System.Linq;using System.Text;using System.Windows.Forms.DataVisualization.Charting;using GSF.Historian;using GSF.Historian.Files;using Root.Reports;using Encoder = System.Drawing.Imaging.Encoder; namespace StatHistorianReportGenerator{    /// <summary>    /// Generates GSF Data Completeness Report for a specified date.    /// </summary>    public class CompletenessReportGenerator    {        #region [ Members ]         // Nested Types        private class DeviceStats        {            public string Name;            public double[] DataQualityErrors;            public double[] TimeQualityErrors;            public double[] MeasurementsReceived;            public double[] MeasurementsExpected;        }         // Constants         // Note: Many of the calculations in this class use conversion        // factors of 96 dots per inch and 25.4 millimeters per inch         private const int ReportDays = 35;  // Gather data for 35 days - necessary for 5-day history summary        private const int Month = 30;       // 1 month = 30 days for reporting purposes         private const double PageMarginMillimeters = 25.4D;             // 1-inch margin        private const double PageWidthMillimeters = 8.5D * 25.4D;       // 8.5 inch width        private const double PageHeightMillimeters = 11.0D * 25.4D;     // 11 inch height         private const double FooterHeightMillimeters = (10.0D / 72.0D) * 25.4D;        private const double SpacingMillimeters = 6.0D;         // Fields        private string m_archiveLocation;        private string m_titleText;        private string m_companyText;        private DateTime m_reportDate;        private double m_level4Threshold;        private double m_level3Threshold;        private string m_level4Alias;        private string m_level3Alias;         private List<DeviceStats> m_deviceStatsList;         #endregion         #region [ Constructors ]         /// <summary>        /// Creates a new instance of the <see cref="CompletenessReportGenerator"/> class.        /// </summary>        public CompletenessReportGenerator()        {            m_titleText = "GSF Data Completeness Report";            m_companyText = "Grid Protection Alliance";            m_reportDate = DateTime.UtcNow.Date;            m_level4Threshold = 99.0D;            m_level3Threshold = 90.0D;        }         #endregion         #region [ Properties ]         /// <summary>        /// Gets or sets the location of the statistics archive that provides data for reporting.        /// </summary>        public string ArchiveLocation        {            get            {                return m_archiveLocation;            }            set            {                m_archiveLocation = value;            }        }         /// <summary>        /// Gets or sets the text that appears in the title section of the generated report.        /// </summary>        public string TitleText        {            get            {                return m_titleText;            }            set            {                m_titleText = value;            }        }         /// <summary>        /// Gets or sets the text that appears in the company section of the generated report.        /// </summary>        public string CompanyText        {            get            {                return m_companyText;            }            set            {                m_companyText = value;            }        }         /// <summary>        /// Gets or sets the date for which the report will be generated.        /// </summary>        public DateTime ReportDate        {            get            {                return m_reportDate;            }            set            {                m_reportDate = value.ToUniversalTime().Date;            }        }         /// <summary>        /// Gets or sets the completeness threshold at which devices will be considered to be in Level 4.        /// </summary>        public double Level4Threshold        {            get            {                return m_level4Threshold;            }            set            {                m_level4Threshold = value;                 if (m_level4Threshold > 100.0D)                    m_level4Threshold = 100.0D;                 if (m_level4Threshold < 0.0D)                    m_level4Threshold = 0.0D;                 if (m_level4Threshold < m_level3Threshold)                    m_level3Threshold = m_level4Threshold;            }        }         /// <summary>        /// Gets or sets the completeness threshold at which devices will be considered to be in Level 3.        /// </summary>        public double Level3Threshold        {            get            {                return m_level3Threshold;            }            set            {                m_level3Threshold = value;                 if (m_level3Threshold > 100.0D)                    m_level3Threshold = 100.0D;                 if (m_level3Threshold < 0.0D)                    m_level3Threshold = 0.0D;                 if (m_level3Threshold > m_level4Threshold)                    m_level4Threshold = m_level3Threshold;            }        }         /// <summary>        /// Gets or sets the alias to be associated with the Level 4 category.        /// </summary>        public string Level4Alias        {            get            {                return m_level4Alias;            }            set            {                m_level4Alias = value;            }        }         /// <summary>        /// Gets or sets the alias to be associated with the Level 3 category.        /// </summary>        public string Level3Alias        {            get            {                return m_level3Alias;            }            set            {                m_level3Alias = value;            }        }         #endregion         #region [ Methods ]         /// <summary>        /// Generates the report.        /// </summary>        /// <returns>The report that was generated.</returns>        public Report GenerateReport()        {            DateTime now = DateTime.Now;            Report report = new Report();            FontDef fontDefinition = new FontDef(report, "Helvetica");            Page pageOne = CreatePage(report);            Page pageTwo = CreatePage(report);            double verticalMillimeters;             ReadStatistics();             // Page one            verticalMillimeters = PageMarginMillimeters;            verticalMillimeters += InsertTitle(fontDefinition, pageOne, verticalMillimeters) + SpacingMillimeters;            verticalMillimeters += InsertReportDate(fontDefinition, pageOne, verticalMillimeters) + 2.0D * SpacingMillimeters;             verticalMillimeters += InsertSectionHeader(fontDefinition, pageOne, verticalMillimeters, "5-day Device Data Completeness");            verticalMillimeters += InsertFiveDaySummary(report, fontDefinition, pageOne, verticalMillimeters) + SpacingMillimeters;             verticalMillimeters += InsertSectionHeader(fontDefinition, pageOne, verticalMillimeters, "Percent of Devices with Acceptable Quality (30 days)");            verticalMillimeters += InsertBarChart(pageOne, verticalMillimeters) + SpacingMillimeters;             verticalMillimeters += InsertSectionHeader(fontDefinition, pageOne, verticalMillimeters, "Definitions");            verticalMillimeters += InsertDefinitions(fontDefinition, pageOne, verticalMillimeters) + SpacingMillimeters;            InsertFooter(fontDefinition, pageOne, now, 1);             // Page two            verticalMillimeters = PageMarginMillimeters;            verticalMillimeters += InsertReportDate(fontDefinition, pageTwo, verticalMillimeters) + SpacingMillimeters;             verticalMillimeters += InsertSectionHeader(fontDefinition, pageTwo, verticalMillimeters, "Data Completeness Breakdown");            verticalMillimeters += InsertPieChart(fontDefinition, pageTwo, verticalMillimeters) + SpacingMillimeters;             InsertDetailsList(report, fontDefinition, pageTwo, verticalMillimeters, now, 2);             return report;        }         // Creates a page and sets width and height to standard 8.5x11 inches.        private Page CreatePage(Report report)        {            Page page = new Page(report);            page.rWidthMM = PageWidthMillimeters;            page.rHeightMM = PageHeightMillimeters;            return page;        }         // Reads statistics from the archive and stores them in a format that can        // be easily manipulated to determine which devices belong in which levels.        private void ReadStatistics()        {            Dictionary<string, DeviceStats> deviceStatsLookup = new Dictionary<string, DeviceStats>();            DateTime startTime;            DateTime endTime;             ArchiveLocator locator;            Dictionary<MetadataRecord, IEnumerable<IDataPoint>> measurementsReceived;            Dictionary<MetadataRecord, IEnumerable<IDataPoint>> measurementsExpected;            Dictionary<MetadataRecord, IEnumerable<IDataPoint>> dataQualityErrors;            Dictionary<MetadataRecord, IEnumerable<IDataPoint>> timeQualityErrors;             MetadataRecord dataQualityRecord;            MetadataRecord timeQualityRecord;             DeviceStats deviceStats;            string signalReference;            string deviceName;            int index;             // Create the statistics reader for reading statistics from the archive            using (StatisticsReader statisticsReader = new StatisticsReader())            {                // Create the archive locator to                // determine the location of the archive                locator = new ArchiveLocator()                {                    ArchiveLocation = m_archiveLocation,                    ArchiveLocationName = "Statistics",                    ArchiveName = "STAT"                };                 endTime = m_reportDate + TimeSpan.FromDays(1);                startTime = endTime - TimeSpan.FromDays(ReportDays);                 // Set up and open the statistics reader                statisticsReader.StartTime = startTime;                statisticsReader.EndTime = endTime;                statisticsReader.ArchiveFilePath = locator.ArchiveFilePath;                statisticsReader.Open();                 measurementsReceived = statisticsReader.Read("PMU", 4);                measurementsExpected = statisticsReader.Read("PMU", 5);                dataQualityErrors = statisticsReader.Read("PMU", 1);                timeQualityErrors = statisticsReader.Read("PMU", 2);                 // Determine which devices in the archive have stats for both measurements received and measurements expected                foreach (Tuple<MetadataRecord, MetadataRecord> tuple in measurementsReceived.Keys.Join(measurementsExpected.Keys, GetDeviceName, GetDeviceName, Tuple.Create))                {                    signalReference = tuple.Item1.Synonym1;                    deviceName = GetDeviceName(tuple.Item1);                     // Ignore statistics that were calculated by an intermediate gateway                    if (!signalReference.StartsWith("LOCAL$") && signalReference.Contains("LOCAL$"))                        continue;                     // Make sure LOCAL$ statistics take precedence over other statistics calculated for the same device                    if (deviceStatsLookup.ContainsKey(deviceName) && !signalReference.StartsWith("LOCAL$"))                        continue;                     dataQualityRecord = dataQualityErrors.Keys.FirstOrDefault(record => GetDeviceName(record) == deviceName);                    timeQualityRecord = timeQualityErrors.Keys.FirstOrDefault(record => GetDeviceName(record) == deviceName);                     // Create arrays to hold the total sum of the stats for each day being reported                    deviceStats = new DeviceStats()                    {                        Name = deviceName,                        DataQualityErrors = new double[ReportDays],                        TimeQualityErrors = new double[ReportDays],                        MeasurementsReceived = new double[ReportDays],                        MeasurementsExpected = new double[ReportDays]                    };                     if ((object)dataQualityRecord != null)                    {                        // Calculate the total data quality errors for each day being reported                        foreach (IDataPoint dataPoint in dataQualityErrors[dataQualityRecord])                        {                            index = (dataPoint.Time.ToDateTime() - startTime).Days;                             if (index >= 0 && index < ReportDays)                                deviceStats.DataQualityErrors[index] += dataPoint.Value;                        }                    }                     if ((object)timeQualityRecord != null)                    {                        // Calculate the total time quality errors for each day being reported                        foreach (IDataPoint dataPoint in timeQualityErrors[timeQualityRecord])                        {                            index = (dataPoint.Time.ToDateTime() - startTime).Days;                             if (index >= 0 && index < ReportDays)                                deviceStats.TimeQualityErrors[index] += dataPoint.Value;                        }                    }                     // Calculate the total measurements received for each day being reported                    foreach (IDataPoint dataPoint in measurementsReceived[tuple.Item1])                    {                        index = (dataPoint.Time.ToDateTime() - startTime).Days;                         if (index >= 0 && index < ReportDays)                            deviceStats.MeasurementsReceived[index] += dataPoint.Value;                    }                     // Calculate the total measurements expected for each day being reported                    foreach (IDataPoint dataPoint in measurementsExpected[tuple.Item2])                    {                        index = (dataPoint.Time.ToDateTime() - startTime).Days;                         if (index >= 0 && index < ReportDays)                            deviceStats.MeasurementsExpected[index] += dataPoint.Value;                    }                     // Store the calculated stats per device                    deviceStatsLookup[deviceName] = deviceStats;                }            }             // Store the statistics data to be used in the report            m_deviceStatsList = deviceStatsLookup.Values.ToList();        }         // Inserts the report date onto the given page.        private double InsertReportDate(FontDef fontDefinition, Page page, double verticalMillimeters)        {            FontProp font = new FontProp(fontDefinition, 0.0D);            font.rSizePoint = 14.0D;            page.AddCB_MM(verticalMillimeters + font.rSizeMM, new RepString(font, m_reportDate.ToLongDateString()));            return font.rSizeMM;        }         // Inserts the given text as a section header (16-pt, bold).        private double InsertSectionHeader(FontDef fontDefinition, Page page, double verticalMillimeters, string text)        {            FontProp font = new FontProp(fontDefinition, 0.0D);            font.rSizePoint = 16.0D;            font.bBold = true;            page.AddCB_MM(verticalMillimeters + font.rSizeMM, new RepString(font, text));            return 2.0D * font.rSizeMM;        }         // Inserts the page footer onto the given page, which includes the time of report generation as well as the page number.        private double InsertFooter(FontDef fontDefinition, Page page, DateTime now, int pageNumber)        {            FontProp font = new FontProp(fontDefinition, 0.0D);            font.rSizePoint = 12.0D;            page.AddMM(PageMarginMillimeters, PageHeightMillimeters - PageMarginMillimeters, new RepString(font, now.ToShortDateString() + " " + now.ToShortTimeString()));            page.AddMM(PageWidthMillimeters - PageMarginMillimeters - font.rGetTextWidthMM(pageNumber.ToString()), PageHeightMillimeters - PageMarginMillimeters, new RepString(font, pageNumber.ToString()));            return font.rSizeMM;        }         // Inserts the title and company text on the given page.        private double InsertTitle(FontDef fontDefinition, Page page, double verticalMillimeters)        {            FontProp titleFont = new FontProp(fontDefinition, 0.0D);            FontProp companyFont = new FontProp(fontDefinition, 0.0D);             titleFont.rSizePoint = 20.0D;            companyFont.rSizePoint = 14.0D;            titleFont.bBold = true;            companyFont.bBold = true;             // Title            page.AddCB_MM(verticalMillimeters + titleFont.rSizeMM, new RepString(titleFont, m_titleText));            verticalMillimeters += 1.5D * titleFont.rSizeMM;             // Company            page.AddCB_MM(verticalMillimeters + companyFont.rSizeMM, new RepString(companyFont, m_companyText));             return (1.5D * titleFont.rSizeMM) + companyFont.rSizeMM;        }         // Inserts the five-day summary table on the given page.        private double InsertFiveDaySummary(Report report, FontDef fontDefinition, Page page, double verticalMillimeters)        {            const double TableHeightMillimeters = 40.0D;            const double ColumnPadding = 5.0D;            const double RowPadding = 2.0D;            const int RowCount = 7;             List<DeviceStats>[] levels;            string[] levelAliases;            double horizontalMillimeters;            double tableWidthMillimeters;            double rowHeightMillimeters;             string[] labelText;            FontProp[] labelFonts;            double labelsWidthMillimeters;             string[] dayOneText;            FontProp[] dayOneFonts;            double dayOneWidthMillimeters;             string[] dayTwoText;            FontProp[] dayTwoFonts;            double dayTwoWidthMillimeters;             string[] dayThreeText;            FontProp[] dayThreeFonts;            double dayThreeWidthMillimeters;

Lines 1–500 of 1152.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t