using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Text; namespace AiQ_GUI { public class Network { public static HttpClient? SingleHTTPClient; public static HttpClient Client => SingleHTTPClient ?? throw new InvalidOperationException("Client not initialized."); public static void Initialize(string username, string password) { HttpClientHandler handler = new HttpClientHandler { MaxConnectionsPerServer = 25, Credentials = new NetworkCredential(username, password) }; SingleHTTPClient?.Dispose(); // Dispose old client if needed SingleHTTPClient = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(20) }; } // Handles get and post to the camera API public static async Task SendHttpRequest(string url, HttpMethod method, int? Timeout, string? jsonData = null, string[]? Headers = null) { try { HttpRequestMessage request = new HttpRequestMessage(method, url); if (jsonData != null) // Fills in the body of the request if jsonData is provided request.Content = new StringContent(jsonData, Encoding.UTF8, "application/json"); if (Headers != null) // Fills in the headers of the request if Headers are provided { foreach (string Header in Headers) { string[] parts = Header.Split(':'); if (parts.Length == 2) request.Headers.Add(parts[0].Trim(), parts[1].Trim()); else throw new ArgumentException($"Invalid header format: {Header}"); } } int timeoutMs = (Timeout ?? 10) * 1000; // Convert from seconds to ms using CancellationTokenSource cts = new CancellationTokenSource(timeoutMs); using HttpResponseMessage response = await Client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cts.Token); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync(cts.Token); // Use token here too if you're chaining timeouts } catch (TaskCanceledException ex) { return $"HTTP error calling {url}: {ex.Message}"; } catch (HttpRequestException ex) { return $"HTTP error calling {url}: {ex.Message}"; } catch (Exception ex) { return $"Unexpected error calling {url}: {ex.Message}"; } } public async static Task> SearchForCams() { const int sendPort = 6666; const int receivePort = 6667; IList FoundCams = []; byte[] discoveryPacket = [0x50, 0x4f, 0x4c, 0x4c, 0xaf, 0xb0, 0xb3, 0xb3, 0xb6, 0x01, 0xa8, 0xc0, 0x0b, 0x1a, 0x00, 0x00]; async Task SendAndListen(IPAddress localIp) { using (UdpClient sender = new(new IPEndPoint(localIp, sendPort))) { sender.EnableBroadcast = true; sender.Connect(new IPEndPoint(IPAddress.Broadcast, sendPort)); sender.Send(discoveryPacket, discoveryPacket.Length); } using UdpClient receiver = new(receivePort); // Listen for replies on fixed port receiver.Client.ReceiveTimeout = 750; DateTime timeout = DateTime.Now.AddMilliseconds(750); try { while (DateTime.Now < timeout) { if (receiver.Available > 0) { UdpReceiveResult result = await receiver.ReceiveAsync(); byte[] recvBuffer = result.Buffer; if (recvBuffer.Length >= 52) // Safety check { byte[] ipBytes = recvBuffer.Skip(recvBuffer.Length - 52).Take(4).Reverse().ToArray(); string ipToAdd = string.Join(".", ipBytes); if (!FoundCams.Contains(ipToAdd)) FoundCams.Add(ipToAdd); } } await Task.Delay(50); // brief wait to allow data in } } catch (SocketException ex) when (ex.SocketErrorCode == SocketError.TimedOut) { // No data received in time — normal case } } // Get first IPv4 interface (non-loopback) foreach (IPAddress ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList) { try { await SendAndListen(ip); } catch { } } return FoundCams; } // Ping to make sure devices are connected to the network, be aware it isn't consistant across subnets. public async static Task PingIP(string ipAddress) { if (RegexCache.RegexIPPattern().IsMatch(ipAddress)) { try { Ping myPing = new(); PingReply reply = await myPing.SendPingAsync(ipAddress, 1000); // Timeout is 1s return reply.Status == IPStatus.Success; } catch (PingException ex) { MainForm.Instance.AddToActionsList($"PingException: Unable to ping {ipAddress}. Reason: {ex.Message}"); } catch (SocketException ex) { MainForm.Instance.AddToActionsList($"SocketException: Network error while pinging {ipAddress}. Reason: {ex.Message}"); } catch (Exception ex) { MainForm.Instance.AddToActionsList($"Unexpected error while pinging {ipAddress}: {ex.Message}"); } } return false; } // Start a thread that pings each IP in the hardware accessories menu public static async Task PingAndUpdateUI(string IP, Label label, string ItemName, ToolTip TT, Button[]? buttonsToEnable = null) { bool isAvailable = await PingIP(IP); label.Invoke(() => { if (isAvailable) { label.Text = "✔"; label.ForeColor = Color.ForestGreen; TT.SetToolTip(label, ItemName + " Available"); } else { label.Text = "❌"; label.ForeColor = Color.Red; TT.SetToolTip(label, "No ping from " + ItemName); } if (buttonsToEnable != null) { foreach (Button button in buttonsToEnable) button.Enabled = isAvailable; } }); } } }