Innovenergy_trunk/csharp/App/Collector/src/Program.cs

175 lines
5.8 KiB
C#
Raw Normal View History

2023-02-16 12:57:06 +00:00
using System.Net.Sockets;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Text.Json;
using InnovEnergy.App.Collector.Influx;
using InnovEnergy.App.Collector.Records;
using InnovEnergy.Lib.Utils;
2023-02-16 12:57:06 +00:00
using InnovEnergy.Lib.Utils.Net;
using InnovEnergy.Lib.WebServer;
using static System.Text.Encoding;
using static InnovEnergy.Lib.Utils.ExceptionHandling;
2023-02-16 12:57:06 +00:00
namespace InnovEnergy.App.Collector;
2023-02-16 12:57:06 +00:00
// TODO: net6
// dotnet publish Collector.csproj -c Release -r linux-x64 -p:PublishSingleFile=true --self-contained true ; scp ./bin/Release/netcoreapp5.0/linux-x64/publish/* ig@salidomo.innovenergy.ch:~/collector
internal record BatteryData
(
String Installation,
String Battery,
Double EnergyCapacity,
Double EnergyStored,
Double Power
);
internal static class Program
{
//private static readonly Logger Logger = new Logger(Settings.LoggingEndPoint);
private static UdpClient _incomingSocket = new UdpClient(Settings.IncomingEndPoint);
private static UdpClient _dbSocket = new UdpClient();
private static readonly Subject<BatteryData> Batteries = new Subject<BatteryData>();
private static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions { WriteIndented = true };
public static void Main(String[] args)
{
Task.Run(ServeJsonStats);
while (true)
ProcessDatagram();
// ReSharper disable once FunctionNeverReturns
}
private static void ServeJsonStats()
{
var json = "";
Batteries.ObserveOn(TaskPoolScheduler.Default)
.Buffer(TimeSpan.FromSeconds(4))
.Select(b => b.GroupBy(d => d.Battery).Select(d => d.First()).ToList())
.Select(ToJson)
.Subscribe(j => json = j);
HttpResponse ServeRequest(HttpRequest httpRequest)
{
return new HttpResponse
{
Content = json.Apply(UTF8.GetBytes),
ContentType = ContentType.ApplicationJson,
Headers = new[] { new HttpHeader("Access-Control-Allow-Origin", "*") }
};
}
WebServer.ServeOnLocalHost(3333, ServeRequest);
}
private static String ToJson(IReadOnlyCollection<BatteryData> batteryData)
{
var nInstallations = batteryData.GroupBy(d => d.Installation).Count();
var nBatteries = batteryData.Count;
var energyStored = batteryData.Sum(d => d.EnergyStored).Apply(Math.Round);
var energyCapacity = batteryData.Sum(d => d.EnergyCapacity).Apply(Math.Round);
var chargingPower = batteryData.Where(d => d.Power > 0).Sum(d => d.Power / 1000).Apply(Math.Round);
var dischargingPower = batteryData.Where(d => d.Power < 0).Sum(d => -d.Power / 1000).Apply(Math.Round);
var json = new
{
nInstallations,
nBatteries,
energyStored_kWh = energyStored,
energyCapacity_kWh = energyCapacity,
chargingPower_kW = chargingPower,
dischargingPower_kW = dischargingPower,
};
return JsonSerializer.Serialize(json, JsonOptions);
// Console.WriteLine($"nInstallations : {nInstallations}");
// Console.WriteLine($"nBatteries : {nBatteries}");
// Console.WriteLine($"energyStored : {Math.Round(energyStored)} kWh");
// Console.WriteLine($"energyCapacity : {Math.Round(energyCapacity)} kWh");
// Console.WriteLine($"chargingPower : {Math.Round(chargingPower / 1000)} kW");
// Console.WriteLine($"dischargingPower: {Math.Round(dischargingPower/ 1000)} kW");
}
private static void ProcessDatagram()
{
ReadDatagram()
.ThenTry(ParseDatagram)
.ThenTry(SendToDb)
.OnErrorDo(Console.WriteLine);
}
private static Try<Byte[]> ParseDatagram(UdpDatagram datagram)
{
Byte[] Parse()
{
var batteryRecords = BatteryDataParser
.ParseDatagram(datagram);
if (batteryRecords.FirstOrDefault() is BatteryStatus bs)
{
var battery = bs.InstallationName + bs.BatteryId;
var capacity = 48.0/1000 * bs.AmpereHours;
var energyStored = (Double) bs.Soc / 100 * capacity;
var power = bs.Current * bs.Voltage;
var data = new BatteryData(bs.InstallationName,
battery,
capacity,
energyStored,
(Double)power);
Batteries.OnNext(data);
}
return batteryRecords
.Select(InfluxRecord.Serialize)
.JoinLines()
.Apply(UTF8.GetBytes);
}
return Try(Parse)
.OnErrorLog("ParseDatagram failed " + datagram.EndPoint.Address);
}
private static Try<UdpDatagram> ReadDatagram()
{
return Try(_incomingSocket.ReadDatagram)
.OnErrorLog("Failed to read from UDP socket")
.OnErrorDo(ResetIncomingSocket);
}
private static Try<Int32> SendToDb(Byte[] data)
{
Int32 Send() => _dbSocket.SendDatagram(data, Settings.DbEndPoint);
return Try(Send)
.OnErrorLog("SendToDb failed")
.OnErrorDo(ResetDbSocket);
}
private static void ResetDbSocket(Exception e)
{
_dbSocket?.Dispose();
_dbSocket = new UdpClient();
}
private static void ResetIncomingSocket(Exception e)
{
_incomingSocket?.Dispose();
_incomingSocket = new UdpClient(Settings.IncomingEndPoint);
}
}