using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Threading.Tasks; using BattSim.Models; using Microsoft.AspNetCore.Components.Forms; namespace BattSim.Services { public static class DataLoader { public static async Task> LoadAndProcessData(IBrowserFile file) { var energyData = new ConcurrentBag(); // Thread-safe collection await using var stream = file.OpenReadStream(maxAllowedSize: (int)1.0e9); using var reader = new StreamReader(stream, bufferSize: (int)1.0e6); // Skip header await reader.ReadLineAsync(); // Read all lines into memory var lines = new List(); string line; while ((line = await reader.ReadLineAsync()) is not null) { lines.Add(line); } // Process lines in parallel Parallel.ForEach(lines, line => { try { var parts = SplitLine(line); DateTime time = ParseDateTime(parts[0..2]); double volume = ParseVolume(parts[8]); string register = parts[7].Trim(); bool dayTarif = register.Contains("Dag"); // Use ConcurrentBag for thread-safe additions var entry = new EnergyData { Time = time, DayTarif = dayTarif }; if (register.Contains("Afname")) entry.Consumption = volume; else if (register.Contains("Injectie")) entry.Production = volume; else throw new Exception("Unknown volume register"); energyData.Add(entry); } catch (Exception e) { Console.WriteLine($"Error parsing line: {line}. Skipping to next line. Exception: {e}"); } }); // Group by Time and merge entries var groupedData = energyData .GroupBy(e => e.Time) .Select(g => { var first = g.First(); first.Consumption = g.Sum(e => e.Consumption); first.Production = g.Sum(e => e.Production); return first; }) .OrderBy(e => e.Time) .ToList(); return groupedData; } private static string[] SplitLine(string line) { var parts = line.Split(';'); if (parts.Length < 9) throw new Exception($"Malformed line (too many parts): {line}"); return parts; } private static DateTime ParseDateTime(string[] dateTimeStrings) { string dateTimeString = dateTimeStrings[0] + " " + dateTimeStrings[1]; return DateTime.ParseExact(dateTimeString, "dd-MM-yyyy HH:mm:ss", CultureInfo.GetCultureInfo("nl-BE")); } private static double ParseVolume(string volumeString) { var volumeStr = volumeString.Trim(); return string.IsNullOrEmpty(volumeStr) ? 0 : double.Parse(volumeStr.Replace(",", "."), CultureInfo.InvariantCulture); } } }