using InnovEnergy.App.SaliMax.Ess; using InnovEnergy.Lib.Utils; using static System.Double; namespace InnovEnergy.App.SaliMax.AggregationService; public static class Aggregator { public static async Task HourlyDataAggregationManager() { var currentDateTime = DateTime.Now; var nextRoundedHour = currentDateTime.AddHours(1).AddMinutes(-currentDateTime.Minute).AddSeconds(-currentDateTime.Second); // Calculate the time until the next rounded hour var timeUntilNextHour = nextRoundedHour - currentDateTime; // Output the current and next rounded hour times Console.WriteLine("------------------------------------------HourlyDataAggregationManager-------------------------------------------"); Console.WriteLine("Current Date and Time: " + currentDateTime); Console.WriteLine("Next Rounded Hour: " + nextRoundedHour); // Output the time until the next rounded hour Console.WriteLine("Waiting for " + timeUntilNextHour.TotalMinutes + " minutes..."); Console.WriteLine("-----------------------------------------------------------------------------------------------------------------"); // Wait until the next rounded hour await Task.Delay(timeUntilNextHour); while (true) { try { AggregatedData hourlyAggregatedData = CreateHourlyData("LogDirectory",DateTime.Now.AddHours(-1).ToUnixTime(),DateTime.Now.ToUnixTime()); hourlyAggregatedData.Save("HourlyData"); } catch (Exception e) { Console.WriteLine("An error has occured when calculating hourly aggregated data, exception is:\n" + e); } await Task.Delay(TimeSpan.FromHours(1)); } } public static async Task DailyDataAggregationManager() { var currentDateTime = DateTime.Now; var nextRoundedHour = currentDateTime.AddDays(1).AddHours(-currentDateTime.Hour).AddMinutes(-currentDateTime.Minute).AddSeconds(-currentDateTime.Second); // Calculate the time until the next rounded hour var timeUntilNextDay = nextRoundedHour - currentDateTime; Console.WriteLine("------------------------------------------DailyDataAggregationManager-------------------------------------------"); // Output the current and next rounded hour times Console.WriteLine("Current Date and Time: " + currentDateTime); Console.WriteLine("Next Rounded Hour: " + nextRoundedHour); // Output the time until the next rounded hour Console.WriteLine("Waiting for " + timeUntilNextDay.TotalHours + " hours..."); Console.WriteLine("-----------------------------------------------------------------------------------------------------------------"); // Wait until the next rounded hour await Task.Delay(timeUntilNextDay); while (true) { try { var currentTime = DateTime.Now; AggregatedData dailyAggregatedData = CreateDailyData("HourlyData",currentTime.AddDays(-1).ToUnixTime(),currentTime.ToUnixTime()); dailyAggregatedData.Save("DailyData"); if (await dailyAggregatedData.PushToS3()) { DeleteHourlyData("HourlyData",currentTime.ToUnixTime()); AggregatedData.DeleteDailyData("DailyData"); } } catch (Exception e) { Console.WriteLine("An error has occured when calculating daily aggregated data, exception is:\n" + e); } await Task.Delay(TimeSpan.FromDays(1)); } } private static void DeleteHourlyData(String myDirectory, Int64 beforeTimestamp) { var csvFiles = Directory.GetFiles(myDirectory, "*.csv"); Console.WriteLine("Delete data before"+beforeTimestamp); foreach (var csvFile in csvFiles) { if (IsFileWithinTimeRange(csvFile, 0, beforeTimestamp)) { File.Delete(csvFile); Console.WriteLine($"Deleted hourly data file: {csvFile}"); } } } // this for test private static AggregatedData CreateHourlyData(String myDirectory, Int64 afterTimestamp, Int64 beforeTimestamp) { // Get all CSV files in the specified directory var csvFiles = Directory.GetFiles(myDirectory, "*.csv"); var batterySoc = new List(); var pvPowerSum = new List(); var heatingPower = new List(); var gridPowerImport = new List(); var gridPowerExport = new List(); var batteryDischargePower = new List(); var batteryChargePower = new List(); Console.WriteLine("File timestamp should start after "+ afterTimestamp); foreach (var csvFile in csvFiles) { if (csvFile == "LogDirectory/log.csv") { continue; } if (IsFileWithinTimeRange(csvFile, afterTimestamp, beforeTimestamp)) { using var reader = new StreamReader(csvFile); while (!reader.EndOfStream) { var line = reader.ReadLine(); var lines = line?.Split(';'); // Assuming there are always three columns (variable name and its value) if (lines is { Length: 3 }) { var variableName = lines[0].Trim(); if (TryParse(lines[1].Trim(), out var value)) { switch (variableName) { case "/Battery/Soc": batterySoc.Add(value); break; case "/PvOnDc/DcWh" : pvPowerSum.Add(value); break; case "/Battery/Dc/Power": if (value < 0) { batteryDischargePower.Add(value); } else { batteryChargePower.Add(value); } break; case "/GridMeter/ActivePowerExportT3": // we are using different register to check which value from the grid meter we need to use // At the moment register 8002 amd 8012. in KWh gridPowerExport.Add(value); break; case "/GridMeter/ActivePowerImportT3": gridPowerImport.Add(value); break; case "/Battery/HeatingPower": heatingPower.Add(value); break; // Add more cases as needed default: // Code to execute when variableName doesn't match any condition break; } } else { //Handle cases where variableValue is not a valid number // Console.WriteLine( // $"Invalid numeric value for variable {variableName}:{lines[1].Trim()}"); } } else { // Handle invalid column format //Console.WriteLine("Invalid format in column"); } } } } //Average Power (Watts)= Sum of Power Readings/Number of Readings //Then, you can use the average power in the energy formula: // //Energy (kWh)= (Average Power / 3600) × Time (1 seconds) // // Dividing the Average power readings by 3600 converts the result from watt-seconds to kilowatt-hours. var dischargingEnergy = (batteryDischargePower.Any() ? batteryDischargePower.Average() : 0.0) / 3600; var chargingEnergy = (batteryChargePower.Any() ? batteryChargePower.Average() : 0.0) / 3600; var heatingPowerAvg = (heatingPower.Any() ? heatingPower.Average() : 0.0) / 3600; var dMaxSoc = batterySoc.Any() ? batterySoc.Max() : 0.0; var dMinSoc = batterySoc.Any() ? batterySoc.Min() : 0.0; var dSumGridExportPower = gridPowerExport.Any() ? gridPowerExport.Max() - gridPowerExport.Min(): 0.0; var dSumGridImportPower = gridPowerImport.Any() ? gridPowerImport.Max() - gridPowerImport.Min(): 0.0; var dSumPvPower = pvPowerSum.Any() ? pvPowerSum.Max() : 0.0; AggregatedData aggregatedData = new AggregatedData { MaxSoc = dMaxSoc, MinSoc = dMinSoc, DischargingBatteryPower = dischargingEnergy, ChargingBatteryPower = chargingEnergy, GridExportPower = dSumGridExportPower, GridImportPower = dSumGridImportPower, PvPower = dSumPvPower, HeatingPower = heatingPowerAvg }; // Print the stored CSV data for verification Console.WriteLine($"Max SOC: {aggregatedData.MaxSoc}"); Console.WriteLine($"Min SOC: {aggregatedData.MinSoc}"); Console.WriteLine($"ChargingBatteryPower: {aggregatedData.DischargingBatteryPower}"); Console.WriteLine($"DischargingBatteryBattery: {aggregatedData.ChargingBatteryPower}"); Console.WriteLine($"SumGridExportPower: {aggregatedData.GridExportPower}"); Console.WriteLine($"SumGridImportPower: {aggregatedData.GridImportPower}"); Console.WriteLine($"Min SOC: {aggregatedData.MinSoc}"); Console.WriteLine("CSV data reading and storage completed."); Console.WriteLine("-----------------------------------------------------------------------------------------------------------------"); return aggregatedData; } private static AggregatedData CreateDailyData(String myDirectory, Int64 afterTimestamp, Int64 beforeTimestamp) { // Get all CSV files in the specified directory var csvFiles = Directory.GetFiles(myDirectory, "*.csv"); var batterySoc = new List(); var pvPower = new List(); var gridPowerImport = new List(); var gridPowerExport = new List(); var batteryDischargePower = new List(); var batteryChargePower = new List(); var heatingPowerAvg = new List(); Console.WriteLine("File timestamp should start after "+ afterTimestamp); foreach (var csvFile in csvFiles) { if (csvFile == "LogDirectory/log.csv") { continue; } if (IsFileWithinTimeRange(csvFile, afterTimestamp, beforeTimestamp)) { using var reader = new StreamReader(csvFile); while (!reader.EndOfStream) { var line = reader.ReadLine(); var lines = line?.Split(';'); // Assuming there are always three columns (variable name and its value) if (lines is { Length: 3 }) { var variableName = lines[0].Trim(); if (TryParse(lines[1].Trim(), out var value)) { switch (variableName) { case "/MinSoc" or "/MaxSoc": batterySoc.Add(value); break; case "/PvPower": pvPower.Add(value); break; case "/DischargingBatteryPower" : batteryDischargePower.Add(value); break; case "/ChargingBatteryPower" : batteryChargePower.Add(value); break; case "/GridExportPower": gridPowerExport.Add(value); break; case "/GridImportPower": gridPowerImport.Add(value); break; case "/HeatingPower": heatingPowerAvg.Add(value); break; // Add more cases as needed default: // Code to execute when variableName doesn't match any condition break; } } else { //Handle cases where variableValue is not a valid number // Console.WriteLine( // $"Invalid numeric value for variable {variableName}:{lines[1].Trim()}"); } } else { // Handle invalid column format //Console.WriteLine("Invalid format in column"); } } } } AggregatedData aggregatedData = new AggregatedData { MaxSoc = batterySoc.Any() ? batterySoc.Max() : 0.0, MinSoc = batterySoc.Any() ? batterySoc.Min() : 0.0, DischargingBatteryPower = batteryDischargePower.Any() ? batteryDischargePower.Average(): 0.0, ChargingBatteryPower = batteryChargePower.Any() ? batteryChargePower.Average() : 0.0, GridExportPower = gridPowerExport.Any() ? gridPowerExport.Sum() : 0.0, GridImportPower = gridPowerImport.Any() ? gridPowerImport.Sum() : 0.0, PvPower = pvPower.Any() ? pvPower.Max() : 0.0, HeatingPower = heatingPowerAvg.Any() ? heatingPowerAvg.Average() : 0.0, }; // Print the stored CSV data for verification Console.WriteLine($"Pv Power: {aggregatedData.PvPower}"); Console.WriteLine($"Heating Power: {aggregatedData.HeatingPower}"); Console.WriteLine($"Max SOC: {aggregatedData.MaxSoc}"); Console.WriteLine($"Min SOC: {aggregatedData.MinSoc}"); Console.WriteLine($"ChargingBatteryPower: {aggregatedData.DischargingBatteryPower}"); Console.WriteLine($"DischargingBatteryBattery: {aggregatedData.ChargingBatteryPower}"); Console.WriteLine($"SumGridExportPower: {aggregatedData.GridExportPower}"); Console.WriteLine($"SumGridImportPower: {aggregatedData.GridImportPower}"); Console.WriteLine("CSV data reading and storage completed."); Console.WriteLine("-----------------------------------------------------------------------------------------------------------------"); return aggregatedData; } // Custom method to check if a string is numeric private static Boolean GetVariable(String value, String path) { return value == path; } private static Boolean IsFileWithinTimeRange(string filePath, long startTime, long endTime) { var fileTimestamp = long.TryParse(Path.GetFileNameWithoutExtension(filePath).Replace("log_", ""), out var fileTimestamp1) ? fileTimestamp1 : -1; return fileTimestamp >= startTime && fileTimestamp < endTime; } }