From 874d6794ca1a8d0d59e5a0ba94bb389c6b91ceeb Mon Sep 17 00:00:00 2001 From: Thomas Antony Date: Tue, 5 Dec 2023 20:22:53 -0800 Subject: [PATCH 1/8] Update .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c0e913a6..8b895c3f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/dist /target Cargo.lock *.anis* @@ -10,4 +11,4 @@ cspice.tar.Z .venv *.pca *.sca -*.epa \ No newline at end of file +*.epa From 319ad5ce71a6096c83a6f8bdcdbc94c66b9a4682 Mon Sep 17 00:00:00 2001 From: Thomas Antony Date: Tue, 5 Dec 2023 18:17:41 -0800 Subject: [PATCH 2/8] Add load_from_bytes() and refactor load() to use it --- src/almanac/mod.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/almanac/mod.rs b/src/almanac/mod.rs index b2a2d0cf..50679647 100644 --- a/src/almanac/mod.rs +++ b/src/almanac/mod.rs @@ -8,6 +8,7 @@ * Documentation: https://nyxspace.com/ */ +use bytes::Bytes; use log::info; use snafu::ResultExt; use std::fs::File; @@ -94,7 +95,11 @@ impl Almanac { let bytes = file2heap!(path).with_context(|_| LoadingSnafu { path: path.to_string(), })?; + info!("Loading almanac from {path}"); + self.load_from_bytes(bytes) + } + pub fn load_from_bytes(&self, bytes: Bytes) -> Result { // Try to load as a SPICE DAF first (likely the most typical use case) // Load the header only @@ -103,7 +108,7 @@ impl Almanac { if let Ok(fileid) = file_record.identification() { match fileid { "PCK" => { - info!("Loading {path} as DAF/PCK"); + info!("Loading as DAF/PCK"); let bpc = BPC::parse(bytes) .with_context(|_| BPCSnafu { action: "parsing bytes", @@ -116,7 +121,7 @@ impl Almanac { }) } "SPK" => { - info!("Loading {path:?} as DAF/SPK"); + info!("Loading as DAF/SPK"); let spk = SPK::parse(bytes) .with_context(|_| SPKSnafu { action: "parsing bytes", From 3277fa1388cf7c5f82e536d4f07db43f9bfb5db9 Mon Sep 17 00:00:00 2001 From: Thomas Antony Date: Tue, 5 Dec 2023 18:49:52 -0800 Subject: [PATCH 3/8] Add web deps --- Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 00a397c4..f64aea33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,10 @@ egui_extras = { version = "0.24.0", features = [ egui-toast = { version = "0.10.0", optional = true } rfd = { version = "0.12.1", optional = true } +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen-futures = "0.4" +poll-promise = { version = "0.3.0", features = ["web"] } + [dev-dependencies] rust-spice = "0.7.6" parquet = "49.0.0" From 7c2c03acdd85c0f93644f6a69bfb8d02a9e184bf Mon Sep 17 00:00:00 2001 From: Thomas Antony Date: Tue, 5 Dec 2023 18:49:57 -0800 Subject: [PATCH 4/8] Add entrypoint for web --- src/bin/anise-gui/main.rs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/bin/anise-gui/main.rs b/src/bin/anise-gui/main.rs index 7c750d7f..e2abaddd 100644 --- a/src/bin/anise-gui/main.rs +++ b/src/bin/anise-gui/main.rs @@ -1,13 +1,14 @@ -use pretty_env_logger; -use std::env::{set_var, var}; - +#[allow(dead_code)] const LOG_VAR: &str = "ANISE_LOG"; mod ui; use ui::UiApp; +#[cfg(not(target_arch = "wasm32"))] fn main() { + use std::env::{set_var, var}; + if var(LOG_VAR).is_err() { set_var(LOG_VAR, "INFO"); } @@ -20,3 +21,24 @@ fn main() { Box::new(|cc| Box::new(UiApp::new(cc))), ); } + +// Entrypoint for WebAssembly +#[cfg(target_arch = "wasm32")] +fn main() { + use log::info; + + eframe::WebLogger::init(log::LevelFilter::Debug).ok(); + let web_options = eframe::WebOptions::default(); + + info!("Starting ANISE in WebAssembly mode"); + wasm_bindgen_futures::spawn_local(async { + eframe::WebRunner::new() + .start( + "anise_canvas", + web_options, + Box::new(|cc| Box::new(UiApp::new(cc))), + ) + .await + .expect("failed to start eframe"); + }); +} From 4542ee84f3372abf9e7fac813c37bd690d3f38b9 Mon Sep 17 00:00:00 2001 From: Thomas Antony Date: Tue, 5 Dec 2023 20:28:38 -0800 Subject: [PATCH 5/8] Add index.html for web app --- index.html | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 index.html diff --git a/index.html b/index.html new file mode 100644 index 00000000..5aa96521 --- /dev/null +++ b/index.html @@ -0,0 +1,11 @@ + + + + + + + + + + + From d7e5e142c59b07a0b368675acd1c6c44aa916ebb Mon Sep 17 00:00:00 2001 From: Thomas Antony Date: Tue, 5 Dec 2023 18:49:46 -0800 Subject: [PATCH 6/8] Add load_almanac() functionality for wasm targets --- src/bin/anise-gui/ui.rs | 79 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 70 insertions(+), 9 deletions(-) diff --git a/src/bin/anise-gui/ui.rs b/src/bin/anise-gui/ui.rs index f203b8ce..8e6b257d 100644 --- a/src/bin/anise-gui/ui.rs +++ b/src/bin/anise-gui/ui.rs @@ -1,5 +1,5 @@ use anise::{ - almanac::Almanac, constants::orientations::orientation_name_from_id, + almanac::Almanac, constants::orientations::orientation_name_from_id, errors::AlmanacError, naif::daf::NAIFSummaryRecord, }; use eframe::egui; @@ -8,12 +8,26 @@ use egui_extras::{Column, TableBuilder}; use egui_toast::{Toast, ToastKind, ToastOptions, Toasts}; use hifitime::TimeScale; +#[cfg(target_arch = "wasm32")] +use poll_promise::Promise; + +#[cfg(target_arch = "wasm32")] +type AlmanacFile = Option<(String, Vec)>; + #[derive(Default)] pub struct UiApp { selected_time_scale: TimeScale, show_unix: bool, almanac: Almanac, path: Option, + #[cfg(target_arch = "wasm32")] + promise: Option>, +} + +enum FileLoadResult { + NoFileSelectedYet, + Ok((String, Almanac)), + Error(AlmanacError), } impl UiApp { @@ -24,6 +38,43 @@ impl UiApp { // for e.g. egui::PaintCallback. Self::default() } + + #[cfg(target_arch = "wasm32")] + fn load_almanac(&mut self) -> FileLoadResult { + if let Some(promise) = self.promise.as_ref() { + // We are already waiting for a file, so we don't need to show the dialog again + if let Some(result) = promise.ready() { + let (file_name, data) = result.as_ref().map(|x| x.clone()).unwrap(); + self.promise = None; + match self.almanac.load_from_bytes(bytes::Bytes::from(data)) { + Ok(almanac) => FileLoadResult::Ok((file_name, almanac)), + Err(e) => FileLoadResult::Error(e), + } + } else { + FileLoadResult::NoFileSelectedYet + } + } else { + // Show the dialog and start loading the file + self.promise = Some(Promise::spawn_local(async move { + let fh = rfd::AsyncFileDialog::new().pick_file().await?; + Some((fh.file_name(), fh.read().await)) + })); + FileLoadResult::NoFileSelectedYet + } + } + + #[cfg(not(target_arch = "wasm32"))] + fn load_almanac(&mut self) -> FileLoadResult { + if let Some(path_buf) = rfd::FileDialog::new().pick_file() { + let path = path_buf.to_str().unwrap().to_string(); + match self.almanac.load(&path) { + Ok(almanac) => FileLoadResult::Ok((path, almanac)), + Err(e) => FileLoadResult::Error(e), + } + } else { + FileLoadResult::NoFileSelectedYet + } + } } impl eframe::App for UiApp { @@ -53,12 +104,24 @@ impl eframe::App for UiApp { ui.vertical_centered(|ui| { match &self.path { None => { + let mut trigger_file_load = false; + trigger_file_load |= ui.button("Select file to inspect...").clicked(); + + // If we are in the browser, we need to also check if the file + // is ready to be loaded instead of just checking if the button + // was clicked + #[cfg(target_arch = "wasm32")] + { + trigger_file_load |= self.promise.is_some(); + } + // Show the open file dialog - if ui.button("Select file to inspect...").clicked() { - if let Some(path) = rfd::FileDialog::new().pick_file() { + if trigger_file_load { // Try to load this file - match self.almanac.load(path.to_str().unwrap()) { - Ok(almanac) => { + match self.load_almanac() { + FileLoadResult::NoFileSelectedYet => { + } + FileLoadResult::Ok((path, almanac)) => { toasts.add(Toast { text: format!("Loaded {path:?}").into(), kind: ToastKind::Success, @@ -67,10 +130,9 @@ impl eframe::App for UiApp { .show_progress(true), }); self.almanac = almanac; - self.path = - Some(path.to_str().unwrap().to_string()); + self.path = Some(path); } - Err(e) => { + FileLoadResult::Error(e) => { toasts.add(Toast { text: format!("{e}").into(), kind: ToastKind::Error, @@ -80,7 +142,6 @@ impl eframe::App for UiApp { }); } } - } } } Some(path) => { From 0d27b2a09b2bd4c44da7e5f183b38aebc876f5d5 Mon Sep 17 00:00:00 2001 From: Thomas Antony Date: Tue, 5 Dec 2023 20:45:37 -0800 Subject: [PATCH 7/8] Point Cargo.toml to custom version of hifitime --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f64aea33..1cbcb5c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ exclude = [ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -hifitime = "3.8" +hifitime = {version = "3.8", git="https://github.com/nyx-space/hifitime", branch="master"} memmap2 = "=0.9.0" crc32fast = "=1.3.2" der = { version = "0.7.8", features = ["derive", "alloc", "real"] } From 1281857d36da0c4b47aed6106672f1627f2aa835 Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 11 Dec 2023 10:54:21 -0700 Subject: [PATCH 8/8] Update Cargo.toml to hifitime 3.8.6 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1cbcb5c8..8273ebaf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ exclude = [ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -hifitime = {version = "3.8", git="https://github.com/nyx-space/hifitime", branch="master"} +hifitime = "3.8.6" memmap2 = "=0.9.0" crc32fast = "=1.3.2" der = { version = "0.7.8", features = ["derive", "alloc", "real"] }