initial comms

This commit is contained in:
2025-10-25 12:23:23 -07:00
parent fd63bdc0c9
commit b067ae5cec
17 changed files with 536 additions and 356 deletions

350
Cargo.lock generated
View File

@@ -17,15 +17,6 @@ version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]]
name = "approx"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.4.0" version = "1.4.0"
@@ -44,12 +35,6 @@ version = "3.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
[[package]]
name = "bytemuck"
version = "1.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.17" version = "1.2.17"
@@ -80,10 +65,38 @@ dependencies = [
"iana-time-zone", "iana-time-zone",
"js-sys", "js-sys",
"num-traits", "num-traits",
"serde",
"wasm-bindgen", "wasm-bindgen",
"windows-link", "windows-link",
] ]
[[package]]
name = "ciborium"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
[[package]]
name = "ciborium-ll"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
dependencies = [
"ciborium-io",
"half",
]
[[package]] [[package]]
name = "colored" name = "colored"
version = "2.2.0" version = "2.2.0"
@@ -121,6 +134,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
[[package]]
name = "crunchy"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]] [[package]]
name = "ctrlc" name = "ctrlc"
version = "3.5.0" version = "3.5.0"
@@ -208,100 +227,15 @@ dependencies = [
] ]
[[package]] [[package]]
name = "glam" name = "half"
version = "0.14.0" version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "333928d5eb103c5d4050533cec0384302db6be8ef7d3cebd30ec6a35350353da" checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
dependencies = [
[[package]] "cfg-if",
name = "glam" "crunchy",
version = "0.15.2" "zerocopy",
source = "registry+https://github.com/rust-lang/crates.io-index" ]
checksum = "3abb554f8ee44336b72d522e0a7fe86a29e09f839a36022fa869a7dfe941a54b"
[[package]]
name = "glam"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4126c0479ccf7e8664c36a2d719f5f2c140fbb4f9090008098d2c291fa5b3f16"
[[package]]
name = "glam"
version = "0.17.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01732b97afd8508eee3333a541b9f7610f454bb818669e66e90f5f57c93a776"
[[package]]
name = "glam"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525a3e490ba77b8e326fb67d4b44b4bd2f920f44d4cc73ccec50adc68e3bee34"
[[package]]
name = "glam"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b8509e6791516e81c1a630d0bd7fbac36d2fa8712a9da8662e716b52d5051ca"
[[package]]
name = "glam"
version = "0.20.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f43e957e744be03f5801a55472f593d43fabdebf25a4585db250f04d86b1675f"
[[package]]
name = "glam"
version = "0.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "518faa5064866338b013ff9b2350dc318e14cc4fcd6cb8206d7e7c9886c98815"
[[package]]
name = "glam"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12f597d56c1bd55a811a1be189459e8fad2bbc272616375602443bdfb37fa774"
[[package]]
name = "glam"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e4afd9ad95555081e109fe1d21f2a30c691b5f0919c67dfa690a2e1eb6bd51c"
[[package]]
name = "glam"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5418c17512bdf42730f9032c74e1ae39afc408745ebb2acf72fbc4691c17945"
[[package]]
name = "glam"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3"
[[package]]
name = "glam"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9"
[[package]]
name = "glam"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "779ae4bf7e8421cf91c0b3b64e7e8b40b862fba4d393f59150042de7c4965a94"
[[package]]
name = "glam"
version = "0.29.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee"
[[package]]
name = "glam"
version = "0.30.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e12d847aeb25f41be4c0ec9587d624e9cd631bc007a8fd7ce3f5851e064c6460"
[[package]] [[package]]
name = "hex" name = "hex"
@@ -361,62 +295,19 @@ version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]]
name = "matrixmultiply"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08"
dependencies = [
"autocfg",
"rawpointer",
]
[[package]]
name = "nalgebra"
version = "0.34.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4d5b3eff5cd580f93da45e64715e8c20a3996342f1e466599cf7a267a0c2f5f"
dependencies = [
"approx",
"glam 0.14.0",
"glam 0.15.2",
"glam 0.16.0",
"glam 0.17.3",
"glam 0.18.0",
"glam 0.19.0",
"glam 0.20.5",
"glam 0.21.3",
"glam 0.22.0",
"glam 0.23.0",
"glam 0.24.2",
"glam 0.25.0",
"glam 0.27.0",
"glam 0.28.0",
"glam 0.29.3",
"glam 0.30.8",
"matrixmultiply",
"nalgebra-macros",
"num-complex 0.4.6",
"num-rational 0.4.2",
"num-traits",
"simba",
"typenum",
]
[[package]]
name = "nalgebra-macros"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "973e7178a678cfd059ccec50887658d482ce16b0aa9da3888ddeab5cd5eb4889"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "nautilus_common" name = "nautilus_common"
version = "0.1.0" version = "0.1.0"
dependencies = [
"anyhow",
"chrono",
"ciborium",
"ctrlc",
"fern",
"log",
"serde",
"thiserror",
]
[[package]] [[package]]
name = "nautilus_flight" name = "nautilus_flight"
@@ -424,23 +315,26 @@ version = "0.0.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
"ciborium",
"crc", "crc",
"ctrlc",
"embedded-hal 1.0.0", "embedded-hal 1.0.0",
"embedded-hal-bus", "embedded-hal-bus",
"embedded-hal-mock", "embedded-hal-mock",
"fern",
"hex", "hex",
"log", "log",
"nalgebra", "nautilus_common",
"num-traits",
"rpi-pal", "rpi-pal",
"thiserror",
] ]
[[package]] [[package]]
name = "nautilus_ground" name = "nautilus_ground"
version = "0.1.0" version = "0.1.0"
dependencies = [
"anyhow",
"ciborium",
"log",
"nautilus_common",
]
[[package]] [[package]]
name = "nb" name = "nb"
@@ -475,20 +369,10 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f" checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f"
dependencies = [ dependencies = [
"num-complex 0.3.1", "num-complex",
"num-integer", "num-integer",
"num-iter", "num-iter",
"num-rational 0.3.2", "num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits", "num-traits",
] ]
@@ -501,15 +385,6 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "num-complex"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.46" version = "0.1.46"
@@ -541,17 +416,6 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "num-rational"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"
@@ -567,12 +431,6 @@ version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.94" version = "1.0.94"
@@ -591,12 +449,6 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rawpointer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
[[package]] [[package]]
name = "rpi-pal" name = "rpi-pal"
version = "0.22.2" version = "0.22.2"
@@ -619,12 +471,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
[[package]] [[package]]
name = "safe_arch" name = "serde"
version = "0.7.4" version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [ dependencies = [
"bytemuck", "serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
] ]
[[package]] [[package]]
@@ -633,19 +506,6 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "simba"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3a386a501cd104797982c15ae17aafe8b9261315b5d07e3ec803f2ea26be0fa"
dependencies = [
"approx",
"num-complex 0.4.6",
"num-traits",
"paste",
"wide",
]
[[package]] [[package]]
name = "spin_sleep" name = "spin_sleep"
version = "1.3.1" version = "1.3.1"
@@ -686,12 +546,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "typenum"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.18" version = "1.0.18"
@@ -762,16 +616,6 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "wide"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41b5576b9a81633f3e8df296ce0063042a73507636cbe956c61133dd7034ab22"
dependencies = [
"bytemuck",
"safe_arch",
]
[[package]] [[package]]
name = "windows-core" name = "windows-core"
version = "0.52.0" version = "0.52.0"
@@ -868,3 +712,23 @@ name = "windows_x86_64_msvc"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "zerocopy"
version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View File

