//******************************************************************************************************
// 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
}
}