Innovenergy_trunk/csharp/App/Backend/Services/ExcelDataParser.cs

89 lines
3.9 KiB
C#

using ClosedXML.Excel;
using InnovEnergy.App.Backend.DataTypes;
namespace InnovEnergy.App.Backend.Services;
public static class ExcelDataParser
{
// Column headers from the ESS Link Cloud Excel export
private const string ColDateTime = "Data time";
private const string ColPvToday = "PV Generated Energy Today";
private const string ColLoadToday = "Load Consumption Today";
private const string ColGridImportToday = "Purchased Energy Today";
private const string ColGridExportToday = "Feed in energy Today";
private const string ColBattChargedToday = "Daily Battery Charged";
private const string ColBattDischargedToday = "Battery Discharged Today";
/// <summary>
/// Parses an ESS Link Cloud Excel export file and returns one DailyEnergyData per day.
/// Takes the last row of each day (where "Today" cumulative values are highest).
/// </summary>
public static List<DailyEnergyData> Parse(string filePath)
{
if (!File.Exists(filePath))
throw new FileNotFoundException($"Excel file not found: {filePath}");
using var workbook = new XLWorkbook(filePath);
var worksheet = workbook.Worksheet(1);
var lastRow = worksheet.LastRowUsed()?.RowNumber() ?? 0;
if (lastRow < 2)
throw new InvalidOperationException("Excel file has no data rows.");
// Find column indices by header name (row 1)
var headerRow = worksheet.Row(1);
var colMap = new Dictionary<string, int>();
for (var col = 1; col <= worksheet.LastColumnUsed()?.ColumnNumber(); col++)
{
var header = headerRow.Cell(col).GetString().Trim();
if (!string.IsNullOrEmpty(header))
colMap[header] = col;
}
// Validate required columns exist
var requiredCols = new[] { ColDateTime, ColPvToday, ColLoadToday, ColGridImportToday, ColGridExportToday, ColBattChargedToday, ColBattDischargedToday };
foreach (var rc in requiredCols)
{
if (!colMap.ContainsKey(rc))
throw new InvalidOperationException($"Required column '{rc}' not found in Excel file. Available: {string.Join(", ", colMap.Keys)}");
}
// Read all rows, group by date, keep last row per day
var dailyLastRows = new SortedDictionary<string, DailyEnergyData>();
for (var row = 2; row <= lastRow; row++)
{
var dateTimeStr = worksheet.Row(row).Cell(colMap[ColDateTime]).GetString().Trim();
if (string.IsNullOrEmpty(dateTimeStr)) continue;
// Extract date portion (first 10 chars: "2026-02-10")
var date = dateTimeStr.Length >= 10 ? dateTimeStr[..10] : dateTimeStr;
var data = new DailyEnergyData
{
Date = date,
PvProduction = GetDouble(worksheet, row, colMap[ColPvToday]),
LoadConsumption = GetDouble(worksheet, row, colMap[ColLoadToday]),
GridImport = GetDouble(worksheet, row, colMap[ColGridImportToday]),
GridExport = GetDouble(worksheet, row, colMap[ColGridExportToday]),
BatteryCharged = GetDouble(worksheet, row, colMap[ColBattChargedToday]),
BatteryDischarged = GetDouble(worksheet, row, colMap[ColBattDischargedToday]),
};
// Always overwrite — last row of the day has the final cumulative values
dailyLastRows[date] = data;
}
Console.WriteLine($"[ExcelDataParser] Parsed {dailyLastRows.Count} days from {filePath}");
return dailyLastRows.Values.ToList();
}
private static double GetDouble(IXLWorksheet ws, int row, int col)
{
var cell = ws.Row(row).Cell(col);
if (cell.IsEmpty()) return 0;
return cell.TryGetValue<double>(out var val) ? Math.Round(val, 4) : 0;
}
}