using System.Diagnostics; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; using Flurl.Http; using InnovEnergy.App.SaliMax.Controller; using InnovEnergy.App.SaliMax.SaliMaxRelays; using InnovEnergy.App.SaliMax.SystemConfig; using InnovEnergy.Lib.Devices.AMPT; using InnovEnergy.Lib.Devices.Battery48TL; using InnovEnergy.Lib.Devices.EmuMeter; using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc; using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc; using InnovEnergy.Lib.Time.Unix; using InnovEnergy.Lib.Utils; #pragma warning disable IL2026 namespace InnovEnergy.App.SaliMax; internal static class Program { private const UInt32 UpdateIntervalSeconds = 2; public static async Task Main(String[] args) { try { await Run(); } catch (Exception e) { await File.AppendAllTextAsync(Config.LogSalimaxLog, String.Join(Environment.NewLine, UnixTime.Now + " \n" + e)); throw; } } private static async Task Run() { Console.WriteLine("Starting SaliMax"); var batteryNodes = new Byte[] { 2, 3 }; var batteryTty = "/dev/ttyUSB0"; var relaysIp = "10.0.1.1"; var truConvertAcIp = "10.0.2.1"; var truConvertDcIp = "10.0.3.1"; var gridMeterIp = "10.0.4.1"; var internalMeter = "10.0.4.2"; var amptIp = "10.0.5.1"; var s3Config = new S3Config { Bucket = "saliomameiringen", Region = "sos-ch-dk-2", Provider = "exo.io", ContentType = "text/plain; charset=utf-8", Key = "EXO2bf0cbd97fbfa75aa36ed46f", Secret = "Bn1CDPqOG-XpDSbYjfIJxojcHTm391vZTc8z8l_fEPs" }; #if DEBUG var inverterDevice = new TruConvertAcDevice("127.0.0.1", 5001); var dcDcDevice = new TruConvertDcDevice("127.0.0.1", 5002); var gridMeterDevice = new EmuMeterDevice("127.0.0.1", 5003); var saliMaxRelaysDevice = new SaliMaxRelaysDevice("127.0.0.1", 5004); var amptDevice = new AmptCommunicationUnit("127.0.0.1", 5005); var acInToAcOutMeterDevice = new EmuMeterDevice("127.0.0.1", 5003); // TODO: use real device var secondBattery48TlDevice = Battery48TlDevice.Fake(); var firstBattery48TlDevice =Battery48TlDevice.Fake();; var salimaxConfig = new SalimaxConfig(); #else var batteries = batteryNodes.Select(n => new Battery48TlDevice(batteryTty, n)).ToList(); var inverterDevice = new TruConvertAcDevice(truConvertAcIp); var dcDcDevice = new TruConvertDcDevice(truConvertDcIp); var gridMeterDevice = new EmuMeterDevice(gridMeterIp); var acInToAcOutMeterDevice = new EmuMeterDevice(internalMeter); // TODO: use real device var amptDevice = new AmptCommunicationUnit(amptIp); var saliMaxRelaysDevice = new SaliMaxRelaysDevice(relaysIp); var salimaxConfig = new SalimaxConfig(); #endif // This is will be always add manually ? or do we need to read devices automatically in a range of IP @ StatusRecord ReadStatus() { var combinedBatteryStatus = batteries .Select(b => b.ReadStatus()) .NotNull() .ToList() .Combine(); // var dcDcStatusArray = dcDcDevices.Select(b => b.ReadStatus()).NotNull().ToArray(); // var inverterStatusArray = inverterDevices.Select(b => b.ReadStatus()).NotNull().ToArray(); return new StatusRecord { InverterStatus = inverterDevice.ReadStatus(), DcDcStatus = dcDcDevice.ReadStatus(), BatteriesStatus = combinedBatteryStatus, AcInToAcOutMeterStatus = acInToAcOutMeterDevice.ReadStatus(), GridMeterStatus = gridMeterDevice.ReadStatus(), SaliMaxRelayStatus = saliMaxRelaysDevice.ReadStatus(), AmptStatus = amptDevice.ReadStatus(), SalimaxConfig = salimaxConfig.Load().Result, }; } var startTime = UnixTime.Now; const Int32 delayTime = 10; Console.WriteLine("press ctrl-C to stop"); while (true) { var t = UnixTime.Now; while (t.Ticks % UpdateIntervalSeconds != 0) { await Task.Delay(delayTime); t = UnixTime.Now; } var status = ReadStatus(); #if BatteriesAllowed var jsonLog = status.ToLog(t); await UploadTimeSeries(s3Config, jsonLog, t); var controlRecord = Controller.Controller.SaliMaxControl(status); Controller.Controller.WriteControlRecord(controlRecord, inverterDevice, dcDcDevice, saliMaxRelaysDevice); //JsonSerializer.Serialize(jsonLog, JsonOptions).WriteLine(ConsoleColor.DarkBlue); #endif Topology.Print(status); while (UnixTime.Now == t) await Task.Delay(delayTime); } // ReSharper disable once FunctionNeverReturns } // to delete not used anymore [Conditional("RELEASE")] private static void ReleaseWriteLog(JsonObject jsonLog, UnixTime timestamp) { // WriteToFile(jsonLog, "/home/debian/DataSaliMax/" + timestamp); // this is was for beaglebone TODO } [Conditional("DEBUG")] private static void DebugWriteLog(JsonObject jsonLog, UnixTime timestamp) { WriteToFile(jsonLog, "/home/atef/JsonData/" + timestamp); } private static void WriteToFile(Object obj, String fileName) { var jsonString = JsonSerializer.Serialize(obj, JsonOptions); File.WriteAllText(fileName, jsonString); } private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true, IgnoreReadOnlyProperties = false, Converters = { new JsonStringEnumConverter() }, NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals, //TODO }; private static async Task UploadTimeSeries(S3Config config, JsonObject json, UnixTime unixTime) { var payload = JsonSerializer.Serialize(json, JsonOptions); var s3Path = unixTime.Ticks + ".json"; var request = config.CreatePutRequest(s3Path); var response = await request.PutAsync(new StringContent(payload)); if (response.StatusCode != 200) { Console.WriteLine("ERROR: PUT"); var error = response.GetStringAsync(); Console.WriteLine(error); } } private static async Task UploadTopology(S3Config config, JsonObject json, UnixTime unixTime) { var payload = JsonSerializer.Serialize(json, JsonOptions); var s3Path = "topology" + unixTime.Ticks + ".json"; var request = config.CreatePutRequest(s3Path); var response = await request.PutAsync(new StringContent(payload)); if (response.StatusCode != 200) { Console.WriteLine("ERROR: PUT"); var error = response.GetStringAsync(); Console.WriteLine(error); } } }