@@ -4,3 +4,11 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
anyhow = "1.0.100"
fern = { version = "0.7.1", features = ["colored"] }
log = { version = "0.4.28", features = ["max_level_trace", "release_max_level_debug"] }
chrono = { version = "0.4.42", features = ["serde"] }
ctrlc = "3.5.0"
serde = { version = "1.0.228", features = ["derive"], default-features = false }
ciborium = { version = "0.2.2" }
thiserror = "2.0.17"

View File

@@ -0,0 +1,6 @@
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Command {
Shutdown,
}

View File

@@ -1,14 +1,16 @@
pub fn add(left: u64, right: u64) -> u64 { use log::info;
left + right use std::sync::atomic::{AtomicBool, Ordering};
} use std::sync::Arc;
#[cfg(test)] pub mod logger;
mod tests { pub mod command;
use super::*; pub mod telemetry;
pub mod udp;
#[test] pub fn add_ctrlc_handler(flag: Arc<AtomicBool>) -> anyhow::Result<()> {
fn it_works() { ctrlc::set_handler(move || {
let result = add(2, 2); info!("Shutdown Requested");
assert_eq!(result, 4); flag.store(false, Ordering::Relaxed);
} })?;
Ok(())
} }

View File

@@ -5,13 +5,13 @@ use std::fs::create_dir_all;
use std::str::FromStr; use std::str::FromStr;
use std::{env, thread}; use std::{env, thread};
pub fn setup_logger() -> Result<()> { pub fn setup_logger(package_name: &'static str) -> Result<()> {
let log_file = env::var("LOG_FILE").or_else(|_| { let log_file = env::var("LOG_FILE").or_else(|_| {
create_dir_all("logs/")?; create_dir_all("logs/")?;
anyhow::Ok(format!( anyhow::Ok(format!(
"logs/{}_{}.log", "logs/{}_{}.log",
env!("CARGO_PKG_NAME"), package_name,
chrono::Local::now().format("%Y%m%d_%H%M%S") chrono::Local::now().format("%Y%m%d_%H%M%S")
)) ))
})?; })?;

View File

@@ -0,0 +1,8 @@
use chrono::serde::ts_nanoseconds;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Telemetry {
Timestamp(#[serde(with = "ts_nanoseconds")] DateTime<Utc>)
}

80
common/src/udp.rs Normal file
View File

@@ -0,0 +1,80 @@
use crate::udp::UdpSendCborError::LengthMismatch;
use log::error;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::io::{Cursor, ErrorKind};
use std::net::{SocketAddr, ToSocketAddrs, UdpSocket};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum UdpRecvCborError {
#[error("IO Error")]
Io(#[from] std::io::Error),
#[error("Deserialization Error")]
Deserialization(#[from] ciborium::de::Error<std::io::Error>),
#[error("No Data")]
NoData,
}
#[derive(Error, Debug)]
pub enum UdpSendCborError {
#[error("IO Error")]
Io(#[from] std::io::Error),
#[error("Serialization Error")]
Serialization(#[from] ciborium::ser::Error<std::io::Error>),
#[error("Length Mismatch")]
LengthMismatch {
expected: usize,
actual: usize,
},
}
pub trait UdpSocketExt {
fn recv_cbor<T: DeserializeOwned, const N: usize>(&self, buffer: &mut Cursor<[u8; N]>) -> Result<(T, SocketAddr), UdpRecvCborError>;
fn send_cbor<T: Serialize + ?Sized, A: ToSocketAddrs, const N: usize>(&self, data: &T, buffer: &mut Cursor<[u8; N]>, addr: A) -> Result<(), UdpSendCborError>;
}
impl UdpSocketExt for UdpSocket {
fn recv_cbor<T: DeserializeOwned, const N: usize>(&self, buffer: &mut Cursor<[u8; N]>) -> Result<(T, SocketAddr), UdpRecvCborError> {
buffer.set_position(0);
match self.recv_from(buffer.get_mut()) {
Ok((size, addr)) => {
match ciborium::from_reader::<T, _>(&buffer.get_ref()[..size]) {
Ok(res) => Ok((res, addr)),
Err(err) => Err(err.into()),
}
}
Err(err) => {
match err.kind() {
ErrorKind::WouldBlock | ErrorKind::TimedOut => {
Err(UdpRecvCborError::NoData)
}
_ => Err(err.into())
}
}
}
}
fn send_cbor<T: Serialize + ?Sized, A: ToSocketAddrs, const N: usize>(&self, data: &T, mut buffer: &mut Cursor<[u8; N]>, addr: A) -> Result<(), UdpSendCborError> {
buffer.set_position(0);
match ciborium::into_writer(data, &mut buffer) {
Ok(_) => match self.send_to(&buffer.get_ref()[..buffer.position() as usize], addr) {
Ok(size) => {
if buffer.position() as usize != size {
return Err(LengthMismatch {
expected: buffer.position() as usize,
actual: size,
});
}
Ok(())
}
Err(e) => {
Err(e.into())
}
}
Err(e) => {
Err(e.into())
}
}
}
}

View File

@@ -5,19 +5,16 @@ edition = "2024"
[dependencies] [dependencies]
anyhow = "1.0.100" anyhow = "1.0.100"
fern = { version = "0.7.1", features = ["colored"] }
log = { version = "0.4.28", features = ["max_level_trace", "release_max_level_debug"] } log = { version = "0.4.28", features = ["max_level_trace", "release_max_level_debug"] }
chrono = "0.4.42" chrono = "0.4.42"
embedded-hal = "1.0.0" embedded-hal = "1.0.0"
embedded-hal-bus = { version = "0.3.0", features = ["std"] } embedded-hal-bus = { version = "0.3.0", features = ["std"] }
embedded-hal-mock = { version = "0.11.1", optional = true } embedded-hal-mock = { version = "0.11.1", optional = true }
rpi-pal = { version = "0.22.2", features = ["hal"], optional = true } rpi-pal = { version = "0.22.2", features = ["hal"], optional = true }
nalgebra = "0.34.1"
hex = "0.4.3" hex = "0.4.3"
thiserror = "2.0.17"
num-traits = "0.2.19"
crc = "3.3.0" crc = "3.3.0"
ctrlc = { version = "3.5" } nautilus_common = { path = "../common" }
ciborium = { version = "0.2.2" }
[dev-dependencies] [dev-dependencies]
embedded-hal-mock = { version = "0.11.1" } embedded-hal-mock = { version = "0.11.1" }

64
flight/src/comms/mod.rs Normal file
View File

@@ -0,0 +1,64 @@
use crate::scheduler::CyclicTask;
use anyhow::Result;
use log::{error, trace};
use nautilus_common::command::Command;
use nautilus_common::telemetry::Telemetry;
use nautilus_common::udp::{UdpRecvCborError, UdpSocketExt};
use std::fmt::Debug;
use std::io::Cursor;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs, UdpSocket};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::Receiver;
use std::sync::Arc;
use std::time::Instant;
#[derive(Debug)]
pub struct CommsTask<A: ToSocketAddrs> {
udp: UdpSocket,
ground_address: A,
running: Arc<AtomicBool>,
}
impl<A: ToSocketAddrs + Debug> CommsTask<A> {
pub fn new(
local_port: u16,
ground_address: A,
running: Arc<AtomicBool>,
) -> Result<Self> {
trace!("CommsTask::new(local_port: {local_port}, ground_address: {ground_address:?})");
let bind_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), local_port);
// let bind_addr = SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), local_port);
let udp = UdpSocket::bind(bind_addr)?;
udp.set_nonblocking(true)?;
Ok(Self {
udp,
ground_address,
running,
})
}
}
impl<A: ToSocketAddrs> CyclicTask for CommsTask<A> {
type Message = ();
fn step(&mut self, _receiver: &Receiver<Self::Message>, _step_time: Instant) {
let mut buffer = Cursor::new([0u8; 512]);
match self.udp.recv_cbor::<Command, _>(&mut buffer) {
Ok((cmd, _)) => {
match cmd {
Command::Shutdown => self.running.store(false, Ordering::Relaxed),
}
}
Err(UdpRecvCborError::NoData) => {}
Err(err) => {
error!("Rx error: {err}");
}
}
let tlm = Telemetry::Timestamp(chrono::Utc::now());
if let Err(err) = self.udp.send_cbor(&tlm, &mut buffer, &self.ground_address) {
error!("Tx Error: {err}");
}
}
}

View File

@@ -1,14 +1,11 @@
use crate::hardware::mcp23017::Mcp23017; use crate::hardware::mcp23017::Mcp23017;
use crate::hardware::pin::PinDevice; use crate::hardware::pin::PinDevice;
use anyhow::Result; use crate::scheduler::{CyclicTask, TaskHandle};
use embedded_hal::digital::PinState; use embedded_hal::digital::PinState;
use log::trace; use log::trace;
use std::fmt::Debug; use std::fmt::{Debug, Formatter};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::Receiver;
use std::sync::mpsc::{channel, Sender}; use std::time::Instant;
use std::thread;
use std::thread::{sleep, Scope, ScopedJoinHandle};
use std::time::{Duration, Instant};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Mcp23017Message { pub enum Mcp23017Message {
@@ -20,14 +17,7 @@ pub enum Mcp23017Message {
}, },
} }
#[derive(Clone, Debug)] impl PinDevice for TaskHandle<Mcp23017Message> {
pub struct Mcp23017Task {
#[allow(dead_code)]
name: String,
sender: Sender<Mcp23017Message>,
}
impl PinDevice for Mcp23017Task {
fn set_pin(&self, pin: u8, value: PinState, valid_until: Instant, priority: u8) { fn set_pin(&self, pin: u8, value: PinState, valid_until: Instant, priority: u8) {
trace!("Mcp23017Task::set_pin(self: {self:?}, pin: {pin}, value: {value:?})"); trace!("Mcp23017Task::set_pin(self: {self:?}, pin: {pin}, value: {value:?})");
// This can only fail if the other end is disconnected - which we intentionally want to // This can only fail if the other end is disconnected - which we intentionally want to
@@ -41,6 +31,17 @@ impl PinDevice for Mcp23017Task {
} }
} }
pub struct Mcp23017Task<M: Mcp23017> {
mcp23017: M,
pins: AllPins,
}
impl<M: Mcp23017 + Debug> Debug for Mcp23017Task<M> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Mcp23017Task {{ mcp23017: {:?} }}", self.mcp23017)
}
}
struct AllPins { struct AllPins {
pins: [PinData; 16], pins: [PinData; 16],
} }
@@ -137,67 +138,48 @@ impl PinData {
} }
} }
impl Mcp23017Task { impl<M: Mcp23017 + Debug> Mcp23017Task<M> {
pub fn start<'a, M: Mcp23017 + Send + Debug>( pub fn new(mcp23017: M) -> Self {
scope: &'a Scope<'a, '_>, trace!("Mcp23017Task::new(mcp23017: {mcp23017:?})");
running: &'a AtomicBool, Self {
mut mcp23017: M, mcp23017,
name: String, pins: AllPins::new(),
frequency: u64, }
) -> Result<(ScopedJoinHandle<'a, ()>, Self)> where }
M: 'a, }
{
trace!("Mcp23017Task::start(scope, running, mcp23017: {mcp23017:?}, name: {name}, frequency: {frequency})"); impl<M: Mcp23017> CyclicTask for Mcp23017Task<M> {
type Message = Mcp23017Message;
let (sender, receiver) = channel::<Mcp23017Message>();
let period = Duration::from_nanos(1_000_000_000 / frequency); fn step(
&mut self,
let handle = thread::Builder::new() receiver: &Receiver<Self::Message>,
.name(name.clone()) step_time: Instant,
.spawn_scoped(scope, move || { ) {
let mut pins = AllPins::new(); let mut changed = false;
let mut cycle_start_time = Instant::now();
while running.load(Ordering::Relaxed) { while let Ok(recv) = receiver.try_recv() {
let mut changed = false; match recv {
Mcp23017Message::SetPin { pin, value, valid_until, priority } => {
while let Ok(recv) = receiver.try_recv() { if (0u8..16u8).contains(&pin) {
match recv { self.pins.pins[pin as usize].set(value, valid_until, priority);
Mcp23017Message::SetPin { pin, value, valid_until, priority } => { }
if (0u8..16u8).contains(&pin) { }
pins.pins[pin as usize].set(value, valid_until, priority); }
} }
}
} for pin in 0u8..16u8 {
} // This shouldn't be able to fail
// TODO: handle error case
for pin in 0u8..16u8 { let state = self.pins.pins[pin as usize].get(step_time);
// This shouldn't be able to fail if self.pins.pins[pin as usize].changed {
// TODO: handle error case self.pins.pins[pin as usize].changed = false;
let state = pins.pins[pin as usize].get(cycle_start_time); let _ = self.mcp23017.set_pin(pin, state);
if pins.pins[pin as usize].changed { changed = true;
pins.pins[pin as usize].changed = false; }
let _ = mcp23017.set_pin(pin, state); }
changed = true; if changed {
} let _ = self.mcp23017.flush();
} }
if changed {
let _ = mcp23017.flush();
}
cycle_start_time += period;
let sleep_duration = cycle_start_time - Instant::now();
if sleep_duration > Duration::ZERO {
sleep(sleep_duration);
}
}
})?;
Ok((
handle,
Self {
name,
sender,
}
))
} }
} }

