Unified · Split
| @@ -29,8 +29,13 @@ using System.IO; · 28 unmodified lines | ||
| 29 | 29 | using System.Linq; |
| 30 | 30 | using System.Text; |
| 31 | 31 | using System.Windows.Forms.DataVisualization.Charting; |
| 32 | +using System.Xml.Linq; | |
| 33 | +using GSF.Configuration; | |
| 34 | +using GSF.Data; | |
| 35 | +using GSF.Data.Model; | |
| 32 | 36 | using GSF.Historian; |
| 33 | 37 | using GSF.Historian.Files; |
| 38 | +using GSF.IO; | |
| 34 | 39 | using GSF.Units; |
| 35 | 40 | using Root.Reports; |
| 36 | 41 | using Encoder = System.Drawing.Imaging.Encoder; |
| @@ -54,6 +59,15 @@ namespace StatHistorianReportGenerator · 17 unmodified lines | ||
| 54 | 59 | public double[] MeasurementsExpected; |
| 55 | 60 | } |
| 56 | 61 | |
| 62 | + private class DataAvailability | |
| 63 | + { | |
| 64 | + [PrimaryKey(true)] | |
| 65 | + public int ID { get; set; } | |
| 66 | + public float GoodAvailableData { get; set; } | |
| 67 | + public float BadAvailableData { get; set; } | |
| 68 | + public float TotalAvailableData { get; set; } | |
| 69 | + } | |
| 70 | + | |
| 57 | 71 | // Constants |
| 58 | 72 | |
| 59 | 73 | // Note: Many of the calculations in this class use conversion |
| @@ -285,9 +299,12 @@ namespace StatHistorianReportGenerator · 225 unmodified lines | ||
| 285 | 299 | double verticalMillimeters; |
| 286 | 300 | |
| 287 | 301 | ReadStatistics(); |
| 302 | + | |
| 288 | 303 | if (m_generateCsvReport && m_reportFilePath != null) |
| 289 | 304 | GenerateCsvReportFunction(m_reportFilePath); |
| 290 | 305 | |
| 306 | + PopulateDataAvailabilityTable(); | |
| 307 | + | |
| 291 | 308 | // Page one |
| 292 | 309 | verticalMillimeters = PageMarginMillimeters; |
| 293 | 310 | verticalMillimeters += InsertTitle(fontDefinition, pageOne, verticalMillimeters) + SpacingMillimeters; |
| @@ -316,6 +333,83 @@ namespace StatHistorianReportGenerator · 22 unmodified lines | ||
| 316 | 333 | return report; |
| 317 | 334 | } |
| 318 | 335 | |
| 336 | + private void PopulateDataAvailabilityTable() | |
| 337 | + { | |
| 338 | + const int DefaultRecordDepth = 14; | |
| 339 | + | |
| 340 | + try | |
| 341 | + { | |
| 342 | + string configFile = GetConfigurationFileName(); | |
| 343 | + | |
| 344 | + if (!File.Exists(configFile)) | |
| 345 | + throw new FileNotFoundException($"Config file \"{configFile}\" was not found."); | |
| 346 | + | |
| 347 | + XDocument serviceConfig = XDocument.Load(configFile); | |
| 348 | + | |
| 349 | + // ReSharper disable once AssignNullToNotNullAttribute | |
| 350 | + string connectionString = serviceConfig | |
| 351 | + .Descendants("systemSettings") | |
| 352 | + .SelectMany(systemSettings => systemSettings.Elements("add")) | |
| 353 | + .Where(element => "ConnectionString".Equals((string)element.Attribute("name"), StringComparison.OrdinalIgnoreCase)) | |
| 354 | + .Select(element => (string)element.Attribute("value")) | |
| 355 | + .FirstOrDefault(); | |
| 356 | + | |
| 357 | + string dataProviderString = serviceConfig | |
| 358 | + .Descendants("systemSettings") | |
| 359 | + .SelectMany(systemSettings => systemSettings.Elements("add")) | |
| 360 | + .Where(element => "DataProviderString".Equals((string)element.Attribute("name"), StringComparison.OrdinalIgnoreCase)) | |
| 361 | + .Select(element => (string)element.Attribute("value")) | |
| 362 | + .FirstOrDefault(); | |
| 363 | + | |
| 364 | + using (AdoDataConnection connection = new AdoDataConnection(connectionString, dataProviderString)) | |
| 365 | + { | |
| 366 | + TableOperations<DataAvailability> dataAvailabilityTable = new TableOperations<DataAvailability>(connection); | |
| 367 | + DataAvailability[] records = dataAvailabilityTable.QueryRecords().ToArray(); | |
| 368 | + | |
| 369 | + if (records.Length == 0) | |
| 370 | + { | |
| 371 | + records = new DataAvailability[DefaultRecordDepth]; | |
| 372 | + | |
| 373 | + for (int i = 0; i < records.Length; i++) | |
| 374 | + records[i] = dataAvailabilityTable.NewRecord(); | |
| 375 | + } | |
| 376 | + | |
| 377 | + for (int i = records.Length - 1; i >= 0; i--) | |
| 378 | + { | |
| 379 | + List<DeviceStats>[] levels = GetLevels(ReportDays - (i + 1)); | |
| 380 | + DataAvailability record = records[i]; | |
| 381 | + | |
| 382 | + record.GoodAvailableData = levels[4].Count; | |
| 383 | + record.BadAvailableData = levels.Take(4).Sum(level => level.Count); | |
| 384 | + record.TotalAvailableData = levels.Sum(level => level.Count); | |
| 385 | + | |
| 386 | + dataAvailabilityTable.AddNewOrUpdateRecord(record); | |
| 387 | + } | |
| 388 | + } | |
| 389 | + } | |
| 390 | + catch (Exception ex) | |
| 391 | + { | |
| 392 | + // TODO: Someone needs to add a logger to this tool :-( | |
| 393 | + System.Diagnostics.Debug.WriteLine($"Failed to populate data availability table: {ex.Message}"); | |
| 394 | + } | |
| 395 | + } | |
| 396 | + | |
| 397 | + private static string GetConfigurationFileName() | |
| 398 | + { | |
| 399 | + string[] knownConfigurationFileNames = { "openPDC.exe.config", "SIEGate.exe.config", "openHistorian.exe.config", "substationSBG.exe.config", "openMIC.exe.config", "PDQTracker.exe.config" }; | |
| 400 | + | |
| 401 | + // Search for the file name in the list of known configuration files | |
| 402 | + foreach (string fileName in knownConfigurationFileNames) | |
| 403 | + { | |
| 404 | + string absolutePath = FilePath.GetAbsolutePath(fileName); | |
| 405 | + | |
| 406 | + if (File.Exists(absolutePath)) | |
| 407 | + return absolutePath; | |
| 408 | + } | |
| 409 | + | |
| 410 | + return ConfigurationFile.Current.Configuration.FilePath; | |
| 411 | + } | |
| 412 | + | |
| 319 | 413 | private void GenerateCsvReportFunction(string filepath) |
| 320 | 414 | { |
| 321 | 415 | filepath = Path.ChangeExtension(filepath, ".csv"); |