diff --git a/README.md b/README.md
index b5036aa..6d6b110 100644
--- a/README.md
+++ b/README.md
@@ -10,18 +10,23 @@ A set of tools to plot values from the target to graph in rerun with minimal per
#![no_main]
use cortex_m_rt::entry;
+
+use defmt_rtt as _;
+use panic_halt as _;
use probe_plotter::{make_metric, make_setting};
#[entry]
fn main() -> ! {
+ defmt::println!("Running...");
let mut sawtooth = make_metric!(SAWTOOTH: i32 = 42, "(SAWTOOTH / 10) % 100").unwrap();
+ defmt::println!("sawtooth initialized to: {}", sawtooth.get());
let mut sine = make_metric!(SINE: i32 = 42, "100 * sin(2 * pi * SINE / 4000)").unwrap();
let mut setting_roundtrip =
make_metric!(SETTING_ROUNDTRIP: i8 = 0, "SETTING_ROUNDTRIP").unwrap();
// Allow values -1..=7, step by 2, so {-1, 1, 3, 5, 7}
- let mut setting = make_setting!(SETTING: i8 = 42, -1..=7, 2).unwrap();
+ let mut setting = make_setting!(SETTING: i8 = 5, -1..=7, 2).unwrap();
loop {
for i in 0..i32::MAX {
@@ -32,7 +37,6 @@ fn main() -> ! {
}
}
}
-
```
The formulas seen in the `make_metric` macro invocation are computed by the host and will thus have zero impact on the targets performance. The `set` method on the metrics object is simply a volatile store which is quite cheap. The host will then read that value using the debug probe at regular intervals and update the graph on any changes.
@@ -54,8 +58,8 @@ cd examples/simple
cargo run # Let it flash and then cancel (Ctrl+C) to let the target continue running in the background while giving up access to the probe
cd ../probe-plotter-tools
-cargo run ../examples/simple/target/thumbv7em-none-eabihf/debug/simple stm32g474retx
-# Rerun will open with a graph showing all created metrics objects
+cargo run --bin custom-viewer ../examples/simple/target/thumbv7em-none-eabihf/debug/simple stm32g474retx
+# Rerun will open with a graph showing all created metrics objects and a panel with settings at the right hand side
```
-
+
diff --git a/examples/simple/src/main.rs b/examples/simple/src/main.rs
index a79b1e2..48401de 100644
--- a/examples/simple/src/main.rs
+++ b/examples/simple/src/main.rs
@@ -11,14 +11,14 @@ use probe_plotter::{make_metric, make_setting};
fn main() -> ! {
defmt::println!("Running...");
let mut sawtooth = make_metric!(SAWTOOTH: i32 = 42, "(SAWTOOTH / 10) % 100").unwrap();
- defmt::println!("foo initialized to: {}", sawtooth.get());
+ defmt::println!("sawtooth initialized to: {}", sawtooth.get());
let mut sine = make_metric!(SINE: i32 = 42, "100 * sin(2 * pi * SINE / 4000)").unwrap();
let mut setting_roundtrip =
make_metric!(SETTING_ROUNDTRIP: i8 = 0, "SETTING_ROUNDTRIP").unwrap();
// Allow values -1..=7, step by 2, so {-1, 1, 3, 5, 7}
- let mut setting = make_setting!(SETTING: i8 = 42, -1..=7, 2).unwrap();
+ let mut setting = make_setting!(SETTING: i8 = 5, -1..=7, 2).unwrap();
loop {
for i in 0..i32::MAX {
diff --git a/probe-plotter-tools/Cargo.lock b/probe-plotter-tools/Cargo.lock
index ee28a5f..6e780e4 100644
--- a/probe-plotter-tools/Cargo.lock
+++ b/probe-plotter-tools/Cargo.lock
@@ -124,7 +124,7 @@ version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
- "gimli",
+ "gimli 0.31.1",
]
[[package]]
@@ -163,6 +163,17 @@ version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
+[[package]]
+name = "alterable_logger"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "914a2c81a0e8d57d88d11554612d5e0afe5f942cecbcc239b10a394fd7ce404b"
+dependencies = [
+ "arc-swap",
+ "log",
+ "once_cell",
+]
+
[[package]]
name = "android-activity"
version = "0.6.0"
@@ -281,6 +292,12 @@ dependencies = [
"x11rb",
]
+[[package]]
+name = "arc-swap"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
+
[[package]]
name = "array-init"
version = "2.1.0"
@@ -621,9 +638,9 @@ dependencies = [
[[package]]
name = "async-lock"
-version = "3.4.0"
+version = "3.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
+checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc"
dependencies = [
"event-listener",
"event-listener-strategy",
@@ -1204,6 +1221,23 @@ dependencies = [
"thiserror 1.0.69",
]
+[[package]]
+name = "cbor-edn"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9b43829b1f353168aa8593c2e4ef6e71a81d6749fe09edfc33849f890c69278"
+dependencies = [
+ "chrono",
+ "data-encoding",
+ "data-encoding-macro",
+ "encoding_rs",
+ "hex",
+ "hexfloat2",
+ "num-bigint",
+ "num-traits",
+ "peg 0.8.5",
+]
+
[[package]]
name = "cc"
version = "1.2.31"
@@ -1724,6 +1758,32 @@ dependencies = [
"parking_lot_core",
]
+[[package]]
+name = "data-encoding"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
+
+[[package]]
+name = "data-encoding-macro"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47ce6c96ea0102f01122a185683611bd5ac8d99e62bc59dd12e6bda344ee673d"
+dependencies = [
+ "data-encoding",
+ "data-encoding-macro-internal",
+]
+
+[[package]]
+name = "data-encoding-macro-internal"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976"
+dependencies = [
+ "data-encoding",
+ "syn 2.0.104",
+]
+
[[package]]
name = "data-url"
version = "0.3.1"
@@ -2261,6 +2321,41 @@ dependencies = [
"defmt-macros",
]
+[[package]]
+name = "defmt-decoder"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87b563d700380314b45fa0ddfbf2ea998a80c64b7d88ab42b0b1fba774c4da66"
+dependencies = [
+ "alterable_logger",
+ "anyhow",
+ "byteorder",
+ "cbor-edn",
+ "colored",
+ "defmt-json-schema",
+ "defmt-parser",
+ "dissimilar",
+ "gimli 0.29.0",
+ "log",
+ "nom",
+ "object 0.35.0",
+ "regex",
+ "ryu",
+ "serde",
+ "serde_json",
+ "time",
+]
+
+[[package]]
+name = "defmt-json-schema"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96b04d228e57a61cf385d86bc8980bb41b47c6fc0eace90592668df97b2dad6a"
+dependencies = [
+ "log",
+ "serde",
+]
+
[[package]]
name = "defmt-macros"
version = "1.0.1"
@@ -2375,6 +2470,12 @@ dependencies = [
"syn 2.0.104",
]
+[[package]]
+name = "dissimilar"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8975ffdaa0ef3661bfe02dbdcc06c9f829dfafe6a3c474de366a8d5e44276921"
+
[[package]]
name = "dlib"
version = "0.5.2"
@@ -2706,6 +2807,15 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
+[[package]]
+name = "encoding_rs"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
+dependencies = [
+ "cfg-if",
+]
+
[[package]]
name = "endi"
version = "1.1.0"
@@ -2915,9 +3025,9 @@ dependencies = [
[[package]]
name = "event-listener"
-version = "5.4.0"
+version = "5.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae"
+checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
dependencies = [
"concurrent-queue",
"parking",
@@ -2934,6 +3044,12 @@ dependencies = [
"pin-project-lite",
]
+[[package]]
+name = "fallible-iterator"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
+
[[package]]
name = "fastrand"
version = "2.3.0"
@@ -2951,9 +3067,9 @@ dependencies = [
[[package]]
name = "ffmpeg-sidecar"
-version = "2.0.6"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f3389f35638a429d3f2b1bbe18a548d6b65cce18f4c32b93d965bf4368fa42d"
+checksum = "0f35f3bfdf862abfb6999b3f9079c5ec5c55dfa1010e907ed055e4fbdd64c335"
dependencies = [
"anyhow",
]
@@ -3143,9 +3259,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-lite"
-version = "2.6.0"
+version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532"
+checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
dependencies = [
"fastrand",
"futures-core",
@@ -3242,6 +3358,16 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "gimli"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
+dependencies = [
+ "fallible-iterator",
+ "stable_deref_trait",
+]
+
[[package]]
name = "gimli"
version = "0.31.1"
@@ -3582,6 +3708,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
+[[package]]
+name = "hexfloat2"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "befe65164a090041cdf6e0d21a0ec3198d856fbfe2b76e324a073e790bb49f8c"
+
[[package]]
name = "hidapi"
version = "2.6.3"
@@ -4180,8 +4312,7 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "lexers"
version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82de9bb5d9e8ff5e13532a45583ea972e220b8a6efef755daad1f285333a8afa"
+source = "git+https://github.com/usbalbin/tox?branch=add-ln#835b0b2598deb80583faee261798a8c4768bbc6a"
[[package]]
name = "lexical-core"
@@ -4346,6 +4477,9 @@ name = "log"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
+dependencies = [
+ "serde",
+]
[[package]]
name = "log-once"
@@ -4524,6 +4658,12 @@ dependencies = [
"unicase",
]
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
[[package]]
name = "miniz_oxide"
version = "0.8.9"
@@ -4713,6 +4853,16 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
[[package]]
name = "notify"
version = "6.1.1"
@@ -5170,6 +5320,15 @@ dependencies = [
"objc2-foundation 0.2.2",
]
+[[package]]
+name = "object"
+version = "0.35.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "object"
version = "0.36.7"
@@ -5368,8 +5527,18 @@ version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f76678828272f177ac33b7e2ac2e3e73cc6c1cd1e3e387928aa69562fa51367"
dependencies = [
- "peg-macros",
- "peg-runtime",
+ "peg-macros 0.6.3",
+ "peg-runtime 0.6.3",
+]
+
+[[package]]
+name = "peg"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9928cfca101b36ec5163e70049ee5368a8a1c3c6efc9ca9c5f9cc2f816152477"
+dependencies = [
+ "peg-macros 0.8.5",
+ "peg-runtime 0.8.5",
]
[[package]]
@@ -5378,7 +5547,18 @@ version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "636d60acf97633e48d266d7415a9355d4389cea327a193f87df395d88cd2b14d"
dependencies = [
- "peg-runtime",
+ "peg-runtime 0.6.3",
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "peg-macros"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6298ab04c202fa5b5d52ba03269fb7b74550b150323038878fe6c372d8280f71"
+dependencies = [
+ "peg-runtime 0.8.5",
"proc-macro2",
"quote",
]
@@ -5389,6 +5569,12 @@ version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555b1514d2d99d78150d3c799d4c357a3e2c2a8062cd108e93a06d9057629c5"
+[[package]]
+name = "peg-runtime"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "132dca9b868d927b35b5dd728167b2dee150eb1ad686008fc71ccb298b776fca"
+
[[package]]
name = "percent-encoding"
version = "2.3.1"
@@ -5536,7 +5722,7 @@ checksum = "dbadf9cb4a79d516de4c64806fe64ffbd8161d1ac685d000be789fb628b88963"
dependencies = [
"byteorder",
"linked-hash-map",
- "peg",
+ "peg 0.6.3",
"skeptic",
]
@@ -5567,9 +5753,9 @@ dependencies = [
[[package]]
name = "polling"
-version = "3.9.0"
+version = "3.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ee9b2fa7a4517d2c91ff5bc6c297a427a96749d15f98fcdbb22c05571a4d4b7"
+checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829"
dependencies = [
"cfg-if",
"concurrent-queue",
@@ -5639,6 +5825,8 @@ name = "probe-plotter-tools"
version = "0.1.0"
dependencies = [
"defmt",
+ "defmt-decoder",
+ "defmt-parser",
"goblin",
"mimalloc",
"object 0.37.2",
@@ -8306,8 +8494,7 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "shunting"
version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6bc210d95d59fbf54a2849a956866f9ec11f8e2ca3595a6d3f94e8d5b4cd74f"
+source = "git+https://github.com/usbalbin/tox?branch=add-ln#835b0b2598deb80583faee261798a8c4768bbc6a"
dependencies = [
"lexers",
"libm",
@@ -8317,9 +8504,9 @@ dependencies = [
[[package]]
name = "signal-hook-registry"
-version = "1.4.5"
+version = "1.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
+checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
dependencies = [
"libc",
]
@@ -8919,9 +9106,9 @@ dependencies = [
[[package]]
name = "tokio-util"
-version = "0.7.15"
+version = "0.7.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
+checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
dependencies = [
"bytes",
"futures-core",
diff --git a/probe-plotter-tools/Cargo.toml b/probe-plotter-tools/Cargo.toml
index 5679a25..34c6150 100644
--- a/probe-plotter-tools/Cargo.toml
+++ b/probe-plotter-tools/Cargo.toml
@@ -18,4 +18,7 @@ serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.141"
# mimalloc is a much faster allocator:
mimalloc = "0.1.43"
-shunting = "0.1.2"
\ No newline at end of file
+#shunting = "0.1.2"
+shunting = { git = "https://github.com/usbalbin/tox", branch = "add-ln" }
+defmt-decoder = "1.0.0"
+defmt-parser = "1.0.0"
diff --git a/probe-plotter-tools/src/bin/viewer.rs b/probe-plotter-tools/src/bin/custom-viewer.rs
similarity index 62%
rename from probe-plotter-tools/src/bin/viewer.rs
rename to probe-plotter-tools/src/bin/custom-viewer.rs
index caf31bf..e1be368 100644
--- a/probe-plotter-tools/src/bin/viewer.rs
+++ b/probe-plotter-tools/src/bin/custom-viewer.rs
@@ -1,27 +1,49 @@
// A custom rerun viewer capable of showing and editing settings
-use std::{sync::mpsc, thread, time::Duration};
-
-use probe_plotter_tools::{gui::MyApp, metric::Status, parse_elf_file, setting::Setting};
-use rerun::external::{eframe, re_crash_handler, re_grpc_server, re_log, re_viewer, tokio};
-use shunting::MathContext;
+use probe_plotter_tools::{gui::MyApp, parse, probe_background_thread, setting::Setting};
+use rerun::external::{eframe, re_crash_handler, re_grpc_server, re_viewer, tokio};
+use std::{env, io::Read, sync::mpsc, thread, time::Duration};
#[tokio::main]
async fn main() -> Result<(), Box> {
- let elf_path = std::env::args()
- .nth(1)
- .expect("Usage: \nprobe-plotter /path/to/elf chip");
+ let help = "Usage: \nprobe-plotter /path/to/elf [chip] [update_rate_ms=10] [channel_mode=no change]";
+
+ let elf_path = env::args().nth(1).expect(help);
- let target = std::env::args()
+ let target = env::args()
.nth(2)
.unwrap_or_else(|| "stm32g474retx".to_owned());
- let (mut metrics, mut settings) = parse_elf_file(&elf_path);
+ let update_rate = env::args()
+ .nth(3)
+ .map(|s| {
+ Duration::from_millis(
+ s.parse()
+ .unwrap_or_else(|_| panic!("Invalid update_rate\n\n{help}")),
+ )
+ })
+ .unwrap_or_else(|| Duration::from_millis(10));
+
+ let channel_mode = env::args()
+ .nth(4)
+ .map(|s| match s.as_str() {
+ "NoBlockSkip" => probe_rs::rtt::ChannelMode::NoBlockSkip,
+ "NoBlockTrim" => probe_rs::rtt::ChannelMode::NoBlockTrim,
+ "BlockIfFull" => probe_rs::rtt::ChannelMode::BlockIfFull,
+ _ => panic!("Invalid channel_mode. Select one of\n* NoBlockSkip\n* NoBlockTrim\n* BlockIfFull\n\n{help}"),
+ });
+
+ let mut elf_bytes = Vec::new();
+ std::fs::File::open(elf_path)
+ .unwrap()
+ .read_to_end(&mut elf_bytes)
+ .unwrap();
+
+ let (metrics, settings) = parse(&elf_bytes);
let main_thread_token = rerun::MainThreadToken::i_promise_i_am_on_the_main_thread();
// Direct calls using the `log` crate to stderr. Control with `RUST_LOG=debug` etc.
- re_log::setup_logging();
// Install handlers for panics and crashes that prints to stderr and send
// them to Rerun analytics (if the `analytics` feature is on in `Cargo.toml`).
@@ -49,37 +71,16 @@ async fn main() -> Result<(), Box> {
// probe-thread
thread::spawn(move || {
- let mut session = probe_rs::Session::auto_attach(target, Default::default()).unwrap();
- let mut core = session.core(0).unwrap();
-
- let rec = rerun::RecordingStreamBuilder::new("probe-plotter")
- .spawn()
- .unwrap();
-
- // Load initial values from device
- for setting in &mut settings {
- setting.read(&mut core).unwrap();
- }
-
- // Send initial settings back to main thread
- initial_settings_sender.send(settings).unwrap();
-
- let mut math_ctx = MathContext::new();
- loop {
- for mut setting in settings_update_receiver.try_iter() {
- setting.write(setting.value, &mut core).unwrap();
- }
-
- for m in &mut metrics {
- m.read(&mut core, &mut math_ctx).unwrap();
- let (x, s) = m.compute(&mut math_ctx);
- if let Status::New = s {
- rec.log(m.name.clone(), &rerun::Scalars::single(x)).unwrap();
- } else {
- std::thread::sleep(Duration::from_millis(1));
- }
- }
- }
+ probe_background_thread(
+ update_rate,
+ channel_mode,
+ &target,
+ &elf_bytes,
+ settings,
+ metrics,
+ settings_update_receiver,
+ initial_settings_sender,
+ )
});
// Receive initial settings from to probe-thread thread
diff --git a/probe-plotter-tools/src/main.rs b/probe-plotter-tools/src/bin/minimal.rs
similarity index 93%
rename from probe-plotter-tools/src/main.rs
rename to probe-plotter-tools/src/bin/minimal.rs
index 851a117..32e1784 100644
--- a/probe-plotter-tools/src/main.rs
+++ b/probe-plotter-tools/src/bin/minimal.rs
@@ -1,3 +1,5 @@
+// Connect to the regular rerun viewer. This does not support Settings, only Metrics
+
use probe_plotter_tools::{metric::Status, parse_elf_file};
use shunting::MathContext;
use std::time::Duration;
diff --git a/probe-plotter-tools/src/lib.rs b/probe-plotter-tools/src/lib.rs
index dad1840..c223d42 100644
--- a/probe-plotter-tools/src/lib.rs
+++ b/probe-plotter-tools/src/lib.rs
@@ -3,10 +3,16 @@ pub mod metric;
pub mod setting;
pub mod symbol;
-use std::io::Read;
+use std::{io::Read, sync::mpsc, time::Duration};
+use defmt_decoder::DecodeError;
+use defmt_parser::Level;
use object::{Object, ObjectSymbol};
-use probe_rs::{Core, MemoryInterface};
+use probe_rs::{
+ Core, MemoryInterface,
+ rtt::{ChannelMode, Rtt},
+};
+use rerun::TextLogLevel;
use serde::Deserialize;
use shunting::{MathContext, ShuntingParser};
@@ -95,6 +101,7 @@ pub fn parse(elf_bytes: &[u8]) -> (Vec, Vec) {
(metrics, settings)
}
+/// Parse elf file into a set of Metrics and Settings
pub fn parse_elf_file(elf_path: &str) -> (Vec, Vec) {
let mut buffer = Vec::new();
std::fs::File::open(elf_path)
@@ -106,3 +113,215 @@ pub fn parse_elf_file(elf_path: &str) -> (Vec, Vec) {
(metrics, settings)
}
+
+/// A background task which communicates with the probe
+///
+/// This handles
+/// * defmt logging
+/// * reading metrics
+/// * reading initial values for settings
+/// * writing updated settings
+#[allow(clippy::too_many_arguments)]
+pub fn probe_background_thread(
+ update_rate: Duration,
+ channel_mode: Option,
+ target: &str,
+ elf_bytes: &[u8],
+ mut settings: Vec,
+ mut metrics: Vec,
+ settings_update_receiver: mpsc::Receiver,
+ initial_settings_sender: mpsc::Sender>,
+) {
+ let mut session = probe_rs::Session::auto_attach(target, Default::default()).unwrap();
+ let mut core = session.core(0).unwrap();
+ let mut rtt = Rtt::attach(&mut core).unwrap();
+ let table = defmt_decoder::Table::parse(elf_bytes).unwrap().unwrap();
+
+ // TODO: Get this to work
+ // This produces ascii escape codes which egui/rerun does not seem to understand
+
+ /*let show_timestamps = true;
+ let show_location = true;
+ let log_format = None;
+ let has_timestamp = table.has_timestamp();
+
+ // Format options:
+ // 1. Oneline format with optional location
+ // 2. Custom format for the channel
+ // 3. Default with optional location
+ let format = match log_format {
+ None | Some("oneline") => FormatterFormat::OneLine {
+ with_location: show_location,
+ },
+ Some("full") => FormatterFormat::Default {
+ with_location: show_location,
+ },
+ Some(format) => FormatterFormat::Custom(format),
+ };
+
+ let formatter = Formatter::new(FormatterConfig {
+ format,
+ is_timestamp_available: has_timestamp && show_timestamps,
+ });*/
+
+ let rec = rerun::RecordingStreamBuilder::new("probe-plotter")
+ .spawn()
+ .unwrap();
+
+ let locs = match table.get_locations(elf_bytes) {
+ Ok(locs) if locs.is_empty() => {
+ rec.log(
+ "log",
+ &rerun::TextLog::new("Insufficient DWARF info; compile your program with `debug = 2` to enable location info.").with_level(TextLogLevel::WARN)).unwrap();
+ None
+ }
+ Ok(locs) if table.indices().all(|idx| locs.contains_key(&(idx as u64))) => Some(locs),
+ Ok(_) => {
+ rec.log(
+ "log",
+ &rerun::TextLog::new(
+ "Location info is incomplete; it will be omitted from the output.",
+ )
+ .with_level(TextLogLevel::WARN),
+ )
+ .unwrap();
+ None
+ }
+ Err(e) => {
+ rec.log(
+ "log",
+ &rerun::TextLog::new(format!(
+ "Failed to parse location data: {e:?}; it will be omitted from the output."
+ ))
+ .with_level(TextLogLevel::WARN),
+ )
+ .unwrap();
+
+ None
+ }
+ };
+
+ if let Some(channel_mode) = channel_mode {
+ for ch in &mut rtt.up_channels {
+ ch.set_mode(&mut core, channel_mode).unwrap();
+ }
+ }
+
+ let mut decoders: Vec<_> = rtt
+ .up_channels
+ .iter()
+ .map(|_| table.new_stream_decoder())
+ .collect();
+
+ // Load initial values from device
+ for setting in &mut settings {
+ setting.read(&mut core).unwrap();
+ }
+
+ // Send initial settings back to main thread
+ initial_settings_sender.send(settings).unwrap();
+
+ let mut math_ctx = MathContext::new();
+ loop {
+ for mut setting in settings_update_receiver.try_iter() {
+ setting.write(setting.value, &mut core).unwrap();
+ }
+
+ receive_defmt_messages(&mut rtt, &mut core, &mut decoders);
+ log_defmt_messages(&rec, &locs, &mut decoders);
+
+ for m in &mut metrics {
+ m.read(&mut core, &mut math_ctx).unwrap();
+ let (x, _s) = m.compute(&mut math_ctx);
+ rec.log(m.name.clone(), &rerun::Scalars::single(x)).unwrap();
+ }
+ std::thread::sleep(update_rate);
+ }
+}
+
+/// Receive defmt messages frome probe
+pub fn receive_defmt_messages<'a>(
+ rtt: &mut Rtt,
+ core: &mut Core,
+ decoders: &mut Vec>,
+) {
+ loop {
+ let mut has_data = false;
+ let mut buf = [0; 256];
+ for (ch, decoder) in rtt.up_channels.iter_mut().zip(&mut *decoders) {
+ let read_count = ch.read(core, &mut buf).unwrap();
+
+ if read_count > 0 {
+ dbg!(read_count);
+ decoder.received(&buf[..read_count]);
+ has_data = true;
+ }
+ }
+ if !has_data {
+ break;
+ }
+ }
+}
+
+/// Log defmt messages to rerun
+pub fn log_defmt_messages<'a>(
+ rec: &rerun::RecordingStream,
+ locs: &'a Option>,
+ decoders: &mut Vec>,
+) {
+ loop {
+ let mut has_decoded = false;
+ for decoder in &mut *decoders {
+ let frame = match decoder.decode() {
+ Ok(f) => f,
+ Err(DecodeError::UnexpectedEof) => continue,
+ Err(defmt_decoder::DecodeError::Malformed) => {
+ rec.log(
+ "log",
+ &rerun::TextLog::new("DecodeError::Malformed")
+ .with_level(TextLogLevel::WARN),
+ )
+ .unwrap();
+ continue;
+ }
+ };
+ has_decoded = true;
+
+ let level = match frame.level() {
+ Some(Level::Trace) => TextLogLevel::TRACE,
+ Some(Level::Debug) => TextLogLevel::DEBUG,
+ Some(Level::Info) => TextLogLevel::INFO,
+ Some(Level::Warn) => TextLogLevel::WARN,
+ Some(Level::Error) => TextLogLevel::ERROR,
+ None => TextLogLevel::INFO,
+ };
+
+ let loc = locs.as_ref().and_then(|locs| locs.get(&frame.index()));
+ let (file, line, module) = if let Some(loc) = loc {
+ (
+ loc.file.display().to_string(),
+ loc.line.to_string(),
+ loc.module.as_str(),
+ )
+ } else {
+ (
+ format!(
+ "└─ ",
+ frame.index()
+ ),
+ "?".to_string(),
+ "?",
+ )
+ };
+
+ //let msg = formatter.format_frame(frame, Some(&file), line, module);
+ let msg = format!("{module} :: {} {file}:{line}", frame.display(false));
+
+ rec.log("log", &rerun::TextLog::new(msg).with_level(level))
+ .unwrap();
+ }
+ if !has_decoded {
+ break;
+ }
+ }
+}