Improved chart visual

This commit is contained in:
douwe
2026-04-24 10:51:48 +02:00
parent ac7c5e6013
commit 0d7bc18293
5 changed files with 55 additions and 114 deletions

View File

@@ -8,6 +8,7 @@ namespace BattSim.Models
public bool DayTariff { get; set; } public bool DayTariff { get; set; }
public double Consumption { get; set; } public double Consumption { get; set; }
public double Production { get; set; } public double Production { get; set; }
public double BatteryCharge { get; set; }
public EnergyData(){} public EnergyData(){}
public EnergyData(EnergyData other) public EnergyData(EnergyData other)
@@ -16,6 +17,7 @@ namespace BattSim.Models
DayTariff = other.DayTariff; DayTariff = other.DayTariff;
Consumption = other.Consumption; Consumption = other.Consumption;
Production = other.Production; Production = other.Production;
BatteryCharge = other.BatteryCharge;
} }
} }
} }

View File

@@ -1,17 +0,0 @@
using System;
namespace BattSim.Models
{
public class SimulatedBatteryEnergyData : EnergyData
{
public double BatteryCharge { get; set; }
public double SimulatedConsumption { get; set; }
public double SimulatedProduction { get; set; }
public SimulatedBatteryEnergyData(EnergyData energyData) : base(energyData) { }
public SimulatedBatteryEnergyData(SimulatedBatteryEnergyData simulatedBatteryEnergyData) : base(simulatedBatteryEnergyData)
{
BatteryCharge = simulatedBatteryEnergyData.BatteryCharge;
}
}
}

View File

