using System; using System.Threading.Tasks; using System.Threading; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Timers; using System.Text.RegularExpressions; using Newtonsoft.Json.Linq; using System.Diagnostics; using Serilog; /* Example: Camera myCamera = new Camera(); myCamera.CameraPowerOnProcedure(); App.myCamera.StartRecording(); myCamera.StopRecording(); await myCamera.CameraTurnOffAsync(); */ namespace VideoBooth { public class Camera { private String cid = ""; private String bid = ""; private String modelName = ""; private String serialNumber = ""; private String browserKey = ""; private Uri baseAddress; private static System.Timers.Timer aTimer; private CookieContainer myCookieContainer = new CookieContainer(); private long timestampInit = 0; public bool connectionBoxConnected = false; public bool cameraConnected = false; public int liveViewPortNumber = 0; //Status public String movieRecordingState = ""; public String recordingSettingMovie = ""; public String batteryLevelIndicator = ""; public String MediaSlot1Status = ""; public int remainingShootingTime = 0; /// /// TODO /// /// public Camera() { Log.Information("[Camera] constructor called"); baseAddress = new Uri("http://169.254.200.200"); // broadcast address when camera is in stand-by cid = "169.254.200.200:-1:-1"; Log.Information("[Camera] constructor done"); } /// /// TODO /// /// public void PrepareState() { // get 169.254.200.200/ // This gets the index page which sets a cookie in the cookie container var responseString = ""; using (var handler = new HttpClientHandler { CookieContainer = myCookieContainer }) using (var client = new HttpClient(handler) { BaseAddress = this.baseAddress }) { var response = client.GetAsync("").Result; responseString = response.Content.ReadAsStringAsync().Result; } this.timestampInit = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); // get browser key from values.php this.getBrowserKey().Wait(); // sets the browser key Log.Information("Browserkey set: {0}", this.browserKey); } /// /// TODO /// /// public Boolean CanRecord() { if (connectionBoxConnected == false) { Log.Information("[record check]\tNo connection box"); return false; } else if (cameraConnected == false) { Log.Information("[record check]\tNo camera"); return false; } else if (MediaSlot1Status == "NO CARD") { Log.Information("[record check]\tNo card"); return false; } else if (remainingShootingTime < 120) { Log.Information("[record check]\tShooting time returned from camera API is 0 seconds."); return false; } else { Log.Information("[record check]\tCamera can record"); return true; } } /// /// TODO /// /// public void StopRecording() { this.CameraRecordStopCommand(); } public void Focus() { this.StartFocus(); this.StopFocus(); } /// /// TODO /// /// public void StartRecording() { this.CheckBoxStateExtendedFormData(); this.CheckBoxStateBidsOn(); this.CameraRecordStartCommand(); } /// /// TODO /// /// public void TurnOff() { this.CameraTurnOffAsync().Wait(); } /// /// TODO /// /// // TODO; create int return value to indicate if camera has been found public void CameraPowerOnProcedure() { Log.Information("Power on procedure:"); // TODO: Check if connection box is online // Check if camera is attached to connection box this.GetCam0InfoAsyncStartup().Wait(); if (cameraConnected == false) { Log.Information("\t[Camera] turning on"); CameraTurnOn(); Log.Information("\t[Camera] waiting 10 seconds for powering on"); Thread.Sleep(10000); // wait 10 seconds Log.Information("\t[Camera] checking if powered on"); // Check if camera is attached to connection box this.GetCam0InfoAsyncStartup().Wait(); // Try second time if camera still not found if (cameraConnected == false) { Log.Information("\t[Camera] camera not found yet"); Log.Information("[Camera] turning on again"); CameraTurnOn(); Log.Information("[Camera] waiting 10 seconds for powering on"); Thread.Sleep(10000); // wait 10 seconds Log.Information("[Camera] checking again if powered on"); // Check if camera is attached to connection box this.GetCam0InfoAsyncStartup().Wait(); } } // Assume camera is on for now; This will be periodically be checked in timed function this.SetTimer(); } /// /// TODO /// /// private void SetTimer() { // Create a timer with a two second interval. aTimer = new System.Timers.Timer(10_000); // Hook up the Elapsed event for the timer. aTimer.Elapsed += this.OnTimedEvent; aTimer.AutoReset = true; aTimer.Enabled = true; } /// /// TODO /// /// private void CheckBoxStateBidsOn() { var values1 = new Dictionary { { "browser_key", this.browserKey }, { "bids_on[]", "169.254.200.200" } }; var content1 = new FormUrlEncodedContent(values1); //Log.Information("Sending BOX status request from browser key {0}", this.browserKey); using (var handler = new HttpClientHandler() { CookieContainer = this.myCookieContainer }) using (var client = new HttpClient(handler) { BaseAddress = baseAddress }) { client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("*/*")); client.DefaultRequestHeaders.TryAddWithoutValidation("Origin", "http://169.254.200.200"); client.DefaultRequestHeaders.TryAddWithoutValidation("Referer", "http://169.254.200.200"); client.DefaultRequestHeaders.TryAddWithoutValidation("Connection", "keep-alive"); client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/x-www-form-urlencoded"); HttpResponseMessage response; string responseString = ""; try { response = client.PostAsync("/api.php?v=2&action=check_box_state", content1).Result; responseString = response.Content.ReadAsStringAsync().Result; } catch (Exception e) { Log.Information(e.ToString()); } Log.Information(responseString); } } /// /// TODO /// /// private void CheckBoxStateExtendedFormData() { var values1 = new Dictionary { { "browser_key", this.browserKey }, { "bids_off[]", "169.254.200.200" }, { "master_notification", "1" }, { "ts", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString() }, { "sync_type", "0x01" }, }; var content1 = new FormUrlEncodedContent(values1); //Log.Information("Sending BOX status request from browser key {0}", this.browserKey); using (var handler = new HttpClientHandler() { CookieContainer = this.myCookieContainer }) using (var client = new HttpClient(handler) { BaseAddress = baseAddress }) { client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("*/*")); client.DefaultRequestHeaders.TryAddWithoutValidation("Origin", "http://169.254.200.200"); client.DefaultRequestHeaders.TryAddWithoutValidation("Referer", "http://169.254.200.200"); client.DefaultRequestHeaders.TryAddWithoutValidation("Connection", "keep-alive"); client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/x-www-form-urlencoded"); //TODO, catch exceptions (https://forums.xamarin.com/discussion/101202/try-catch-on-httpclient-not-working) try { var response1 = client.PostAsync("/api.php?v=2&action=check_box_state", content1).Result; var responseString1 = response1.Content.ReadAsStringAsync().Result; //Log.Information(responseString1); } catch (Exception e) { Log.Information(e.ToString()); } } } /// /// TODO /// /// private void OnTimedEvent(Object source, ElapsedEventArgs e) { this.UpdateCam0StatusAsync().Wait(); } /// /// TODO /// /// private async Task UpdateCam0StatusAsync() { Log.Debug("[Status update] Retrieving latest status from camera... "); var values = new Dictionary { { "v", "2" }, { "action", "check_cam_state" }, { "param", "1" }, { "bid", "169.254.200.200" } }; using (var handler = new HttpClientHandler { CookieContainer = myCookieContainer }) // TODO: test, probably this can be deleted using (HttpClient client = new HttpClient()) { var camStateInfo = new JObject(); //TODO, catch exceptions (https://forums.xamarin.com/discussion/101202/try-catch-on-httpclient-not-working) try { var content = new FormUrlEncodedContent(values); var response = await client.PostAsync("http://169.254.200.200/api.php", content); var responseString = await response.Content.ReadAsStringAsync(); JObject allInfo = JObject.Parse(responseString); camStateInfo = (JObject)allInfo["cams"][0]["prop"]["state"]; if (camStateInfo.ContainsKey("Movie_Recording_State")) movieRecordingState = camStateInfo["Movie_Recording_State"][1].ToString(); if (camStateInfo.ContainsKey("Recoding_Setting_Movie")) recordingSettingMovie = camStateInfo["Recoding_Setting_Movie"][1].ToString(); if (camStateInfo.ContainsKey("Battery_Level_Indicator")) batteryLevelIndicator = camStateInfo["Battery_Level_Indicator"][1].ToString(); if (camStateInfo.ContainsKey("Media_SLOT1_Status")) MediaSlot1Status = camStateInfo["Media_SLOT1_Status"][1].ToString(); if (camStateInfo.ContainsKey("Media_SLOT1_Remaining_Shooting_Time")) { string remainingShootingTimeString = camStateInfo["Media_SLOT1_Remaining_Shooting_Time"][1].ToString(); remainingShootingTime = Convert.ToInt32(remainingShootingTimeString, 16); } connectionBoxConnected = true; } catch (Exception e) { Log.Information("[Status update]" + e); cameraConnected = false; //Restarting camera Log.Information("[Status update] Trying to start camera again"); Log.Information("[Status update] Temporarily stopping timer"); aTimer.Stop(); Log.Information("[Status update] Power on procedure"); CameraPowerOnProcedure(); Log.Information("[Status update] Starting timer again"); aTimer.Start(); } Log.Information("\t" + movieRecordingState); } } /// /// TODO /// /// private void StopFocus() { /* On and Off command seems to be working as a toggle switch. Turn the camera on twices, turns the camera on and off. */ var values = new Dictionary { { "v", "2" }, { "action", "cam_action" }, { "action_name", "half_button_off_um" }, { "cids[]", this.cid} }; using (var handler = new HttpClientHandler { CookieContainer = myCookieContainer }) // TODO: test, probably this can be deleted using (HttpClient client = new HttpClient()) { var content = new FormUrlEncodedContent(values); var response = client.PostAsync("http://169.254.200.200/api.php", content).Result; var responseString = response.Content.ReadAsStringAsync().Result; } } /// /// TODO /// /// private void StartFocus() { /* On and Off command seems to be working as a toggle switch. Turn the camera on twices, turns the camera on and off. */ var values = new Dictionary { { "v", "2" }, { "action", "cam_action" }, { "action_name", "half_button_on_um" }, { "cids[]", this.cid} }; using (var handler = new HttpClientHandler { CookieContainer = myCookieContainer }) // TODO: test, probably this can be deleted using (HttpClient client = new HttpClient()) { var content = new FormUrlEncodedContent(values); var response = client.PostAsync("http://169.254.200.200/api.php", content).Result; var responseString = response.Content.ReadAsStringAsync().Result; } } /// /// TODO /// /// private void CameraTurnOn() { /* On and Off command seems to be working as a toggle switch. Turn the camera on twices, turns the camera on and off. */ var values = new Dictionary { { "v", "2" }, { "action", "cam_action" }, { "action_name", "power_on_um" }, { "cids[]", this.cid} }; using (var handler = new HttpClientHandler { CookieContainer = myCookieContainer }) // TODO: test, probably this can be deleted using (HttpClient client = new HttpClient()) { var content = new FormUrlEncodedContent(values); var response = client.PostAsync("http://169.254.200.200/api.php", content).Result; var responseString = response.Content.ReadAsStringAsync().Result; //Log.Information(responseString); } } public Uri getLiveStreamUri() { String baseAddressString = baseAddress.ToString(); baseAddressString = baseAddressString.TrimEnd('/'); return new Uri(baseAddressString + ":" + liveViewPortNumber + "/?action=stream"); } /// /// TODO /// /// private async Task GetCam0InfoAsyncStartup() { /* On and Off command seems to be working as a toggle switch. Turn the camera on twices, turns the camera on and off. */ Log.Information("[GetCam0InfoAsyncStartup] in method"); // Body to check cam status on bus 169.254.200.200, probably the first camera control box var values = new Dictionary { { "v", "2" }, { "action", "check_cam_state" }, { "param", "1" }, { "bid", "169.254.200.200" } }; using (var handler = new HttpClientHandler { CookieContainer = myCookieContainer }) // TODO: test, probably this can be deleted using (HttpClient client = new HttpClient()) { Log.Information("[GetCam0InfoAsyncStartup] set dict"); var content = new FormUrlEncodedContent(values); Log.Information("[GetCam0InfoAsyncStartup] URL encoded"); Log.Information(content.ToString()); var camInfo = new JObject(); //TODO, catch exceptions (https://forums.xamarin.com/discussion/101202/try-catch-on-httpclient-not-working) try { var response = client.PostAsync("http://169.254.200.200/api.php", content).Result; // fix this Result shit (https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html) Log.Information("[GetCam0InfoAsyncStartup] async post send"); var responseString = await response.Content.ReadAsStringAsync(); JObject allInfo = JObject.Parse(responseString); Log.Information("[GetCam0InfoAsyncStartup] content read"); Log.Information("[GetCam0InfoAsyncStartup] JSON parsed"); camInfo = (JObject)allInfo["cams"][0]; connectionBoxConnected = true; } catch(Exception e) { connectionBoxConnected = false; Log.Information("[GetCam0InfoAsyncStartup] " + e); } /* Example output when no camera is connected { "dummy": true, "busnumber": -1, "devicebusnumber": -1, "modelname": "", "serialnumber": "", "bid": "169.254.200.200", "cid": "169.254.200.200:-1:-1" } */ if (camInfo.ContainsKey("liveviewportnumber")) this.liveViewPortNumber = (int)camInfo["liveviewportnumber"]; if (camInfo.ContainsKey("modelname")) this.modelName = camInfo["modelname"].ToString(); if (camInfo.ContainsKey("serialnumber")) this.serialNumber = camInfo["serialnumber"].ToString(); if (camInfo.ContainsKey("bid")) this.bid = camInfo["bid"].ToString(); if (camInfo.ContainsKey("cid")) this.cid = camInfo["cid"].ToString(); /* Check if a camera is connected TODO: explain working of dummy key */ if (camInfo.ContainsKey("dummy")) { this.cameraConnected = false; } else { this.cameraConnected = true; } if (this.cameraConnected) { Log.Information("\tFound a connected camera"); Log.Information("\t\tLive view port number: {0}", this.liveViewPortNumber); Log.Information("\t\tBID: {0}", bid); Log.Information("\t\tCID: {0}", cid); Log.Information("\t\tModel name: {0}", modelName); Log.Information("\t\tSerial nr: {0}\r\n", serialNumber); } else Log.Information("No camera found"); } } /// /// TODO /// /// public async Task CameraTurnOffAsync() { /* On and Off command seems to be working as a toggle switch. Turn the camera on twices, turns the camera on and off. */ var values = new Dictionary { { "v", "2" }, { "action", "cam_action" }, { "action_name", "power_off_um" }, { "cids[]", "169.254.200.200:1:17" } // wanneer camera ingeschakeld is }; using (var handler = new HttpClientHandler { CookieContainer = myCookieContainer }) // TODO: test, probably this can be deleted using (HttpClient client = new HttpClient()) { var content = new FormUrlEncodedContent(values); try { var response = await client.PostAsync("http://169.254.200.200/api.php", content); var responseString = await response.Content.ReadAsStringAsync(); } catch (Exception e) { Log.Information(e.ToString()); } } } /// /// TODO /// /// private void CameraRecordStartCommand() { var values = new Dictionary { { "v", "2" }, { "action", "cam_action" }, { "action_name", "movie_recording_start_usb" }, { "cids[]", "169.254.200.200:1:3" }, { "browser_key", this.browserKey } }; var content = new FormUrlEncodedContent(values); Log.Information("Sending start command to cid: {0}, browser_key: {1}", this.cid, this.browserKey); using (var handler = new HttpClientHandler() { CookieContainer = this.myCookieContainer }) using (var client = new HttpClient(handler) { BaseAddress = baseAddress }) { try { var response = client.PostAsync("/api.php", content).Result; var responseString = response.Content.ReadAsStringAsync().Result; Log.Information(responseString); } catch (Exception e) { Log.Information(e.ToString()); } } } /// /// TODO /// /// private void CameraRecordStopCommand() { var values = new Dictionary { { "v", "2" }, { "action", "cam_action" }, { "action_name", "movie_recording_stop_usb" }, { "cids[]", "169.254.200.200:1:3" }, { "browser_key", this.browserKey } }; var content = new FormUrlEncodedContent(values); using (var handler = new HttpClientHandler() { CookieContainer = this.myCookieContainer }) using (var client = new HttpClient(handler) { BaseAddress = baseAddress }) { try { var response = client.PostAsync("/api.php", content).Result; var responseString = response.Content.ReadAsStringAsync().Result; Log.Information(responseString); } catch(Exception e) { Log.Information(e.ToString()); } } } /// /// TODO /// /// private async Task getBrowserKey() { var responseString = ""; using (var handler = new HttpClientHandler { CookieContainer = myCookieContainer }) using (var client = new HttpClient(handler) { BaseAddress = baseAddress }) { try { var response = await client.GetAsync("/values.php?v=" + this.timestampInit); responseString = await response.Content.ReadAsStringAsync(); } catch (Exception e) { Log.Information(e.ToString()); } } /* Grab the value from browserKey, output is like this: * var serverValues = { "browserKey": "bd198f83", "camParamKeys": ["Compression_Setting", "White_Balance", "Focus_Mode", "Exposure_Metering_Mode", "Flash_Mode", "Exposure_Program_Mode", "Drive_Mode" */ // TODO; check if a val string pattern = @"(?<=browserKey"":"").+?(?="")"; Match m = Regex.Match(responseString, pattern); //Log.Information("'{0}' found at position {1}", m.Value, m.Index); if (m.Success) { this.browserKey = m.Value; this.connectionBoxConnected = true; } else { this.connectionBoxConnected = false; } } } }