using CliWrap; using CliWrap.Buffered; using InnovEnergy.Lib.Utils; using static InnovEnergy.App.BmsTunnel.CliPrograms; namespace InnovEnergy.App.BmsTunnel; using Nodes = IReadOnlyList; public readonly struct BatteryConnection { public String Tty { get; } public Nodes Nodes { get; } private BatteryConnection(String tty, Nodes nodes) { Tty = tty; Nodes = nodes; } private BatteryConnection(String tty, params Byte[] nodes) : this(tty, (Nodes) nodes) { } public static async Task Connect(SshHost? host = null) { Console.WriteLine("searching battery connection..."); return await GetConnectionFromDBus(host) ?? await GetConnectionFromDevTty(host); } private static async Task GetConnectionFromDevTty(SshHost? host) { var fs = FileSystem.OnHost(host); var ttys = await fs.GetFiles("/dev", FileType.CharacterDevice); var candidateTtys = ttys .Where(f => f.StartsWith("ttyUSB")) .Select(t => t.Split("/").LastOrDefault()) .NotNull() .ToList(); if (!candidateTtys.Any()) { Console.WriteLine("no USB converter cable attached!"); return null; } var userSelectedTty = "Select TTY:".ChooseFrom(candidateTtys); return userSelectedTty != null ? new BatteryConnection(userSelectedTty) : null; } private static async Task GetConnectionFromDBus(SshHost? host) { var tty = await LsDBus .OnHost(host) .ExecuteBufferedAsync() .Select(ParseBatteryTty); if (tty == null) return null; Console.WriteLine("found battery on DBus"); var availableNodes = await GetNodes(tty, host); return new BatteryConnection(tty, availableNodes); } private static CommandTask GetNodes(String tty, SshHost? host = null) { const String defaultArgs = "--system --print-reply --type=method_call / com.victronenergy.BusItem.GetValue"; return DBusSend .OnHost(host) .AppendArgument($"--dest=com.victronenergy.battery.{tty}") .AppendArgument(defaultArgs) .ExecuteBufferedAsync() .Select(ParseBatteryNodes); } private static Nodes ParseBatteryNodes(BufferedCommandResult result) { return result .StandardOutput .Split(Environment.NewLine) .Where(l => l.Contains("_Battery/")) .Select(l => l.Split('/')[1]) .Where(n => Byte.TryParse(n, out _)) .Select(Byte.Parse) .Distinct() .OrderBy(n => n) .ToList(); } private static String? ParseBatteryTty(BufferedCommandResult result) { return result .StandardOutput .Split(Environment.NewLine) .Where(l => l.Contains("com.victronenergy.battery.")) .SelectMany(l => l.Split('.')) .LastOrDefault(); } }