1use crate::sauron;
4use crate::{database::insert_image, utils::get_env};
5
6use a8mini_camera_rs::A8Mini;
7use anyhow::anyhow;
8use chrono::Utc;
9use constants::CAMERA_HEARTBEAT_FAILS_PER_RESTART;
10use sqlx::postgres::PgPoolOptions;
11use std::time::Duration;
12use tokio::time;
13use tracing::{debug, error, info};
14
15use peripherals::peripherals::DroneCamera;
16
17pub async fn start_camera(sauron: sauron::SauronHandle) {
19 let mut fail_counter: u32 = 0;
21 tokio::spawn(async move {
22 loop {
23 match run_camera(sauron.clone()).await {
24 Ok(_) => break,
25 Err(e) => {
26 fail_counter += 1;
27 error!("Camera thread failed ({} times): {}", fail_counter, e);
28 info!("Restarting camera thread in 2500 miliseconds...");
29 tokio::time::sleep(Duration::from_millis(2500)).await;
30 }
31 }
32 }
33 });
34}
35
36async fn run_camera(sauron: sauron::SauronHandle) -> anyhow::Result<()> {
38 info!("Started Camera Thread");
39 let database_url = get_env(
40 "DATABASE_URL",
41 "postgresql://user:password@localhost:5432/local".to_string(),
42 );
43
44 let pool = PgPoolOptions::new()
45 .max_connections(constants::CONNECTIONS_PER_DATABASE_POOL)
46 .connect(&database_url)
47 .await?;
48
49 let camera = A8Mini::connect().await.unwrap();
50
51 info!("Connected a8mini, verifying connection with heartbeat message...");
52 let mut heartbeat_fail_counter: u64 = 0;
53 while camera.check_heartbeat().await.is_err() {
54 heartbeat_fail_counter += 1;
55 error!(
56 "Camera heartbeat failed {} times. Retrying...",
57 heartbeat_fail_counter
58 );
59 if heartbeat_fail_counter >= CAMERA_HEARTBEAT_FAILS_PER_RESTART {
60 return Err(anyhow!(
61 "Camera heartbeat fail counter exceeded limit ({}).",
62 heartbeat_fail_counter
63 ));
64 }
65 }
66
67 let mut camera_timer = time::interval(Duration::from_millis(constants::CAMERA_INTERVAL_MS));
68
69 loop {
70 camera_timer.tick().await;
72
73 let time_before = Utc::now();
74
75 camera.take_photo().await?;
76
77 let time_after = Utc::now();
78 let average_time = (time_before + (time_after - time_before) / 2).timestamp_millis();
79
80 let photo_path = camera
82 .save_latest_photo("./untagged_image_folder".to_string()) .await?;
84 debug!("Saved photo to {}", photo_path);
85
86 let insert_image_promise = insert_image(&pool, &photo_path, &average_time);
88
89 sauron.send_photo(photo_path.clone(), average_time).await;
90 insert_image_promise.await?;
91 }
92}