1use super::mode_context::ModeContext;
2use super::signal::{
3 PeriodicImagingEndSignal,
4 TaskEndSignal::{self, Join, Timestamp},
5};
6use crate::flight_control::{FlightComputer, FlightState, orbit::IndexedOrbitPosition};
7use crate::imaging::CameraAngle;
8use crate::objective::BeaconControllerState;
9use crate::scheduling::{EndCondition, TaskController, task::SwitchStateTask};
10use crate::{DT_0_STD, error, fatal, info, log};
11use chrono::{DateTime, TimeDelta, Utc};
12use std::{future::Future, pin::Pin, sync::Arc};
13use strum_macros::Display;
14use tokio::{sync::oneshot, task::JoinHandle, time::Instant};
15use tokio_util::sync::CancellationToken;
16
17#[derive(Display, Clone, Copy)]
20pub(super) enum BaseMode {
21 MappingMode,
23 BeaconObjectiveScanningMode,
25}
26
27impl BaseMode {
28 const DEF_MAPPING_ANGLE: CameraAngle = CameraAngle::Narrow;
30
31 #[allow(clippy::cast_possible_wrap)]
42 async fn exec_map(context: Arc<ModeContext>, end: TaskEndSignal, c_tok: CancellationToken) {
43 let end_t = {
44 match end {
45 Timestamp(dt) => dt,
46 Join(_) => Utc::now() + TimeDelta::seconds(10000),
47 }
48 };
49 let o_ch_clone = context.o_ch_clone().await;
50 let acq_phase = {
51 let f_cont_lock = Arc::clone(&context.k().f_cont());
52 let (tx, rx) = oneshot::channel();
53 let i_start = o_ch_clone.i_entry().new_from_pos(f_cont_lock.read().await.current_pos());
54 let k_clone = Arc::clone(context.k());
55 let img_dt = o_ch_clone.img_dt();
56 FlightComputer::set_angle_wait(Arc::clone(&f_cont_lock), Self::DEF_MAPPING_ANGLE).await;
57 let handle = tokio::spawn(async move {
58 k_clone
59 .c_cont()
60 .execute_acquisition_cycle(
61 f_cont_lock,
62 k_clone.con(),
63 (end_t, rx),
64 img_dt,
65 i_start.index(),
66 )
67 .await
68 });
69 (handle, tx)
70 };
71
72 let ranges = {
73 if let Join(join_handle) = end {
74 tokio::pin!(join_handle);
75 tokio::select! {
76 () = c_tok.cancelled() => {
77 let sig = PeriodicImagingEndSignal::KillNow;
78 acq_phase.1.send(sig).unwrap_or_else(|_|fatal!("Receiver hung up!"));
79 join_handle.abort();
80 acq_phase.0.await.ok().unwrap_or(vec![(0, 0)])
81 },
82 _ = &mut join_handle => {
83 let sig = PeriodicImagingEndSignal::KillLastImage;
84 acq_phase.1.send(sig).unwrap_or_else(|_|fatal!("Receiver hung up!"));
85 acq_phase.0.await.ok().unwrap_or(vec![(0, 0)])
86 }
87 }
88 } else {
89 let img_fut = acq_phase.0;
90 tokio::pin!(img_fut);
91 tokio::select! {
92 () = c_tok.cancelled() => {
93 let sig = PeriodicImagingEndSignal::KillNow;
94 acq_phase.1.send(sig).expect("[FATAL] Receiver hung up!");
95 img_fut.await.ok().unwrap_or(vec![(0, 0)])
96 }
97 res = &mut img_fut => {
98 res.ok().unwrap_or(vec![(0, 0)])
99 }
100 }
101 }
102 };
103 let fixed_ranges =
104 IndexedOrbitPosition::map_ranges(&ranges, o_ch_clone.i_entry().period() as isize);
105 let and = if let Some(r) = ranges.get(1) {
106 format!(" and {} - {}", r.0, r.1)
107 } else {
108 String::new()
109 };
110 log!("Marking done: {} - {}{and}", ranges[0].0, ranges[0].1);
111 let k_loc = Arc::clone(context.k());
112 let c_orbit_lock = k_loc.c_orbit();
113 let mut c_orbit = c_orbit_lock.write().await;
114 for (start, end) in &fixed_ranges {
115 if start != end {
116 c_orbit.mark_done(*start, *end);
117 }
118 }
119 log!(
120 "Current discrete Orbit Coverage is {}%.",
121 c_orbit.get_coverage() * 100
122 );
123 c_orbit.try_export_default();
124 }
125
126 async fn exec_comms(context: Arc<ModeContext>, end: TaskEndSignal, c_tok: CancellationToken) {
136 let mut event_rx = context.super_v().subscribe_event_hub();
137
138 let mut fut: Pin<Box<dyn Future<Output = ()> + Send>> = match end {
139 Timestamp(t) => {
140 let due_secs = (t - Utc::now()).to_std().unwrap_or(DT_0_STD);
141 Box::pin(tokio::time::sleep_until(Instant::now() + due_secs))
142 }
143 Join(join_handle) => Box::pin(async { join_handle.await.ok().unwrap() }),
144 };
145
146 let start = Utc::now();
147 info!("Starting Comms Listener.");
148 loop {
149 tokio::select! {
150 Ok(msg) = event_rx.recv() => {
152 let f_cont = context.k().f_cont();
153 context.beac_cont().handle_poss_bo_ping(msg, f_cont).await;
154 }
155 () = &mut fut => {
157 log!("Comms Deadline reached after {}s. Stopping listener.",
158 (Utc::now() - start).num_seconds());
159 break;
160 },
161 () = c_tok.cancelled() => {
163 log!("Comms Listener cancelled. Stopping listener.");
164 break;
165 }
166 }
167 }
168 }
169
170 pub(super) async fn handle_sched_preconditions(
178 &self,
179 context: Arc<ModeContext>,
180 ) -> DateTime<Utc> {
181 match self {
182 BaseMode::MappingMode => FlightComputer::escape_if_comms(context.k().f_cont()).await,
183 BaseMode::BeaconObjectiveScanningMode => {
184 FlightComputer::get_to_comms(context.k().f_cont()).await
185 }
186 }
187 }
188
189 #[must_use]
206 pub(super) async fn get_schedule_handle(
207 &self,
208 context: Arc<ModeContext>,
209 c_tok: CancellationToken,
210 comms_end: DateTime<Utc>,
211 end: Option<EndCondition>,
212 ) -> JoinHandle<()> {
213 let k = Arc::clone(context.k());
214 let o_ch = context.o_ch_clone().await;
215 let j_handle = match self {
216 BaseMode::MappingMode => tokio::spawn(TaskController::sched_opt_orbit(
217 k.t_cont(),
218 k.c_orbit(),
219 k.f_cont(),
220 o_ch.i_entry(),
221 end,
222 )),
223 BaseMode::BeaconObjectiveScanningMode => {
224 let last_obj_end =
225 context.beac_cont().last_active_beac_end().await.unwrap_or(Utc::now());
226 tokio::spawn(TaskController::sched_opt_orbit_w_comms(
227 k.t_cont(),
228 k.c_orbit(),
229 k.f_cont(),
230 o_ch.i_entry(),
231 last_obj_end,
232 comms_end,
233 end,
234 ))
235 }
236 };
237 let state = context.k().f_cont().read().await.state();
238 if state == FlightState::Acquisition {
239 tokio::spawn(BaseMode::exec_map(context, Join(j_handle), c_tok))
240 } else if state == FlightState::Comms {
241 match self {
242 BaseMode::MappingMode => fatal!("Illegal state ({state})!"),
243 BaseMode::BeaconObjectiveScanningMode => {
244 tokio::spawn(BaseMode::exec_comms(context, Join(j_handle), c_tok))
245 }
246 }
247 } else {
248 j_handle
249 }
250 }
251
252 #[must_use]
269 pub(super) async fn get_wait(
270 &self,
271 context: Arc<ModeContext>,
272 due: DateTime<Utc>,
273 c_tok: CancellationToken,
274 ) -> JoinHandle<()> {
275 let (current_state, _) = {
276 let f_cont = context.k().f_cont();
277 let lock = f_cont.read().await;
278 (lock.state(), lock.client())
279 };
280 let c_tok_clone = c_tok.clone();
281 let def = Box::pin(async move {
282 let sleep = (due - Utc::now()).to_std().unwrap_or(DT_0_STD);
283 tokio::time::timeout(sleep, c_tok_clone.cancelled()).await.ok().unwrap_or(());
284 });
285 let task_fut: Pin<Box<dyn Future<Output = _> + Send>> = match current_state {
286 FlightState::Charge => def,
287 FlightState::Acquisition => Box::pin(async move {
288 Self::exec_map(context, Timestamp(due), c_tok).await;
289 }),
290 FlightState::Comms => {
291 if let Self::BeaconObjectiveScanningMode = self {
292 Box::pin(async move {
293 Self::exec_comms(context, Timestamp(due), c_tok).await;
294 })
295 } else {
296 error!("Not in Beacon Objective Scanning Mode. Waiting for Comms to end.");
297 def
298 }
299 }
300 _ => {
301 fatal!("Illegal state ({current_state})!")
302 }
303 };
304 tokio::spawn(task_fut)
305 }
306
307 pub(super) async fn get_task(&self, context: Arc<ModeContext>, task: SwitchStateTask) {
315 let f_cont = context.k().f_cont();
316 match task.target_state() {
317 FlightState::Acquisition => {
318 FlightComputer::set_state_wait(f_cont, FlightState::Acquisition).await;
319 }
320 FlightState::Charge => {
321 let task_handle = async {
322 FlightComputer::set_state_wait(f_cont, FlightState::Charge).await;
323 };
324 let k_clone = Arc::clone(context.k());
325 let export_handle = tokio::spawn(async move {
326 let c_cont = k_clone.c_cont();
327 c_cont
328 .export_full_snapshot()
329 .await
330 .unwrap_or_else(|_| fatal!("Export failed!"));
331 c_cont.create_thumb_snapshot().await.unwrap_or_else(|e| {
332 error!("Error exporting thumb snapshot: {e}.");
333 });
334 });
335 task_handle.await;
336 if export_handle.is_finished() {
337 export_handle.await.unwrap();
338 } else {
339 error!("Couldnt finish Map export!");
340 export_handle.abort();
341 }
342 }
343 FlightState::Comms => match self {
344 BaseMode::MappingMode => {
345 fatal!("Illegal target state!")
346 }
347 BaseMode::BeaconObjectiveScanningMode => {
348 FlightComputer::set_state_wait(f_cont, FlightState::Comms).await;
349 }
350 },
351 _ => fatal!("Illegal target state!"),
352 }
353 }
354
355 pub(super) fn get_rel_bo_event(self) -> BeaconControllerState {
359 match self {
360 BaseMode::MappingMode => BeaconControllerState::ActiveBeacons,
361 BaseMode::BeaconObjectiveScanningMode => BeaconControllerState::NoActiveBeacons,
362 }
363 }
364
365 pub(super) fn bo_event(self) -> Self {
369 match self {
370 BaseMode::MappingMode => Self::BeaconObjectiveScanningMode,
371 BaseMode::BeaconObjectiveScanningMode => Self::MappingMode,
372 }
373 }
374}