using InnovEnergy.App.SodiStoreMax.Ess; using InnovEnergy.Lib.Utils; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using static System.Double; namespace InnovEnergy.App.SodiStoreMax.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("JsonLogDirectory",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 jsonFiles = Directory.GetFiles(myDirectory, "*.json"); //Console.WriteLine("Delete data before"+beforeTimestamp); foreach (var jsonFile in jsonFiles) { if (IsFileWithinTimeRange(jsonFile, 0, beforeTimestamp)) { File.Delete(jsonFile); //Console.WriteLine($"Deleted hourly data file: {jsonFile}"); } } } private static AggregatedData CreateHourlyData(String myDirectory, Int64 afterTimestamp, Int64 beforeTimestamp) { // Get all json files in the specified directory var jsonFiles = Directory.GetFiles(myDirectory, "*.json"); var batterySoc = new List(); var pvPowerSum = new List(); var gridPowerImport = new List(); var gridPowerExport = new List(); var batteryDischargePower = new List(); var batteryChargePower = new List(); foreach (var jsonFile in jsonFiles) { if (jsonFile == "JsonLogDirectory/log.json") { continue; } if (IsFileWithinTimeRange(jsonFile, afterTimestamp, beforeTimestamp)) { try { // Read and parse JSON var jsonData = File.ReadAllText(jsonFile); // Step 2: Find the first '{' character and trim everything before it int startIndex = jsonData.IndexOf('{'); if (startIndex != -1) { jsonData = jsonData.Substring(startIndex); // Trim everything before '{' } var jsonObject = JObject.Parse(jsonData); if (jsonObject["Battery"] != null && jsonObject["Battery"]["Soc"] != null) { batterySoc.Add((double)jsonObject["Battery"]["Soc"]); } if (jsonObject["PvOnDc"] != null && jsonObject["PvOnDc"]["DcWh"] != null) { pvPowerSum.Add((double)jsonObject["PvOnDc"]["DcWh"]); } if (jsonObject["Battery"] != null && jsonObject["Battery"]["Power"] != null) { double batteryPower = (double)jsonObject["Battery"]["Power"]; if (batteryPower < 0) batteryDischargePower.Add(batteryPower); else batteryChargePower.Add(batteryPower); } if (jsonObject["GridMeter"] != null && jsonObject["GridMeter"]["ActivePowerExportT3"] != null) { gridPowerExport.Add((double)jsonObject["GridMeter"]["ActivePowerExportT3"]); } if (jsonObject["GridMeter"] != null && jsonObject["GridMeter"]["ActivePowerImportT3"] != null) { gridPowerImport.Add((double)jsonObject["GridMeter"]["ActivePowerImportT3"]); } } catch (Exception e) { Console.WriteLine($"Failed to parse JSON file {jsonFile}: {e.Message}"); } } } //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. //Console.WriteLine("MAX IS "+gridPowerExport.Max()); // Console.WriteLine("MIN IS "+gridPowerExport.Min()); var dischargingEnergy = (batteryDischargePower.Any() ? batteryDischargePower.Average() : 0.0) / 3600; var chargingEnergy = (batteryChargePower.Any() ? batteryChargePower.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 = 0 }; // Print the stored JSON data for verification Console.WriteLine($"Max SOC: {aggregatedData.MaxSoc}"); Console.WriteLine($"Min SOC: {aggregatedData.MinSoc}"); Console.WriteLine($"DischargingBatteryBattery: {aggregatedData.DischargingBatteryPower}"); Console.WriteLine($"ChargingBatteryPower: {aggregatedData.ChargingBatteryPower}"); Console.WriteLine($"SumGridExportPower: {aggregatedData.GridExportPower}"); Console.WriteLine($"SumGridImportPower: {aggregatedData.GridImportPower}"); Console.WriteLine($"Min SOC: {aggregatedData.MinSoc}"); Console.WriteLine("JSON data reading and storage completed."); Console.WriteLine("-----------------------------------------------------------------------------------------------------------------"); return aggregatedData; } private static AggregatedData CreateDailyData(String myDirectory, Int64 afterTimestamp, Int64 beforeTimestamp) { // Get all JSON files in the specified directory var jsonFiles = Directory.GetFiles(myDirectory, "*.json"); 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(); foreach (var jsonFile in jsonFiles) { if (jsonFile == "JsonLogDirectory/log.json") { continue; } if (IsFileWithinTimeRange(jsonFile, afterTimestamp, beforeTimestamp)) { try { var jsonData = File.ReadAllText(jsonFile); // Parse JSON into a Dictionary var jsonDict = JsonConvert.DeserializeObject>(jsonData); // Process values foreach (var (variableName, value) in jsonDict) { switch (variableName) { case "MinSoc": case "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; } } } catch (Exception e) { Console.WriteLine($"Failed to parse JSON file {jsonFile}: {e.Message}"); } } } 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.Last() : 0.0, HeatingPower = heatingPowerAvg.Any() ? heatingPowerAvg.Average() : 0.0, }; // Print the stored JSON 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("JSON 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; } }