89 lines
3.9 KiB
C#
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;
|
|
}
|
|
}
|