Add project files.
This commit is contained in:
354
FakeCamera/FakeCamera.cs
Normal file
354
FakeCamera/FakeCamera.cs
Normal file
@@ -0,0 +1,354 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace AiQ_GUI
|
||||
{
|
||||
public enum CAMTYPE
|
||||
{
|
||||
GOOD,
|
||||
BAD,
|
||||
BIZARRE
|
||||
}
|
||||
|
||||
public class FakeCamera
|
||||
{
|
||||
public const string JSONLoc = "C:\\Users\\BradleyBorn\\OneDrive - MAV Systems Ltd\\Desktop\\AIQ_GUI_TEST\\FakeCameraGood\\";
|
||||
|
||||
public static bool Snapshot = false;
|
||||
private HttpListener listener;
|
||||
|
||||
public FakeCamera(int port = 8080)
|
||||
{
|
||||
listener = new HttpListener();
|
||||
listener.Prefixes.Add($"http://*:{port}/");
|
||||
listener.Prefixes.Add("http://localhost:8080/");
|
||||
}
|
||||
|
||||
public async Task StartAsync(CAMTYPE camType)
|
||||
{
|
||||
try
|
||||
{
|
||||
listener.Start();
|
||||
MainForm.Instance.AddToActionsList("Started server successfully");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MainForm.Instance.AddToActionsList($"Failed to start server: {ex.Message}");
|
||||
return;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
HttpListenerContext? context = await listener.GetContextAsync();
|
||||
_ = HandleRequestAsync(context, camType);
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (listener.IsListening)
|
||||
{
|
||||
listener.Stop();
|
||||
listener.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ValidateCredentials(string username, string password, CAMTYPE camType)
|
||||
{
|
||||
string filePath = camType switch
|
||||
{
|
||||
CAMTYPE.GOOD => $"{JSONLoc}Versions.json",
|
||||
CAMTYPE.BAD => $"{JSONLoc}Versions_Bad.json",
|
||||
CAMTYPE.BIZARRE => $"{JSONLoc}Versions_Bizarre.json",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(camType), "Invalid camera type")
|
||||
};
|
||||
|
||||
string versJson = File.ReadAllText(filePath);
|
||||
Versions vers = JsonConvert.DeserializeObject<Versions>(versJson);
|
||||
TimeSpan t = DateTime.UtcNow - new DateTime(1970, 1, 1);
|
||||
int secondsSinceEpoch = (int)t.TotalSeconds;
|
||||
string PASS = Lics.GeneratePassword(vers.MAC, vers.version, secondsSinceEpoch);
|
||||
|
||||
return username == "developer" && password == PASS;
|
||||
}
|
||||
|
||||
private static async Task HTTPReplySetup(HttpListenerContext context, string ResponseText, int StatusCode, string ContentType)
|
||||
{
|
||||
try
|
||||
{
|
||||
context.Response.StatusCode = StatusCode;
|
||||
context.Response.ContentType = ContentType;
|
||||
byte[] responseBytes = Encoding.UTF8.GetBytes(ResponseText);
|
||||
context.Response.ContentLength64 = responseBytes.Length;
|
||||
|
||||
await context.Response.OutputStream.WriteAsync(responseBytes);
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.Response.OutputStream.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HTTPReplySetup(HttpListenerContext context, byte[] content, int statusCode, string contentType)
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
context.Response.ContentType = contentType;
|
||||
context.Response.ContentLength64 = content.Length;
|
||||
|
||||
await context.Response.OutputStream.WriteAsync(content);
|
||||
await context.Response.OutputStream.FlushAsync();
|
||||
context.Response.OutputStream.Close();
|
||||
}
|
||||
|
||||
private static async Task HandleRequestAsync(HttpListenerContext context, CAMTYPE camType)
|
||||
{
|
||||
string path = context.Request.Url?.AbsolutePath ?? "/";
|
||||
string Method = context.Request.HttpMethod;
|
||||
string headers = context.Request.Headers.ToString();
|
||||
string Content = context.Request.InputStream?.ToString() ?? "";
|
||||
|
||||
// Define endpoints that do NOT require authentication
|
||||
string[] publicEndpoints = ["/api/versions", "/api/diagnostics"];
|
||||
|
||||
bool requiresAuth = !publicEndpoints.Contains(path);
|
||||
|
||||
if (requiresAuth)
|
||||
{
|
||||
string authHeader = context.Request.Headers["Authorization"];
|
||||
|
||||
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Basic "))
|
||||
{
|
||||
context.Response.AddHeader("WWW-Authenticate", "Basic realm=\"FakeCamera\"");
|
||||
await HTTPReplySetup(context, "", 401, "text/plain");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string encodedCredentials = authHeader.Substring("Basic ".Length).Trim();
|
||||
string decodedCredentials = Encoding.UTF8.GetString(Convert.FromBase64String(encodedCredentials));
|
||||
|
||||
string[] parts = decodedCredentials.Split(':', 2);
|
||||
|
||||
if (parts.Length != 2)
|
||||
{
|
||||
throw new Exception("Invalid Basic auth format. Expected username:password.");
|
||||
}
|
||||
|
||||
string username = parts[0];
|
||||
string password = parts[1];
|
||||
|
||||
if (!ValidateCredentials(username, password, camType))
|
||||
{
|
||||
throw new Exception("Invalid credentials");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
context.Response.AddHeader("WWW-Authenticate", "Basic realm=\"FakeCamera\"");
|
||||
await HTTPReplySetup(context, "", 401, "text/plain");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (Method == "GET")
|
||||
{
|
||||
if (path.Equals("/Infrared/led-controls")) // Works
|
||||
{
|
||||
string[] allowedPowers = ["LOW", "MID", "HIGH", "SAFE", "OFF"];
|
||||
string power = context.Request.QueryString["power"]?.ToUpper();
|
||||
|
||||
if (allowedPowers.Contains(power))
|
||||
await HTTPReplySetup(context, "Power levels set successfully", 200, "text/plain");
|
||||
else
|
||||
await HTTPReplySetup(context, "Power level not accepted", 400, "text/plain");
|
||||
}
|
||||
else if (path.Equals("/api/versions"))
|
||||
{
|
||||
string versionsJson = File.ReadAllText(camType switch
|
||||
{
|
||||
CAMTYPE.GOOD => $"{JSONLoc}Versions.json",
|
||||
CAMTYPE.BAD => $"{JSONLoc}Versions_Bad.json",
|
||||
CAMTYPE.BIZARRE => $"{JSONLoc}Versions_Bizarre.json",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
});
|
||||
|
||||
Versions vers = JsonConvert.DeserializeObject<Versions>(versionsJson);
|
||||
vers.timeStamp = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
|
||||
versionsJson = JsonConvert.SerializeObject(vers);
|
||||
|
||||
await HTTPReplySetup(context, versionsJson, 200, "application/json");
|
||||
}
|
||||
else if (path.Equals("/api/diagnostics"))
|
||||
{
|
||||
string filePath = camType switch
|
||||
{
|
||||
CAMTYPE.GOOD => $"{JSONLoc}Diagnostics.json",
|
||||
CAMTYPE.BAD => $"{JSONLoc}Diagnostics_Bad.json",
|
||||
CAMTYPE.BIZARRE => $"{JSONLoc}Diagnostics_Bizarre.json",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
string diagsJson = File.ReadAllText(filePath);
|
||||
|
||||
if (camType == CAMTYPE.GOOD)
|
||||
{
|
||||
Diags diags = JsonConvert.DeserializeObject<Diags>(diagsJson);
|
||||
diags.timeStamp = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
|
||||
diagsJson = JsonConvert.SerializeObject(diags);
|
||||
}
|
||||
|
||||
await HTTPReplySetup(context, diagsJson, 200, "application/json");
|
||||
}
|
||||
else if (path.Equals("/api/zoomLock")) // Fixed
|
||||
{
|
||||
string enable = context.Request.QueryString["enable"];
|
||||
|
||||
if (!string.IsNullOrEmpty(enable) && enable.Equals("true", StringComparison.OrdinalIgnoreCase)) // FIXED Use http://localhost:8080/api/zoomLock?enable=true
|
||||
{
|
||||
await HTTPReplySetup(context, "Zoom lock enabled.", 200, "text/plain");
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(enable) && enable.Equals("false", StringComparison.OrdinalIgnoreCase)) // FIXED Use http://localhost:8080/api/zoomLock?enable=false
|
||||
{
|
||||
await HTTPReplySetup(context, "Zoom lock disabled.", 200, "text/plain");
|
||||
}
|
||||
else
|
||||
{
|
||||
await HTTPReplySetup(context, "Missing or invalid 'enable' parameter.", 400, "text/plain");
|
||||
}
|
||||
}
|
||||
else if (path.Equals("/Infrared-camera-factory-reset") || path.Equals("/Colour-camera-factory-reset")) // WORKS
|
||||
{
|
||||
await HTTPReplySetup(context, "Factory reset OK.", 200, "text/plain");
|
||||
}
|
||||
else if (path.Equals("/Infrared-camera-control") || path.Equals("/Colour-camera-control")) // Fixed
|
||||
{
|
||||
string viscaReply = "9041FF9051FF";
|
||||
|
||||
if (!RegexCache.VISCARegex().IsMatch(context.Request.QueryString["commandHex"]))
|
||||
viscaReply = "9060FF";
|
||||
|
||||
await HTTPReplySetup(context, viscaReply, 200, "text/plain");
|
||||
}
|
||||
else if (path.Equals("/SightingCreator-plate-positions")) //Works
|
||||
{
|
||||
string trimJson = File.ReadAllText($"{JSONLoc}Trim.json");
|
||||
await HTTPReplySetup(context, trimJson, 200, "application/json");
|
||||
}
|
||||
else if (path.Equals("/Infrared-snapshot")) // Fixed
|
||||
{
|
||||
string jpegPath;
|
||||
|
||||
if (!Snapshot)
|
||||
{
|
||||
jpegPath = $"{JSONLoc}IR_Open_image.jpg"; // Light Image
|
||||
Snapshot = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
jpegPath = $"{JSONLoc}IR_Tight_image.jpg"; // Dark Image
|
||||
}
|
||||
|
||||
byte[] jpegBytes = File.ReadAllBytes(jpegPath);
|
||||
await HTTPReplySetup(context, jpegBytes, 200, "image/jpeg");
|
||||
}
|
||||
else if (path.Equals("/Colour-snapshot")) // Fixed
|
||||
{
|
||||
byte[] jpegBytes = File.ReadAllBytes($"{JSONLoc}OV_image.jpg");
|
||||
await HTTPReplySetup(context, jpegBytes, 200, "image/jpeg");
|
||||
}
|
||||
else if (path.Equals("/api/fetch-config"))
|
||||
{
|
||||
ConfigObject config = JsonConvert.DeserializeObject<ConfigObject>(Content);
|
||||
|
||||
if (config.Id == "GLOBAL--NetworkConfig")
|
||||
{
|
||||
// TODO - Return success message?
|
||||
}
|
||||
else
|
||||
{
|
||||
await HTTPReplySetup(context, "ID not valid.", 200, "text/plain");
|
||||
}
|
||||
}
|
||||
else if (path.Equals("/api/RaptorOCR-auto-license")) // Works
|
||||
{
|
||||
string vaxtorJson = "{ \"protectionKeyId\":\"123456789012345678\" }";
|
||||
await HTTPReplySetup(context, vaxtorJson, 200, "application/json");
|
||||
}
|
||||
else
|
||||
{
|
||||
await HTTPReplySetup(context, $"GET request for {path} not supported", 400, "text/plain");
|
||||
}
|
||||
}
|
||||
else if (Method == "POST")
|
||||
{
|
||||
if (path.Equals("/api/update-config"))
|
||||
{
|
||||
ConfigObject fo = JsonConvert.DeserializeObject<ConfigObject>(Content);
|
||||
|
||||
if (fo.Id == "GLOBAL--NetworkConfig" || fo.Id == "SightingCreator" || fo.Id == "RaptorOCR")
|
||||
{
|
||||
await HTTPReplySetup(context, "Update successful.", 200, "text/plain");
|
||||
}
|
||||
else if (fo.Id == "Internal Config")
|
||||
{
|
||||
string SerialNumber = "";
|
||||
string ModelNumber = "";
|
||||
fo.Fields.ForEach(field =>
|
||||
{
|
||||
if (field.Property == "propSerialNumber")
|
||||
SerialNumber = field.Value;
|
||||
else if (field.Property == "propMavModelNumber")
|
||||
ModelNumber = field.Value;
|
||||
});
|
||||
|
||||
// Update JSON with new SerialNumber and ModelNumber beofre sending back to the client
|
||||
using StreamReader r = new($"{JSONLoc}InternalConfig.json");
|
||||
string InternalConfig_JSON = r.ReadToEnd();
|
||||
InternalConfig_JSON = InternalConfig_JSON.Replace("SSSSSS", SerialNumber).Replace("MMMMMM", ModelNumber);
|
||||
HTTPReplySetup(context, InternalConfig_JSON, 200, "application/json");
|
||||
}
|
||||
else
|
||||
{
|
||||
await HTTPReplySetup(context, "ID not valid.", 200, "text/plain");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await HTTPReplySetup(context, $"POST request for {path} not supported.", 400, "text/plain");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await HTTPReplySetup(context, "Bad Request.", 400, "text/plain");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await HTTPReplySetup(context, $"Server error: {ex.Message}", 500, "text/plain");
|
||||
MainForm.Instance.AddToActionsList($"Server error handling {Method} {path}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public class ConfigObject
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("fields")]
|
||||
public List<Field> Fields { get; set; }
|
||||
}
|
||||
|
||||
public class Field
|
||||
{
|
||||
[JsonProperty("property")]
|
||||
public string Property { get; set; }
|
||||
|
||||
[JsonProperty("value")]
|
||||
public string Value { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user