View File

@@ -44,6 +44,7 @@ impl RaspiHardware {
Mode::Mode0, Mode::Mode0,
)?), 3.3f64).into(), )?), 3.3f64).into(),
mct8316a: SimMct8316a::new().into(), mct8316a: SimMct8316a::new().into(),
// mct8316a: SimMct8316a::new().into(),
}) })
} }
} }

View File

@@ -1,30 +1,23 @@
use crate::comms::CommsTask;
use crate::hardware::channelization::{LED_A, LED_B}; use crate::hardware::channelization::{LED_A, LED_B};
use crate::hardware::initialize; use crate::hardware::initialize;
use crate::hardware::mcp23017::{Mcp23017, Mcp23017Task}; use crate::hardware::mcp23017::{Mcp23017, Mcp23017Task};
use crate::hardware::mct8316a::Mct8316a; use crate::hardware::mct8316a::Mct8316a;
use crate::hardware::pin::Pin; use crate::hardware::pin::Pin;
use crate::hardware::Hardware; use crate::hardware::Hardware;
use crate::on_drop::on_drop; use crate::scheduler::Scheduler;
use anyhow::Result; use anyhow::Result;
use embedded_hal::digital::PinState; use embedded_hal::digital::PinState;
use embedded_hal::pwm::SetDutyCycle; use embedded_hal::pwm::SetDutyCycle;
use log::{debug, info}; use log::{debug, info};
use nautilus_common::add_ctrlc_handler;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::thread;
use std::thread::sleep; use std::thread::sleep;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
mod hardware; mod hardware;
fn add_ctrlc_handler(flag: Arc<AtomicBool>) -> Result<()> {
ctrlc::set_handler(move || {
info!("Shutdown Requested");
flag.store(false, Ordering::Relaxed);
})?;
Ok(())
}
pub fn run() -> Result<()> { pub fn run() -> Result<()> {
info!( info!(
"Project Nautilus Flight Software {}", "Project Nautilus Flight Software {}",
@@ -49,21 +42,17 @@ pub fn run() -> Result<()> {
mcp23017_b.init()?; mcp23017_b.init()?;
mct8316.init()?; mct8316.init()?;
thread::scope(|scope| { Scheduler::new(running.clone(), |s| {
// This will automatically set running to false when it drops let task_a = s.run_cyclic("mcp23017-a", Mcp23017Task::new(mcp23017_a), 10)?;
// This means that if the main thread exits this scope, we will let task_b = s.run_cyclic("mcp23017-b", Mcp23017Task::new(mcp23017_b), 10)?;
// shut down any side branches
let _shutdown_threads = on_drop(|| running.store(false, Ordering::Relaxed));
let (_, task_a) = Mcp23017Task::start(scope, &running, mcp23017_a, "mcp23017-a".into(), 10)?; let _comms = s.run_cyclic("comms", CommsTask::new(15000, "192.168.50.157:14000", running.clone())?, 1)?;
let (_, task_b) = Mcp23017Task::start(scope, &running, mcp23017_b, "mcp23017-b".into(), 10)?;
let mut led_pin_a = LED_A.new(&task_a, &task_b)?; let mut led_pin_a = LED_A.new(&task_a, &task_b)?;
let mut led_pin_b = LED_B.new(&task_a, &task_b)?; let mut led_pin_b = LED_B.new(&task_a, &task_b)?;
info!("Starting Main Loop"); info!("Starting Main Loop");
for _ in 0..2 { loop {
debug!("A On"); debug!("A On");
led_pin_a.set(PinState::High, Instant::now() + Duration::from_secs(2), 0); led_pin_a.set(PinState::High, Instant::now() + Duration::from_secs(2), 0);
sleep(Duration::from_secs(1)); sleep(Duration::from_secs(1));
@@ -103,3 +92,5 @@ mod test_utils;
mod data; mod data;
mod on_drop; mod on_drop;
mod rcs; mod rcs;
mod comms;
mod scheduler;

View File

@@ -1,11 +1,10 @@
use crate::logger::setup_logger;
use log::error; use log::error;
use nautilus_common::logger::setup_logger;
use nautilus_flight::run; use nautilus_flight::run;
mod logger;
fn main() { fn main() {
setup_logger().expect("Failed to setup logger"); setup_logger(env!("CARGO_PKG_NAME")).expect("Failed to setup logger");
match run() { match run() {
Ok(_) => {} Ok(_) => {}
Err(err) => { Err(err) => {

114
flight/src/scheduler/mod.rs Normal file
View File

@@ -0,0 +1,114 @@
use crate::on_drop::on_drop;
use anyhow::Result;
use log::trace;
use std::fmt::Debug;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::Arc;
use std::thread;
use std::thread::{sleep, Scope};
use std::time::{Duration, Instant};
#[derive(Clone, Debug)]
pub struct TaskHandle<Message> {
#[allow(dead_code)]
pub name: String,
pub sender: Sender<Message>,
}
#[allow(dead_code)]
pub trait Task {
type Message;
fn run(self, receiver: Receiver<Self::Message>, running: Arc<AtomicBool>);
}
pub trait CyclicTask {
type Message;
fn step(&mut self, receiver: &Receiver<Self::Message>, step_time: Instant);
}
pub struct Scheduler<'s, 'e>
{
scope: &'s Scope<'s, 'e>,
running: Arc<AtomicBool>,
}
impl<'s, 'e> Scheduler<'s, 'e> {
pub fn new<'env, F, R>(running: Arc<AtomicBool>, f: F) -> R
where
F: FnOnce(Scheduler<'_, 'env>) -> R,
{
trace!("Scheduler::new(running: {running:?}, f)");
thread::scope(|scope: &Scope| {
// This will automatically set running to false when it drops
// This means that if the function returns any side branches
// checking running will shut down
let _shutdown_threads = on_drop(|| running.store(false, Ordering::Relaxed));
f(Scheduler {
scope,
running: running.clone(),
})
})
}
#[allow(dead_code)]
pub fn run<T>(
&self,
name: impl Into<String>,
task: T,
) -> Result<TaskHandle<T::Message>> where
T: Task + Send + Debug + 's,
T::Message: Send,
{
let name = name.into();
trace!("Scheduler::run(name: {name}, task: {task:?})");
let running = self.running.clone();
let (sender, receiver) = channel::<T::Message>();
let _ = thread::Builder::new()
.name(name.clone())
.spawn_scoped(self.scope, move || {
task.run(receiver, running);
})?;
Ok(TaskHandle {
name,
sender,
})
}
pub fn run_cyclic<T>(
&self,
name: impl Into<String>,
mut task: T,
frequency: u64,
) -> Result<TaskHandle<T::Message>> where
T: CyclicTask + Send + Debug + 's,
T::Message: Send,
{
let name = name.into();
trace!("Scheduler::run_cyclic(name: {name}, task: {task:?}, frequency: {frequency})");
let running = self.running.clone();
let (sender, receiver) = channel::<T::Message>();
let _ = thread::Builder::new()
.name(name.clone())
.spawn_scoped(self.scope, move || {
let period = Duration::from_nanos(1_000_000_000 / frequency);
let mut cycle_start_time = Instant::now();
while running.load(Ordering::Relaxed) {
task.step(&receiver, cycle_start_time);
cycle_start_time += period;
let sleep_duration = cycle_start_time - Instant::now();
if sleep_duration > Duration::ZERO {
sleep(sleep_duration);
}
}
})?;
Ok(TaskHandle {
name,
sender,
})
}
}

View File

@@ -4,3 +4,7 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
anyhow = "1.0.100"
nautilus_common = { path = "../common" }
log = "0.4.28"
ciborium = { version = "0.2.2" }

50
ground/src/lib.rs Normal file
View File

@@ -0,0 +1,50 @@
use anyhow::Result;
use log::{error, info};
use nautilus_common::add_ctrlc_handler;
use nautilus_common::command::Command;
use nautilus_common::telemetry::Telemetry;
use nautilus_common::udp::{UdpRecvCborError, UdpSocketExt};
use std::io::Cursor;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
pub fn run() -> Result<()> {
info!(
"Project Nautilus Ground Software {}",
env!("CARGO_PKG_VERSION")
);
let running = Arc::new(AtomicBool::new(true));
add_ctrlc_handler(running.clone())?;
let mut flight_addr = None;
let bind_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 14000);
let udp = UdpSocket::bind(bind_addr)?;
udp.set_read_timeout(Some(Duration::from_millis(100)))?;
let mut buffer = Cursor::new([0u8; 512]);
while running.load(Ordering::Relaxed) {
match udp.recv_cbor::<Telemetry, _>(&mut buffer) {
Ok((tlm, addr)) => {
flight_addr = Some(addr);
info!("{tlm:?}");
}
Err(UdpRecvCborError::NoData) => {
// NoOp
}
Err(err) => {
error!("Rx error: {err}");
}
}
}
if let Some(flight_addr) = flight_addr {
let cmd = Command::Shutdown;
udp.send_cbor(&cmd, &mut buffer, &flight_addr)?;
}
Ok(())
}

View File

@@ -1,3 +1,13 @@
use log::error;
use nautilus_common::logger::setup_logger;
use nautilus_ground::run;
fn main() { fn main() {
println!("Hello, world!"); setup_logger(env!("CARGO_PKG_NAME")).expect("Failed to setup logger");
match run() {
Ok(_) => {}
Err(err) => {
error!("An unhandled error occurred: {}\n\n{}", err, err.backtrace());
}
}
} }