381 lines
17 KiB
C#
381 lines
17 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 = 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<Double>();
|
||
var pvPowerSum = new List<Double>();
|
||
var heatingPower = 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("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<Double>();
|
||
var pvPower = new List<Double>();
|
||
var gridPowerImport = new List<Double>();
|
||
var gridPowerExport = new List<Double>();
|
||
var batteryDischargePower = new List<Double>();
|
||
var batteryChargePower = new List<Double>();
|
||
var heatingPowerAvg = new List<Double>();
|
||
|
||
|
||
|
||
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;
|
||
}
|
||
} |