108 lines
3.4 KiB
Rust
108 lines
3.4 KiB
Rust
#![warn(clippy::all, clippy::pedantic)]
|
|
|
|
use nautilus_common::udp::tokio::AsyncUdpSocketExt;
|
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
|
mod command;
|
|
mod telemetry;
|
|
|
|
use crate::command::CommandHandler;
|
|
use crate::telemetry::TelemetryHandler;
|
|
use anyhow::{Result, anyhow, bail};
|
|
use api::client::Client;
|
|
use futures::executor::block_on;
|
|
use futures::future::{Either, select};
|
|
use log::{info, warn};
|
|
use nautilus_common::add_ctrlc_handler_cancel;
|
|
use std::sync::Arc;
|
|
use std::thread::{Builder, scope};
|
|
use tokio::net::UdpSocket;
|
|
use tokio::pin;
|
|
use tokio::sync::RwLock;
|
|
use tokio_util::sync::CancellationToken;
|
|
|
|
/// Run the Ground Software
|
|
///
|
|
/// # Errors
|
|
/// If any unhandled error occurred in the Ground Software
|
|
pub fn run() -> Result<()> {
|
|
info!(
|
|
"Project Nautilus Ground Software {}",
|
|
env!("CARGO_PKG_VERSION")
|
|
);
|
|
|
|
let udp_shutdown = CancellationToken::new();
|
|
let cancel = udp_shutdown.child_token();
|
|
add_ctrlc_handler_cancel(cancel.clone())?;
|
|
|
|
let (udp_thread, udp) = {
|
|
let udp_shutdown = udp_shutdown.clone();
|
|
let (udp_tx, udp) = tokio::sync::oneshot::channel();
|
|
|
|
let udp_thread = Builder::new()
|
|
.name("flight-udp-connection-handler".to_string())
|
|
.spawn(move || {
|
|
let runtime = tokio::runtime::Builder::new_current_thread()
|
|
.enable_all()
|
|
.build()?;
|
|
|
|
runtime.block_on(async {
|
|
let udp = UdpSocket::bind("0.0.0.0:14000").await?;
|
|
udp_tx
|
|
.send(udp)
|
|
.map_err(|_| anyhow!("Couldn't complete UDP establish"))?;
|
|
Ok(()) as Result<()>
|
|
})?;
|
|
|
|
runtime.block_on(udp_shutdown.cancelled());
|
|
|
|
Ok(()) as Result<()>
|
|
})?;
|
|
|
|
let f1 = cancel.cancelled();
|
|
pin!(f1);
|
|
let udp = block_on(async {
|
|
let f2 = udp;
|
|
select(f1, f2).await
|
|
});
|
|
let udp = match udp {
|
|
Either::Left(_) => bail!("Cancelled Before UDP established"),
|
|
Either::Right(x) => x.0?,
|
|
};
|
|
(udp_thread, udp)
|
|
};
|
|
|
|
let flight_addr = RwLock::new(SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0));
|
|
let lock = flight_addr.blocking_write();
|
|
|
|
scope(|scope| {
|
|
let client = Arc::new(Client::connect("ws://localhost:8080/backend")?);
|
|
|
|
let tlm = TelemetryHandler::new(client.clone(), &flight_addr, lock, &udp, cancel.clone());
|
|
let cmd = CommandHandler::new(client, &flight_addr, &udp, cancel.clone());
|
|
|
|
let tlm_thread = Builder::new()
|
|
.name("telemetry-handler".to_string())
|
|
.spawn_scoped(scope, move || tlm.run())?; // move to take ownership (drop when exited)
|
|
|
|
let cmd_thread = Builder::new()
|
|
.name("command-handler".to_string())
|
|
.spawn_scoped(scope, move || cmd.run())?;
|
|
|
|
// Force the thread panics into anyhow errors
|
|
tlm_thread.join().map_err(|e| anyhow!("{e:?}"))??;
|
|
cmd_thread.join().map_err(|e| anyhow!("{e:?}"))??;
|
|
Ok(()) as Result<()>
|
|
})?;
|
|
|
|
info!("Sending Shutdown Command");
|
|
|
|
if let Ok(flight_addr) = flight_addr.try_read() {
|
|
block_on(async { udp.send_command("/shutdown", &(), *flight_addr).await })?;
|
|
}
|
|
|
|
udp_shutdown.cancel();
|
|
udp_thread.join().map_err(|e| anyhow!("{e:?}"))??;
|
|
|
|
Ok(())
|
|
}
|