1#![feature(duration_millis_float)]
10#![allow(unreachable_code)]
11
12#[macro_use]
14extern crate tracing;
15#[macro_use]
16extern crate serde;
17extern crate tokio;
18extern crate minint;
19
20#[cfg(feature = "web")]
22extern crate actix_web;
23#[cfg(feature = "web")]
24extern crate utoipa as utopia;
25
26#[cfg(feature = "capriltags")]
28extern crate apriltag;
29#[cfg(feature = "apriltags")]
30extern crate chalkydri_apriltags;
31
32#[cfg(feature = "python")]
33extern crate pyo3;
34
35#[cfg(feature = "ml")]
36extern crate tfledge;
37
38#[cfg(feature = "web")]
39mod api;
40mod calibration;
41mod cameras;
42mod config;
43mod error;
44mod pose;
45mod subsystems;
46mod utils;
47
48#[cfg(feature = "web")]
49use api::run_api;
50use cameras::CameraManager;
51use config::Config;
52use mimalloc::MiMalloc;
53use minint::NtConn;
54use once_cell::sync::Lazy;
55#[cfg(feature = "rerun")]
56use re_sdk::{MemoryLimit, RecordingStream};
57#[cfg(feature = "rerun_web_viewer")]
58use re_web_viewer_server::WebViewerServerPort;
59#[cfg(feature = "rerun")]
60use re_ws_comms::RerunServerPort;
61use std::{error::Error, net::Ipv4Addr, path::Path, sync::Arc};
62use tokio::sync::{RwLock, mpsc};
63use tracing_subscriber::{EnvFilter, Layer, layer::SubscriberExt, util::SubscriberInitExt};
64
65#[global_allocator]
67static GLOBAL: MiMalloc = MiMalloc;
68
69use utils::gen_team_ip;
70
71#[allow(non_upper_case_globals)]
73static Cfg: Lazy<Arc<RwLock<Config>>> = Lazy::new(|| {
74 let mut path = Path::new("/boot/chalkydri.toml");
76 if !path.exists() {
77 path = Path::new("/etc/chalkydri.toml");
78 if !path.exists() {
79 path = Path::new("./chalkydri.toml");
80 }
81 }
82
83 Arc::new(RwLock::new(Config::load(path).unwrap_or_default()))
85});
86
87#[cfg(feature = "rerun")]
88#[allow(non_upper_case_globals)]
89static Rerun: Lazy<RecordingStream> = Lazy::new(|| {
90 #[cfg(feature = "rerun_web_viewer")]
91 re_sdk::RecordingStreamBuilder::new("chalkydri")
92 .serve_web(
93 "0.0.0.0",
94 WebViewerServerPort(8080),
95 RerunServerPort(6969),
96 MemoryLimit::from_bytes(10_000_000),
97 true,
98 )
99 .unwrap()
100 .into()
101});
102
103#[allow(non_upper_case_globals)]
104static Nt: Lazy<NtConn> = Lazy::new(|| {
105 futures_executor::block_on(async {
106 let roborio_ip = {
108 let Config {
109 ntables_ip,
110 team_number,
111 ..
112 } = &*Cfg.read().await;
113
114 ntables_ip
115 .clone()
116 .map(|s| {
117 s.parse::<Ipv4Addr>()
118 .expect("failed to parse ip address")
119 .octets()
120 })
121 .unwrap_or_else(|| gen_team_ip(*team_number).expect("failed to generate team ip"))
122 };
123
124 let dev_name = if let Some(dev_name) = (*Cfg.read().await).device_name.clone() {
126 dev_name
127 } else {
128 warn!("device name not set! generating one...");
129
130 let dev_name = String::from("chalkydri");
132 (*Cfg.write().await).device_name = Some(dev_name.clone());
133
134 dev_name
135 };
136
137 let nt: NtConn;
138
139 match NtConn::new(Ipv4Addr::from(roborio_ip).to_string(), dev_name.clone()).await {
140 Ok(conn) => {
141 nt = conn;
142 }
143 Err(err) => {
144 panic!("Error connecting to NT server: {err:?}");
145 }
146 }
147
148 info!("Connected to NT server at {roborio_ip:?} successfully!");
149
150 nt
151 })
152});
153
154#[tokio::main(worker_threads = 16)]
155async fn main() -> Result<(), Box<dyn Error>> {
156 println!(
157 r#"
158 )) (( ___ __ _ __ ___
159 / / \ \ | | | | | | | / \ / | \ | | |
160 / \\ _ / /\ | |__| |__| | |/ V | | |__| |
161 / / \__/6\>_/ \ \ | | | | | | |\ | | | | \ |
162( __ __ ) |___ | | | | |__ | \ | |_/ | \ _|_
163 \_____ _____/
164 //////\ High-performance vision system
165 UUUUUUU FRC Team 4533 - Phoenix
166"#
167 );
168
169 let filter = EnvFilter::from_default_env();
171 let layer = tracing_subscriber::fmt::layer().with_filter(filter);
172 tracing_subscriber::registry().with(layer).init();
173
174 info!("starting up...");
175
176 let _ = rustix::system::delete_module(c"rpivid_hevc", 0);
178 let _ = rustix::system::delete_module(c"pisp_be", 0);
179
180 gstreamer::init().unwrap();
182 debug!("initialized gstreamer");
183
184 let (tx, mut rx) = mpsc::channel::<()>(1);
186 let cam_man = CameraManager::new(Nt.clone(), tx).await;
188 let api = tokio::spawn(run_api(cam_man.clone()));
190
191 tokio::select!(
193 _ = api => {},
194 _ = tokio::signal::ctrl_c() => {},
195 _ = rx.recv() => {},
196 );
197
198 Ok(())
199}