@@ -267,7 +267,7 @@
<!-- Chart - Directly on page, full width --> <!-- Chart - Directly on page, full width -->
@if (FluviusDataDaily.Length > 0) @if (FilteredFluviusData.Length > 0)
{ {
<div class="chart-container"> <div class="chart-container">
<h2>Overzicht: Energie Data & Simulatie</h2> <h2>Overzicht: Energie Data & Simulatie</h2>
@@ -299,7 +299,7 @@
</RadzenAreaSeries> </RadzenAreaSeries>
<!-- Simulated Data --> <!-- Simulated Data -->
@if (SimulationDataDaily.Length > 0) @if (FilteredSimulationData.Length > 0)
{ {
<RadzenAreaSeries Smooth=true Data="@FilteredSimulationData" CategoryProperty="Time" Title="Simulated Consumption" ValueProperty="Consumption"> <RadzenAreaSeries Smooth=true Data="@FilteredSimulationData" CategoryProperty="Time" Title="Simulated Consumption" ValueProperty="Consumption">
</RadzenAreaSeries> </RadzenAreaSeries>
@@ -323,13 +323,12 @@
@code { @code {
EnergyData[] FluviusDataRaw = []; EnergyData[] FluviusDataRaw = [];
EnergyData[] FluviusDataDaily = []; EnergyData[] SimulationData = [];
SimulatedBatteryEnergyData[] SimulationData = []; DataFilter.FilterOption SelectedFilterOption = DataFilter.FilterOption.ALL;
SimulatedBatteryEnergyData[] SimulationDataDaily = [];
EnergyData[] FilteredFluviusData = []; EnergyData[] FilteredFluviusData = [];
SimulatedBatteryEnergyData[] FilteredSimulationData = []; EnergyData[] FilteredSimulationData = [];
EnergyCostCalculator calculator = new(); EnergyCostCalculator calculator = new();
double normalCost = 0.0; double normalCost = 0.0;
@@ -406,8 +405,7 @@
StateHasChanged(); StateHasChanged();
FluviusDataRaw = await FluviusDataHandler.LoadAndProcessFile(file); FluviusDataRaw = await FluviusDataHandler.LoadAndProcessFile(file);
FluviusDataDaily = FluviusDataHandler.GenerateDailyData(FluviusDataRaw); SetTimePeriod("all");
FilteredFluviusData = FluviusDataDaily;
FilteredSimulationData = []; FilteredSimulationData = [];
StepData[0].isProcessing = false; StepData[0].isProcessing = false;
@@ -415,7 +413,6 @@
StepData[1].Completed = false; StepData[1].Completed = false;
StepData[2].Completed = false; StepData[2].Completed = false;
SimulationData = []; SimulationData = [];
SimulationDataDaily = [];
StateHasChanged(); StateHasChanged();
} }
@@ -437,8 +434,7 @@
try try
{ {
SimulationData = BatterySimulator.SimulateBattery(FluviusDataRaw, BatteryCapacity, Efficiency/100); SimulationData = BatterySimulator.SimulateBattery(FluviusDataRaw, BatteryCapacity, Efficiency/100);
SimulationDataDaily = BatterySimulator.GenerateDailyData(SimulationData); SetTimePeriod("all");
FilteredSimulationData = SimulationDataDaily;
StepData[1].isProcessing = false; StepData[1].isProcessing = false;
StepData[1].Completed = true; StepData[1].Completed = true;
@@ -465,8 +461,6 @@
try try
{ {
normalCost = calculator.CalculateCostOfEnergyUsage(FluviusDataRaw); normalCost = calculator.CalculateCostOfEnergyUsage(FluviusDataRaw);
simulatedCost = calculator.CalculateCostOfEnergyUsage(SimulationData);
StepData[2].isProcessing = false; StepData[2].isProcessing = false;
StepData[2].Completed = true; StepData[2].Completed = true;
StateHasChanged(); StateHasChanged();
@@ -481,63 +475,20 @@
private void SetTimePeriod(string period) private void SetTimePeriod(string period)
{ {
_timePeriod = period; _timePeriod = period;
var filterOption = period switch {
"all" => DataFilter.FilterOption.ALL,
"year" => DataFilter.FilterOption.YEAR,
"month" => DataFilter.FilterOption.MONTH,
"week" => DataFilter.FilterOption.WEEK,
"day" => DataFilter.FilterOption.DAY,
};
// Filter data based on selected period // Filter data based on selected period
FilteredFluviusData = FilterFluviusDataByPeriod(period); if(FluviusDataRaw.Length > 0) FilteredFluviusData = DataFilter.FilterData(FluviusDataRaw, filterOption);
FilteredSimulationData = FilterSimulationDataByPeriod(period); if(FilteredSimulationData.Length > 0) FilteredSimulationData = DataFilter.FilterData(SimulationData, filterOption);
StateHasChanged(); StateHasChanged();
} }
private EnergyData[] FilterFluviusDataByPeriod(string period)
{
if (period == "all" || FluviusDataDaily.Length == 0)
return FluviusDataDaily;
var result = new List<EnergyData>();
foreach (var item in FluviusDataDaily)
{
// The Time property is DateTime but for daily data it represents the day
var date = DateOnly.FromDateTime(item.Time);
bool include = false;
switch (period)
{
case "day": include = true; break; // Show all daily data
case "week": include = date.DayOfWeek == DayOfWeek.Monday; break;
case "month": include = date.Day == 1; break;
case "year": include = date.Month == 1 && date.Day == 1; break;
}
if (include) result.Add(item);
}
return result.ToArray();
}
private SimulatedBatteryEnergyData[] FilterSimulationDataByPeriod(string period)
{
if (period == "all" || SimulationDataDaily.Length == 0)
return SimulationDataDaily;
var result = new List<SimulatedBatteryEnergyData>();
foreach (var item in SimulationDataDaily)
{
var date = DateOnly.FromDateTime(item.Time);
bool include = false;
switch (period)
{
case "day": include = true; break;
case "week": include = date.DayOfWeek == DayOfWeek.Monday; break;
case "month": include = date.Day == 1; break;
case "year": include = date.Month == 1 && date.Day == 1; break;
}
if (include) result.Add(item);
}
return result.ToArray();
}
private string FormatObject(object value) { private string FormatObject(object value) {
if(value is double d) return $"{d:0.##} kWh"; if(value is double d) return $"{d:0.##} kWh";
if(value is DateTime time) return time.ToString("dd/MM/yyyy"); if(value is DateTime time) return time.ToString("dd/MM/yyyy");

View File

@@ -6,14 +6,14 @@ namespace BattSim.Services
{ {
public static class BatterySimulator public static class BatterySimulator
{ {
public static SimulatedBatteryEnergyData[] SimulateBattery(EnergyData[] energyData, double batteryCapacity, double efficiency) public static EnergyData[] SimulateBattery(EnergyData[] energyData, double batteryCapacity, double efficiency)
{ {
var results = new List<SimulatedBatteryEnergyData>(); var results = new List<EnergyData>();
double batteryCharge = 0; double batteryCharge = 0;
foreach (var e in energyData) foreach (var e in energyData)
{ {
var simulatedBatteryEnergyData = new SimulatedBatteryEnergyData(e); var simulatedBatteryEnergyData = new EnergyData(e);
// Simulate charging the battery based on production // Simulate charging the battery based on production
double excessProduction = batteryCharge + e.Production - batteryCapacity; double excessProduction = batteryCharge + e.Production - batteryCapacity;
batteryCharge = double.Min(batteryCharge + e.Production, batteryCapacity); batteryCharge = double.Min(batteryCharge + e.Production, batteryCapacity);
@@ -33,30 +33,5 @@ namespace BattSim.Services
return results.ToArray(); return results.ToArray();
} }
public static SimulatedBatteryEnergyData[] GenerateDailyData(SimulatedBatteryEnergyData[] simulationData)
{
var simulationDataCopy = (SimulatedBatteryEnergyData[]) simulationData.Clone();
var dailySimulationData = new ConcurrentDictionary<DateOnly, SimulatedBatteryEnergyData>();
Parallel.ForEach(simulationDataCopy, simulationPoint =>
{
var date = DateOnly.FromDateTime(simulationPoint.Time);
// Use AddOrUpdate to avoid double lookup
dailySimulationData.AddOrUpdate(
date,
new SimulatedBatteryEnergyData(simulationPoint), // If key doesn't exist, add this value
(_, existing) =>
{
// If key exists, aggregate the values
existing.Consumption += simulationPoint.Consumption;
existing.Production += simulationPoint.Production;
existing.BatteryCharge = double.Max(existing.BatteryCharge, simulationPoint.BatteryCharge);
return existing;
}
);
});
return dailySimulationData.Values.ToArray();
}
} }
} }

30
Services/DataFilter.cs Normal file
View File

@@ -0,0 +1,30 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using BattSim.Models;
namespace BattSim.Services
{
public static class DataFilter
{
public enum FilterOption { ALL, YEAR, MONTH, WEEK, DAY }
public static EnergyData[] FilterData(EnergyData[] data, FilterOption filterOption)
{
var dailyData = data
.GroupBy(d=>d.Time.Date)
.Select(group=> new EnergyData{
Consumption = group.Sum(d => d.Consumption),
Production = group.Sum(d => d.Production),
BatteryCharge = group.Max(d => d.BatteryCharge),
Time = group.First().Time
}).ToArray();
return filterOption switch {
FilterOption.DAY => data[(data.Length > 96?data.Length - 96:0)..], // 24 hours * 4 quarters per hour
FilterOption.WEEK => data[(data.Length > 672?data.Length - 672:0)..], // 7 days * 24 hours * 4 quarters per hour
FilterOption.MONTH => dailyData[(dailyData.Length > 30?dailyData.Length - 30:0)..],
FilterOption.YEAR => dailyData[(dailyData.Length > 365?dailyData.Length - 365:0)..],
_ => dailyData
};
}
}
}