243 lines
11 KiB
C#
243 lines
11 KiB
C#
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 = CreateAverage("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 = CreateAverage("HourlyData",currentTime.AddDays(-1).ToUnixTime(),currentTime.ToUnixTime());
|
||
dailyAggregatedData.Save("DailyData");
|
||
if (await dailyAggregatedData.PushToS3())
|
||
{
|
||
DeleteHourlyData("HourlyData",currentTime.ToUnixTime());
|
||
dailyAggregatedData.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}");
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
private static AggregatedData CreateAverage(String myDirectory, Int64 afterTimestamp, Int64 beforeTimestamp)
|
||
{
|
||
// Get all CSV files in the specified directory
|
||
var csvFiles = Directory.GetFiles(myDirectory, "*.csv");
|
||
var batterySoc = new List<Double>();
|
||
var batteryAvgSoc = new List<Double>(); // this is only used for the daily data.
|
||
var pvPowerAverage = new List<Double>();
|
||
var gridPowerImport = new List<Double>();
|
||
var gridPowerExport = new List<Double>();
|
||
var batteryDischargePower = new List<Double>();
|
||
var batteryChargePower = new List<Double>();
|
||
|
||
|
||
Console.WriteLine("-----------------------------------------------------------------------------------------------------------------");
|
||
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" or "/MinSoc" or "/MaxSoc":
|
||
batterySoc.Add(value);
|
||
break;
|
||
|
||
case "/PvOnDc/DcWh" or "/SumPvPower":
|
||
pvPowerAverage.Add(value);
|
||
break;
|
||
|
||
case "/Battery/Dc/Power" or "/SumDischargingBatteryPower" or "/SumChargingBatteryPower":
|
||
|
||
//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 (seconds)
|
||
//
|
||
// Dividing the Average power readings by 3600 converts the result from watt-seconds to kilowatt-hours.
|
||
if (value < 0)
|
||
{
|
||
batteryDischargePower.Add(value);
|
||
}
|
||
else
|
||
{
|
||
batteryChargePower.Add(value);
|
||
|
||
}
|
||
break;
|
||
|
||
case "/GridMeter/ActivePowerExportT2" or "/SumGridExportPower":
|
||
// we are using different register to check which value from the grid meter we need to use
|
||
gridPowerExport.Add(value);
|
||
break;
|
||
|
||
case "/GridMeter/ActivePowerImportT2" or "/SumGridImportPower":
|
||
gridPowerImport.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,
|
||
SumDischargingBatteryPower = batteryDischargePower.Any() ? batteryDischargePower.Average(): 0.0,
|
||
SumChargingBatteryPower = batteryChargePower.Any() ? batteryChargePower.Average() : 0.0,
|
||
SumGridExportPower = gridPowerExport.Any() ? gridPowerExport.Average() : 0.0,
|
||
SumGridImportPower = gridPowerImport.Any() ? gridPowerImport.Average() : 0.0,
|
||
SumPvPower = pvPowerAverage.Any() ? pvPowerAverage.Average() : 0.0,
|
||
};
|
||
|
||
// Print the stored CSV data for verification
|
||
Console.WriteLine($"Max SOC: {aggregatedData.MaxSoc}");
|
||
Console.WriteLine($"Min SOC: {aggregatedData.MinSoc}");
|
||
|
||
Console.WriteLine($"ChargingBatteryPower: {aggregatedData.SumDischargingBatteryPower}");
|
||
Console.WriteLine($"DischargingBatteryBattery: {aggregatedData.SumChargingBatteryPower}");
|
||
|
||
Console.WriteLine($"SumGridExportPower: {aggregatedData.SumGridExportPower}");
|
||
Console.WriteLine($"SumGridImportPower: {aggregatedData.SumGridImportPower}");
|
||
|
||
Console.WriteLine($"Min SOC: {aggregatedData.MinSoc}");
|
||
|
||
|
||
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;
|
||
}
|
||
} |