added documentation and polishing UI around GPS mode

This commit is contained in:
Carlos Guerra
2026-03-29 17:44:39 +02:00
committed by Will Greenberg
parent 66f0c2a336
commit 5451e23293
11 changed files with 68 additions and 59 deletions
-43
View File
@@ -162,49 +162,6 @@ fn resolve_bin(name: &str) -> Option<String> {
None
}
impl Config {
pub fn wifi_config(&self) -> wifi_station::WifiConfig {
let (wpa_bin, hostapd_conf, ctrl_interface) = match self.device {
Device::Tmobile | Device::Wingtech => (
Some("/usr/sbin/wpa_supplicant".into()),
Some("/data/configs/hostapd.conf".into()),
None,
),
Device::Uz801 => (
Some("/system/bin/wpa_supplicant".into()),
Some("/data/misc/wifi/hostapd.conf".into()),
Some("/data/misc/wifi/sockets".into()),
),
_ => (None, None, None),
};
wifi_station::WifiConfig {
wifi_enabled: self.wifi_enabled,
dns_servers: self.dns_servers.clone(),
wifi_ssid: self.wifi_ssid.clone(),
wifi_password: self.wifi_password.clone(),
security_type: self.wifi_security,
wpa_supplicant_bin: wpa_bin.or_else(|| resolve_bin("wpa_supplicant")),
hostapd_conf,
ctrl_interface,
udhcpc_hook_path: Some("/data/rayhunter/udhcpc-hook.sh".into()),
dhcp_lease_path: Some("/data/rayhunter/dhcp_lease".into()),
wpa_conf_path: Some("/data/rayhunter/wpa_sta.conf".into()),
iw_bin: resolve_bin("iw"),
udhcpc_bin: resolve_bin("udhcpc"),
crash_log_dir: Some("/data/rayhunter/crash-logs".into()),
wakelock_name: Some("rayhunter".into()),
}
}
}
fn resolve_bin(name: &str) -> Option<String> {
let local = format!("/data/rayhunter/bin/{name}");
if std::path::Path::new(&local).exists() {
return Some(local);
}
None
}
pub async fn parse_config<P>(path: P) -> Result<Config, RayhunterError>
where
P: AsRef<std::path::Path>,
+7 -2
View File
@@ -56,6 +56,7 @@ pub struct DiagTask {
notification_channel: tokio::sync::mpsc::Sender<Notification>,
min_space_to_start_mb: u64,
min_space_to_continue_mb: u64,
gps_mode: u8,
state: DiagState,
max_type_seen: EventType,
bytes_since_space_check: usize,
@@ -104,6 +105,7 @@ impl DiagTask {
notification_channel: tokio::sync::mpsc::Sender<Notification>,
min_space_to_start_mb: u64,
min_space_to_continue_mb: u64,
gps_mode: u8,
) -> Self {
Self {
ui_update_sender,
@@ -112,6 +114,7 @@ impl DiagTask {
notification_channel,
min_space_to_start_mb,
min_space_to_continue_mb,
gps_mode,
state: DiagState::Stopped,
max_type_seen: EventType::Informational,
bytes_since_space_check: 0,
@@ -144,7 +147,7 @@ impl DiagTask {
DiskSpaceCheck::Failed => {}
}
let (qmdl_file, analysis_file) = match qmdl_store.new_entry().await {
let (qmdl_file, analysis_file) = match qmdl_store.new_entry(self.gps_mode).await {
Ok(files) => files,
Err(e) => {
let msg = format!("failed creating QMDL file entry: {e}");
@@ -381,6 +384,7 @@ pub fn run_diag_read_thread(
notification_channel: tokio::sync::mpsc::Sender<Notification>,
min_space_to_start_mb: u64,
min_space_to_continue_mb: u64,
gps_mode: u8,
) {
task_tracker.spawn(async move {
info!("Using configuration for device: {0:?}", device);
@@ -396,7 +400,8 @@ pub fn run_diag_read_thread(
analyzer_config,
notification_channel,
min_space_to_start_mb,
min_space_to_continue_mb
min_space_to_continue_mb,
gps_mode,
);
qmdl_file_tx
.send(DiagDeviceCtrlMessage::StartRecording { response_tx: None })
+22 -2
View File
@@ -1,17 +1,37 @@
use axum::Json;
use axum::extract::State;
use axum::http::StatusCode;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use std::sync::Arc;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use crate::server::ServerState;
/// Accepts both a JSON number and a numeric string (e.g. `"1234567890"` or `1234567890`).
/// Truncates floats to seconds. Returns an error for non-numeric values.
fn deserialize_unix_ts<'de, D>(deserializer: D) -> Result<i64, D::Error>
where
D: Deserializer<'de>,
{
use serde::de;
use serde_json::Value;
match Value::deserialize(deserializer)? {
Value::Number(n) => n.as_i64()
.or_else(|| n.as_f64().map(|f| f as i64))
.ok_or_else(|| de::Error::custom("timestamp out of range")),
Value::String(s) => s.trim().parse::<f64>()
.map(|f| f as i64)
.map_err(|_| de::Error::custom("timestamp must be a numeric value")),
_ => Err(de::Error::custom("timestamp must be a number or numeric string")),
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct GpsData {
pub latitude: f64,
pub longitude: f64,
pub timestamp: String,
#[serde(deserialize_with = "deserialize_unix_ts")]
pub timestamp: i64,
}
#[derive(Serialize, Deserialize)]
+2 -1
View File
@@ -232,6 +232,7 @@ async fn run_with_config(
notification_service.new_handler(),
config.min_space_to_start_recording_mb,
config.min_space_to_continue_recording_mb,
config.gps_mode,
);
info!("Starting UI");
@@ -305,7 +306,7 @@ async fn run_with_config(
(Some(lat), Some(lon)) => Some(gps::GpsData {
latitude: lat,
longitude: lon,
timestamp: "fixed".to_string(),
timestamp: 0,
}),
_ => None,
}
+12 -8
View File
@@ -70,10 +70,12 @@ pub struct ManifestEntry {
/// When the manifest was uploaded to a WebDAV server
#[cfg_attr(feature = "apidocs", schema(value_type = String))]
pub upload_time: Option<DateTime<Local>>,
#[serde(default)]
pub gps_mode: Option<u8>,
}
impl ManifestEntry {
fn new() -> Self {
fn new(gps_mode: u8) -> Self {
let now = rayhunter::clock::get_adjusted_now();
let metadata = RuntimeMetadata::new();
ManifestEntry {
@@ -86,6 +88,7 @@ impl ManifestEntry {
arch: Some(metadata.arch),
stop_reason: None,
upload_time: None,
gps_mode: Some(gps_mode),
}
}
@@ -223,6 +226,7 @@ impl RecordingStore {
arch: None,
stop_reason: None,
upload_time: None,
gps_mode: None,
});
}
@@ -255,12 +259,12 @@ impl RecordingStore {
// Closes the current entry (if needed), creates a new entry based on the
// current time, and updates the manifest. Returns a tuple of the entry's
// newly created QMDL file and analysis file.
pub async fn new_entry(&mut self) -> Result<(File, File), RecordingStoreError> {
pub async fn new_entry(&mut self, gps_mode: u8) -> Result<(File, File), RecordingStoreError> {
// if we've already got an entry open, close it
if self.current_entry.is_some() {
self.close_current_entry().await?;
}
let new_entry = ManifestEntry::new();
let new_entry = ManifestEntry::new(gps_mode);
let qmdl_filepath = new_entry.get_qmdl_filepath(&self.path);
let qmdl_file = File::create(&qmdl_filepath)
.await
@@ -545,7 +549,7 @@ mod tests {
async fn test_creating_updating_and_closing_entries() {
let dir = make_temp_dir();
let mut store = RecordingStore::create(dir.path()).await.unwrap();
let _ = store.new_entry().await.unwrap();
let _ = store.new_entry(0).await.unwrap();
let entry_index = store.current_entry.unwrap();
assert_eq!(
RecordingStore::read_manifest(dir.path()).await.unwrap(),
@@ -582,7 +586,7 @@ mod tests {
async fn test_create_on_existing_store() {
let dir = make_temp_dir();
let mut store = RecordingStore::create(dir.path()).await.unwrap();
let _ = store.new_entry().await.unwrap();
let _ = store.new_entry(0).await.unwrap();
let entry_index = store.current_entry.unwrap();
store
.update_entry_qmdl_size(entry_index, 1000)
@@ -596,9 +600,9 @@ mod tests {
async fn test_repeated_new_entries() {
let dir = make_temp_dir();
let mut store = RecordingStore::create(dir.path()).await.unwrap();
let _ = store.new_entry().await.unwrap();
let _ = store.new_entry(0).await.unwrap();
let entry_index = store.current_entry.unwrap();
let _ = store.new_entry().await.unwrap();
let _ = store.new_entry(0).await.unwrap();
let new_entry_index = store.current_entry.unwrap();
assert_ne!(entry_index, new_entry_index);
assert_eq!(store.manifest.entries.len(), 2);
@@ -608,7 +612,7 @@ mod tests {
async fn test_delete_all_entries() {
let dir = make_temp_dir();
let mut store = RecordingStore::create(dir.path()).await.unwrap();
let _ = store.new_entry().await.unwrap();
let _ = store.new_entry(0).await.unwrap();
assert!(store.current_entry.is_some());
store.delete_all_entries().await.unwrap();
+1 -1
View File
@@ -523,7 +523,7 @@ mod tests {
) -> String {
let entry_name = {
let mut store = store_lock.write().await;
let (mut qmdl_file, _analysis_file) = store.new_entry().await.unwrap();
let (mut qmdl_file, _analysis_file) = store.new_entry(0).await.unwrap();
if !test_data.is_empty() {
use tokio::io::AsyncWriteExt;