1#![allow(dead_code, clippy::similar_names)]
2#![warn(clippy::shadow_reuse, clippy::shadow_same, clippy::builtin_type_shadow)]
3mod console_communication;
8mod flight_control;
9mod http_handler;
10mod imaging;
11mod mode_control;
12mod objective;
13mod scheduling;
14mod util;
15
16#[cfg(not(target_env = "msvc"))]
17use tikv_jemallocator::Jemalloc;
18
19#[cfg(not(target_env = "msvc"))]
20#[global_allocator]
21static GLOBAL: Jemalloc = Jemalloc;
22
23use crate::flight_control::{
24 FlightComputer, FlightState,
25 orbit::{ClosedOrbit, OrbitBase, OrbitCharacteristics, OrbitUsabilityError},
26};
27use crate::imaging::CameraAngle;
28use crate::mode_control::{
29 ModeContext, OpExitSignal,
30 mode::{GlobalMode, OrbitReturnMode},
31};
32use crate::objective::BeaconController;
33use crate::util::{Keychain, KeychainWithOrbit};
34use chrono::TimeDelta;
35use fixed::types::I32F32;
36use std::{env, sync::Arc, time::Duration};
37
38const DT_0: TimeDelta = TimeDelta::seconds(0);
40const DT_0_STD: Duration = Duration::from_secs(0);
42
43const STATIC_ORBIT_VEL: (I32F32, I32F32) = (I32F32::lit("6.40"), I32F32::lit("7.40"));
45const ENV_BASE_URL: &str = "DRS_BASE_URL";
47const ENV_SKIP_RESET: &str = "SKIP_RESET";
49
50#[tokio::main(flavor = "multi_thread", worker_threads = 4)]
51async fn main() {
52 let base_url_var = env::var(ENV_BASE_URL);
53 let base_url = base_url_var.as_ref().map_or("http://localhost:33000", |v| v.as_str());
54 let (context, start_mode) = init(base_url).await;
55
56 let mut global_mode = start_mode;
57 loop {
58 let phase = context.o_ch_clone().await.mode_switches();
59 info!("Starting phase {phase} in {}!", global_mode.type_name());
60 match global_mode.init_mode(Arc::clone(&context)).await {
61 OpExitSignal::ReInit(mode) => {
62 global_mode = mode;
63 continue;
64 }
65 OpExitSignal::Continue => (),
66 };
67 match global_mode.exec_task_queue(Arc::clone(&context)).await {
68 OpExitSignal::ReInit(mode) => {
69 global_mode = mode;
70 continue;
71 }
72 OpExitSignal::Continue => {
73 global_mode = global_mode.exit_mode(Arc::clone(&context)).await;
74 continue;
75 }
76 }
77 }
78 }
80
81#[allow(clippy::cast_precision_loss)]
82async fn init(url: &str) -> (Arc<ModeContext>, Box<dyn GlobalMode>) {
83 let (init_k, obj_rx, beac_rx) = Keychain::new(url).await;
84
85 let supervisor_clone = init_k.supervisor();
86 tokio::spawn(async move {
87 supervisor_clone.run_obs_obj_mon().await;
88 });
89
90 if env::var(ENV_SKIP_RESET).is_ok_and(|s| s == "1") {
91 warn!("Skipping reset!");
92 FlightComputer::avoid_transition(&init_k.f_cont()).await;
93 } else {
94 init_k.f_cont().write().await.reset().await;
95 }
96
97 let (beac_cont, beac_state_rx) = {
98 let res = BeaconController::new(beac_rx);
99 (Arc::new(res.0), res.1)
100 };
101
102 let supervisor_clone = init_k.supervisor();
103 tokio::spawn(async move {
104 supervisor_clone.run_announcement_hub().await;
105 });
106 let supervisor_clone = init_k.supervisor();
107 let init_k_c_cont = init_k.c_cont();
108 tokio::spawn(async move {
109 supervisor_clone.run_daily_map_uploader(init_k_c_cont).await;
110 });
111 let beac_cont_clone = Arc::clone(&beac_cont);
112 let handler = Arc::clone(&init_k.client());
113 tokio::spawn(async move {
114 beac_cont_clone.run(handler).await;
115 });
116
117 tokio::time::sleep(Duration::from_secs(5)).await;
118
119 if let Some(c_orbit) = ClosedOrbit::try_from_env() {
120 info!(
121 "Imported existing Orbit with {}% coverage!",
122 c_orbit.get_coverage() * 100
123 );
124 let orbit_char = OrbitCharacteristics::new(&c_orbit, &init_k.f_cont()).await;
125 let supervisor = init_k.supervisor();
126 let mode_context = ModeContext::new(
127 KeychainWithOrbit::new(init_k, c_orbit),
128 orbit_char,
129 obj_rx,
130 beac_state_rx,
131 supervisor,
132 beac_cont,
133 );
134 return (mode_context, Box::new(OrbitReturnMode::new()));
135 }
136
137 let c_orbit: ClosedOrbit = {
138 info!("Creating new Static Orbit!");
139 if init_k.f_cont().read().await.current_battery() < I32F32::lit("50") {
140 FlightComputer::charge_full_wait(&init_k.f_cont()).await;
141 }
142 let f_cont_lock = init_k.f_cont();
143 FlightComputer::set_state_wait(init_k.f_cont(), FlightState::Acquisition).await;
144 FlightComputer::set_vel_wait(init_k.f_cont(), STATIC_ORBIT_VEL.into(), false).await;
145 FlightComputer::set_angle_wait(init_k.f_cont(), CameraAngle::Narrow).await;
146 let f_cont = f_cont_lock.read().await;
147 ClosedOrbit::new(OrbitBase::new(&f_cont), CameraAngle::Wide).unwrap_or_else(|e| match e {
148 OrbitUsabilityError::OrbitNotClosed => fatal!("Static orbit is not closed"),
149 OrbitUsabilityError::OrbitNotEnoughOverlap => {
150 fatal!("Static orbit is not overlapping enough")
151 }
152 })
153 };
154
155 let orbit_char = OrbitCharacteristics::new(&c_orbit, &init_k.f_cont()).await;
156 let supervisor = init_k.supervisor();
157 let mode_context = ModeContext::new(
158 KeychainWithOrbit::new(init_k, c_orbit),
159 orbit_char,
160 obj_rx,
161 beac_state_rx,
162 supervisor,
163 beac_cont,
164 );
165 let mode = OrbitReturnMode::get_next_mode(&mode_context).await;
166 (mode_context, mode)
167}