//****************************************************************************************************** // StatisticsReader.cs - Gbtc // // Copyright © 2012, 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: // ---------------------------------------------------------------------------------------------------- // 12/14/2010 - Stephen C. Wills // Generated original version of source code. // 12/13/2012 - Starlynn Danyelle Gilliam // Modified Header. // //****************************************************************************************************** using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml; using GSF.Historian; using GSF.Historian.Files; namespace HistorianAdapters { /// /// Reads certain device and input stream statistics from the statistics archive. /// The statistics that are read are the total frame count, the missing frame count, /// connectivity, average latency, the actual data rate, data quality errors, and /// time quality errors. /// public class StatisticsReader : IDisposable { #region [ Members ] // Fields private ArchiveFile m_archive; private readonly Dictionary> m_totalFrames; private readonly Dictionary> m_missingFrames; private readonly Dictionary> m_connectedStats; private readonly Dictionary> m_averageLatency; private readonly Dictionary> m_actualDataRate; private readonly Dictionary> m_dataQualityErrors; private readonly Dictionary> m_timeQualityErrors; private bool m_disposed; #endregion #region [ Constructors ] /// /// Creates a new instance of the class. /// All parameters--, , /// , , /// , and --must be set manually /// before attempting to read statistics from the archive. /// public StatisticsReader() { m_totalFrames = new Dictionary>(); m_missingFrames = new Dictionary>(); m_connectedStats = new Dictionary>(); m_averageLatency = new Dictionary>(); m_actualDataRate = new Dictionary>(); m_dataQualityErrors = new Dictionary>(); m_timeQualityErrors = new Dictionary>(); } /// /// Creates a new instance of the class. /// The configuration file specified in the parameter must contain the statistics /// historian records in order to automatically locate the archive files. /// Otherwise, the other constructor should be used. The and /// parameters are not set when using this constructor and must /// be set manually before attempting to read statistics from the archive. /// /// Path to the source configuration file. public StatisticsReader(string sourceConfigPath) : this() { XmlDocument doc; XmlNode archiveFileNode; XmlNode stateFileNode; XmlNode intercomFileNode; XmlNode metadataFileNode; if (!File.Exists(sourceConfigPath)) throw new FileNotFoundException(sourceConfigPath); doc = new XmlDocument(); doc.Load(sourceConfigPath); archiveFileNode = doc.SelectSingleNode("configuration/categorizedSettings/statArchiveFile"); stateFileNode = doc.SelectSingleNode("configuration/categorizedSettings/statStateFile"); intercomFileNode = doc.SelectSingleNode("configuration/categorizedSettings/statIntercomFile"); metadataFileNode = doc.SelectSingleNode("configuration/categorizedSettings/statMetadataFile"); if ((object)archiveFileNode != null) ArchiveFilePath = archiveFileNode.ChildNodes.Cast().Single(node => node.Name == "FileName").Value; if ((object)stateFileNode != null) StateFilePath = stateFileNode.ChildNodes.Cast().Single(node => node.Name == "FileName").Value; if ((object)intercomFileNode != null) IntercomFilePath = intercomFileNode.ChildNodes.Cast().Single(node => node.Name == "FileName").Value; if ((object)metadataFileNode != null) MetadataFilePath = metadataFileNode.ChildNodes.Cast().Single(node => node.Name == "FileName").Value; } #endregion #region [ Properties ] /// /// Path to the archive file (*_archive.d). /// public string ArchiveFilePath { get; set; } /// /// Path to the state file (*_startup.dat). /// public string StateFilePath { get; set; } /// /// Path to the intercom file (scratch.dat). /// public string IntercomFilePath { get; set; } /// /// Path to the metadata file (*_dbase.dat). /// public string MetadataFilePath { get; set; } /// /// The timestamp at which to start gathering statistics from the archive. /// public DateTime StartTime { get; set; } /// /// The timestamp at which to stop gathering statistics from the archive. /// public DateTime EndTime { get; set; } /// /// After the has read statistics from the archive file, /// this will contain the statistics on the total number of frames. /// public Dictionary> TotalFrames { get { return m_totalFrames; } } /// /// After the has read statistics from the archive file, /// this will contain the statistics on missing frames. /// public Dictionary> MissingFrames { get { return m_missingFrames; } } /// /// After the has read statistics from the archive file, /// this will contain the statistics on input stream connectivity. /// public Dictionary> ConnectedStats { get { return m_connectedStats; } } /// /// After the has read statistics from the archive file, /// this will contain the statistics on the average latency. /// public Dictionary> AverageLatency { get { return m_averageLatency; } } /// /// After the has read statistics from the archive file, /// this will contain the statistics on the actual data rate. /// public Dictionary> ActualDataRate { get { return m_actualDataRate; } } /// /// After the has read statistics from the archive file, /// this will contain the statistics on data quality errors. /// public Dictionary> DataQualityErrors { get { return m_dataQualityErrors; } } /// /// After the has read statistics from the archive file, /// this will contain the statistics on time quality errors. /// public Dictionary> TimeQualityErrors { get { return m_timeQualityErrors; } } #endregion #region [ Methods ] /// /// Causes the to open the archive file and retrieve the statistics. /// public void Open() { IEnumerable records; string signalReference; int index; if (m_archive != null) throw new InvalidOperationException("The StatisticsReader must be closed before reopening."); m_archive = OpenArchiveFile(); records = m_archive.MetadataFile.Read(); foreach (MetadataRecord record in records) { signalReference = record.Synonym1; index = signalReference.LastIndexOf('!'); if (index >= 0) { switch (signalReference.Substring(index)) { case "!IS-ST1": m_totalFrames.Add(record, m_archive.ReadData(record.HistorianID, StartTime, EndTime)); break; case "!IS-ST3": m_missingFrames.Add(record, m_archive.ReadData(record.HistorianID, StartTime, EndTime)); break; case "!IS-ST8": m_connectedStats.Add(record, m_archive.ReadData(record.HistorianID, StartTime, EndTime)); break; case "!IS-ST14": m_averageLatency.Add(record, m_archive.ReadData(record.HistorianID, StartTime, EndTime)); break; case "!IS-ST17": m_actualDataRate.Add(record, m_archive.ReadData(record.HistorianID, StartTime, EndTime)); break; case "!PMU-ST1": m_dataQualityErrors.Add(record, m_archive.ReadData(record.HistorianID, StartTime, EndTime)); break; case "!PMU-ST2": m_timeQualityErrors.Add(record, m_archive.ReadData(record.HistorianID, StartTime, EndTime)); break; } } } } /// /// Closes the archive file once the statistics are no longer needed. /// public void Close() { Dispose(); } /// /// Releases all the resources used by the object. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Releases the unmanaged resources used by the object and optionally releases the managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool disposing) { if (!m_disposed) { try { // This will be done regardless of whether the object is finalized or disposed. if (disposing) { m_archive?.Close(); m_archive = null; } } finally { m_disposed = true; // Prevent duplicate dispose. } } } // Opens the archive file in order to retrieve the statistics. private ArchiveFile OpenArchiveFile() { ArchiveFile file = new ArchiveFile { FileName = ArchiveFilePath, FileAccessMode = FileAccess.Read, StateFile = new StateFile { FileAccessMode = FileAccess.Read, FileName = StateFilePath }, IntercomFile = new IntercomFile { FileAccessMode = FileAccess.Read, FileName = IntercomFilePath }, MetadataFile = new MetadataFile { FileAccessMode = FileAccess.Read, FileName = MetadataFilePath, LoadOnOpen = true } }; file.Open(); return file; } #endregion } }