using Newtonsoft.Json; using System.Net.Http.Headers; namespace AiQ_GUI { public enum LEDPOWER { LOW, MID, HIGH, SAFE, OFF } public class FlexiAPI { // GET API from camera public static async Task APIHTTPRequest(string EndPoint, string IPAddress, int? Timeout = 2) { try { string URL = $"http://{IPAddress}{EndPoint}"; return await Network.SendHttpRequest(URL, HttpMethod.Get, Timeout); } catch (Exception ex) { return $"Error during GET request: {ex.Message}"; } } // For 'update-config' sending a change to the AiQ // TODO - Not many of the references check the output is positive. Need them all to check. public static async Task HTTP_Update(string ID, string IPAddress, string[,] jsonArrayData) { try { string JSONdata = BuildJsonUpdate(jsonArrayData, ID); string url = $"http://{IPAddress}/api/update-config"; return await Network.SendHttpRequest(url, HttpMethod.Post, 2, JSONdata); } catch (Exception ex) { return $"Error in HTTP_Update: {ex.Message}"; } } // For 'fetch-config' getting info from AiQ public static async Task HTTP_Fetch(string ID, string IPAddress, int? Timeout = 2) { try { string JSONdata = "{ \"id\":\"" + ID + "\" }"; string url = $"http://{IPAddress}/api/fetch-config"; return await Network.SendHttpRequest(url, HttpMethod.Get, Timeout, JSONdata); } catch (Exception ex) { return $"Error in HTTP_Fetch: {ex.Message}"; } } // For sending VISCA directly to the camera module // Be aware this does bypass Flexi's watchdog so settings like zoom, focus, SIG wont keep forever public static async Task APIHTTPVISCA(string IPAddress, string VISCA, bool IR) { string suffix = $"-camera-control?commandHex={VISCA}"; if (IR) // Add on which camera to control suffix = "/Infrared" + suffix; else suffix = "/Colour" + suffix; return await APIHTTPRequest(suffix, IPAddress); } // Sets the LED level into the camera public static async Task APIHTTPLED(string IPAddress, LEDPOWER LEVEL) { // Always Infrared as LED's are controlled from infrared page // Level can be word eg. SAFE or hex eg. 0x0E string suffix = $"/Infrared/led-controls?power={LEVEL}"; return await APIHTTPRequest(suffix, IPAddress); } public static async Task SendBlobFileUpload(string url, string filePath, string fileName) { try { Network.Client.DefaultRequestHeaders.ExpectContinue = false; MultipartFormDataContent content; byte[] fileBytes = await File.ReadAllBytesAsync(filePath).ConfigureAwait(false); MemoryStream ms = new(fileBytes); StreamContent streamContent = new(ms); streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); content = new MultipartFormDataContent { { streamContent, "upload", fileName } }; using HttpResponseMessage response = await Network.Client.PostAsync(url, content); string responseBody = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) return $"Server returned {(int)response.StatusCode}: {response.ReasonPhrase}. Details: {responseBody}"; return responseBody; } catch (TaskCanceledException) { return $"Timeout uploading to {url}."; } catch (HttpRequestException ex) { return $"HTTP error uploading to {url}: {ex.Message}"; } catch (Exception ex) { return $"Unexpected error uploading to {url}: {ex.Message} {(ex.InnerException?.Message ?? "")}"; } } public static async Task GetVersions(string IPAddress) { string JSON = await APIHTTPRequest("/api/versions", IPAddress); // Version API request if (JSON == null || JSON.Contains("Error") || JSON.Contains("Timeout")) return null; Logging.LogMessage("Received versions JSON: " + JSON); try { return JsonConvert.DeserializeObject(JSON); } catch { MainForm.Instance.AddToActionsList("Cannot deserialise Versions JSON" + Environment.NewLine + JSON); return null; // If it fails to parse the JSON } } public static async Task GetDiagnostics(string IPAddress) { string JSON = await APIHTTPRequest("/api/diagnostics", IPAddress, 20); // Version API request if (JSON == null || JSON.Contains("Error") || JSON.Contains("Timeout")) { MainForm.Instance.AddToActionsList("Error talking to Flexi, are you sure this is an AiQ?" + Environment.NewLine + JSON); return null; } Logging.LogMessage("Received diagnostics JSON: " + JSON); try { return JsonConvert.DeserializeObject(JSON); } catch { MainForm.Instance.AddToActionsList("Cannot deserialise Diagnostics JSON" + Environment.NewLine + JSON); return null; // If it fails to parse the JSON } } public static async Task SetZoomLockOn(string IP) { // Set Zoomlock on and if it fails ask user to set it manually if (!(await APIHTTPRequest("/api/zoomLock?enable=true", IP)).Contains("Zoom lock enabled.") && !await MainForm.Instance.DisplayQuestion("Could not set zoomlock on" + Environment.NewLine + "Set Zoomlock to on then click YES. Click NO to restart.")) { return false; } return true; } public static async Task ZoomModules(string VISCAInput, string IPAddress) { // Populate the VISCA command with the four zoom characters string VISCA = $"810104470{VISCAInput[0]}0{VISCAInput[1]}0{VISCAInput[2]}0{VISCAInput[3]}FF"; Task TS1 = APIHTTPVISCA(IPAddress, VISCA, true); Task TS2 = APIHTTPVISCA(IPAddress, VISCA, false); await Task.WhenAll(TS1, TS2); const string ExpReply = "9041FF9051FF"; if (TS1.Result == ExpReply && TS1.Result == ExpReply) return true; return false; } // Processes the network config from the camera and returns a string indicating the status public static async Task ProcessNetworkConfig(string IPAddress) { try { string JSON = await HTTP_Fetch("GLOBAL--NetworkConfig", IPAddress, 10); NetworkConfig NC = JsonConvert.DeserializeObject(JSON); if (Convert.ToBoolean(NC.propDHCP.Value) == false) return "Set to DHCP"; else return "Set to 211"; } catch (Exception ex) { MainForm.Instance.AddToActionsList($"Error in getting network config from camera: {ex.Message}"); return null; // Return empty string if there is an error } } // Knowing the format this builds the message to send to AiQ private static string BuildJsonUpdate(string[,] jsonData, string id) { if (jsonData == null || jsonData.GetLength(1) != 2) throw new ArgumentException("Input data must be a non-null 2D array with two columns."); int rows = jsonData.GetLength(0); List fields = []; for (int i = 0; i < rows; i++) // Puts each part of the array into correct format { fields.Add(new { property = jsonData[i, 0], value = jsonData[i, 1] }); } var updateObject = new // Correct naming and format so it is JSON ready { id, fields }; return JsonConvert.SerializeObject(updateObject, Formatting.None); // Uses no white space } // Change network settings to 192.168.1.211 and wait 5 seconds to see if it takes effect public static async Task ChangeNetwork211(string IPAddress) { // Update GLOBAL--NetworkConfig with fixed IP and turn off DHCP string[,] TEST_JSON = { { "propDHCP", "false" }, { "propHost", "192.168.1.211" }, { "propNetmask", "255.255.255.0" }, { "propGateway", "192.168.1.1" } }; await HTTP_Update("GLOBAL--NetworkConfig", IPAddress, TEST_JSON); await Task.Delay(5000); // Wait for 5 seconds to allow the camera to restart IList FoundCams = await Network.SearchForCams(); // Have to check via broadcast becuase Ping sometimes fails across subnets return FoundCams.Contains("192.168.1.211"); } // Change network settings to DHCP and restart camera for it to take effect public async static Task ChangeNetworkToDHCP(string IPAddress) { string[,] TEST_JSON = { { "propDHCP", "true" } }; // Update GLOBAL--NetworkConfig with fixed IP and turn off DHCP await HTTP_Update("GLOBAL--NetworkConfig", IPAddress, TEST_JSON); // TODO - Check if this worked, if not return false } } //Items recieved in Versions API public class Versions { public string version { get; set; } = string.Empty; public string revision { get; set; } = string.Empty; public string buildtime { get; set; } = string.Empty; public string appname { get; set; } = string.Empty; public string MAC { get; set; } = string.Empty; public int timeStamp { get; set; } public string UUID { get; set; } = string.Empty; public string proquint { get; set; } = string.Empty; [JsonProperty("Serial No.")] public string Serial { get; set; } = string.Empty; [JsonProperty("Model No.")] public string Model { get; set; } = string.Empty; } // Items returned in the diagnostics api public class Diags { [JsonProperty("version")] public string FlexiVersion { get; set; } = string.Empty; [JsonProperty("revision")] public string FlexiRevision { get; set; } = string.Empty; public string serialNumber { get; set; } = string.Empty; public string modelNumber { get; set; } = string.Empty; public string MAC { get; set; } = string.Empty; public long timeStamp { get; set; } public Licenses licenses { get; set; } = new Licenses(); [JsonProperty("internalTemperature")] public double IntTemperature { get; set; } [JsonProperty("cpuUsage")] public double CPUusage { get; set; } public List trim { get; set; } = []; public bool zoomLock { get; set; } public Module IRmodule { get; set; } = new Module(); public Module OVmodule { get; set; } = new Module(); [JsonProperty("ledChannelVoltages")] public List LedVoltage { get; set; } = []; [JsonProperty("ledChannelCurrents")] public List LedCurrent { get; set; } = []; } public class Module { public int zoom { get; set; } public int expMode { get; set; } public string firmwareVer { get; set; } = string.Empty; } public class Trim { public int infraredX { get; set; } public int infraredY { get; set; } public int colourX { get; set; } public int colourY { get; set; } } public class NetworkConfig { public Property propDHCP { get; set; } = new Property(); } public class Property { public string Value { get; set; } = string.Empty; } }