Skip to content

Commit d398935

Browse files
committed
Graph
1 parent 7cfbf7f commit d398935

File tree

12 files changed

+255
-106
lines changed

12 files changed

+255
-106
lines changed

README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,19 @@ A set of tools to plot values from the target to graph in rerun with minimal per
1010
#![no_main]
1111

1212
use cortex_m_rt::entry;
13-
use probe_plotter::{make_metric, make_setting};
13+
use probe_plotter::{make_graph, make_metric, make_setting};
1414

1515
#[entry]
1616
fn main() -> ! {
17-
let mut sawtooth = make_metric!(SAWTOOTH: i32 = 42, "(SAWTOOTH / 10) % 100").unwrap();
18-
let mut sine = make_metric!(SINE: i32 = 42, "100 * sin(2 * pi * SINE / 4000)").unwrap();
17+
make_graph!(SAWTOOTH = "(SAWTOOTH / 10) % 100");
18+
make_graph!(SINE = "100 * sin(2 * pi * SINE / 4000)");
19+
make_graph!(SINE_TIMES_SAWTOOTH = "100 * sin(2 * pi * SINE / 4000) * (SAWTOOTH / 10) % 100)");
20+
make_graph!(SETTING = "SETTING");
21+
make_graph!(SETTING_ROUNDTRIP = "SETTING_ROUNDTRIP");
1922

20-
let mut setting_roundtrip =
21-
make_metric!(SETTING_ROUNDTRIP: i8 = 0, "SETTING_ROUNDTRIP").unwrap();
23+
let mut sawtooth = make_metric!(SAWTOOTH: i32 = 42).unwrap();
24+
let mut sine = make_metric!(SINE: i32 = 42).unwrap();
25+
let mut setting_roundtrip = make_metric!(SETTING_ROUNDTRIP: i8 = 0).unwrap();
2226

2327
// Allow values -1..=7, step by 2, so {-1, 1, 3, 5, 7}
2428
let mut setting = make_setting!(SETTING: i8 = 42, -1..=7, 2).unwrap();
@@ -29,6 +33,8 @@ fn main() -> ! {
2933
sine.set(i);
3034

3135
setting_roundtrip.set(setting.get());
36+
37+
cortex_m::asm::delay(100_000);
3238
}
3339
}
3440
}

examples/simple/src/main.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,22 @@ use cortex_m_rt::entry;
55

66
use defmt_rtt as _;
77
use panic_halt as _;
8-
use probe_plotter::{make_metric, make_setting};
8+
use probe_plotter::{make_graph, make_metric, make_setting};
99

1010
#[entry]
1111
fn main() -> ! {
12+
make_graph!(SAWTOOTH = "(SAWTOOTH / 10) % 100");
13+
make_graph!(SINE = "100 * sin(2 * pi * SINE / 4000)");
14+
make_graph!(SINE_TIMES_SAWTOOTH = "100 * sin(2 * pi * SINE / 4000) * (SAWTOOTH / 10) % 100)");
15+
make_graph!(SETTING = "SETTING");
16+
make_graph!(SETTING_ROUNDTRIP = "SETTING_ROUNDTRIP");
17+
1218
defmt::println!("Running...");
13-
let mut sawtooth = make_metric!(SAWTOOTH: i32 = 42, "(SAWTOOTH / 10) % 100").unwrap();
19+
let mut sawtooth = make_metric!(SAWTOOTH: i32 = 42).unwrap();
1420
defmt::println!("foo initialized to: {}", sawtooth.get());
15-
let mut sine = make_metric!(SINE: i32 = 42, "100 * sin(2 * pi * SINE / 4000)").unwrap();
21+
let mut sine = make_metric!(SINE: i32 = 42).unwrap();
1622

17-
let mut setting_roundtrip =
18-
make_metric!(SETTING_ROUNDTRIP: i8 = 0, "SETTING_ROUNDTRIP").unwrap();
23+
let mut setting_roundtrip = make_metric!(SETTING_ROUNDTRIP: i8 = 0).unwrap();
1924

2025
// Allow values -1..=7, step by 2, so {-1, 1, 3, 5, 7}
2126
let mut setting = make_setting!(SETTING: i8 = 42, -1..=7, 2).unwrap();

macros/src/args.rs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
// Based on defmt
22

33
use syn::{
4-
LitStr, RangeLimits, Token,
4+
RangeLimits, Token,
55
parse::{self, Parse, ParseStream},
66
spanned::Spanned,
77
};
88

9-
//FOO: i32 = 0, "x * 3.0"
10-
//FOO: i32 = 0 // defaults to "x"
9+
//FOO: i32 = 0
1110
pub(crate) struct MetricArgs {
1211
pub(crate) name: syn::Ident,
1312
pub(crate) ty: syn::Ident,
1413
pub(crate) initial_val: syn::Expr,
15-
pub(crate) expression_string: syn::LitStr,
1614
}
1715

1816
impl Parse for MetricArgs {
@@ -23,20 +21,10 @@ impl Parse for MetricArgs {
2321
let _comma: Token![=] = input.parse()?;
2422
let initial_val = input.parse()?;
2523

26-
let comma: parse::Result<Token![,]> = input.parse();
27-
let expression_string = input.parse();
28-
29-
let expression_string = match (comma, expression_string) {
30-
(Ok(_), Ok(expr)) => expr,
31-
(Ok(_), Err(e)) => return Err(e),
32-
(Err(_), _) => LitStr::new(&name.to_string(), name.span()),
33-
};
34-
3524
Ok(Self {
3625
name,
3726
ty,
3827
initial_val,
39-
expression_string,
4028
})
4129
}
4230
}
@@ -100,6 +88,25 @@ impl Parse for SettingArgs {
10088
}
10189
}
10290

91+
//FOO = "x * 3.0"
92+
pub(crate) struct GraphArgs {
93+
pub(crate) name: syn::Ident,
94+
pub(crate) expression_string: syn::LitStr,
95+
}
96+
97+
impl Parse for GraphArgs {
98+
fn parse(input: ParseStream) -> parse::Result<Self> {
99+
let name: syn::Ident = input.parse()?;
100+
let _comma: Token![=] = input.parse()?;
101+
let expression_string = input.parse()?;
102+
103+
Ok(Self {
104+
name,
105+
expression_string,
106+
})
107+
}
108+
}
109+
103110
// TODO: Clean up this mess
104111
fn expr_to_float_lit(e: syn::Expr) -> Result<syn::LitFloat, syn::Error> {
105112
let error_msg = "expected float or int literal";

macros/src/lib.rs

Lines changed: 81 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use proc_macro::{Span, TokenStream};
77
use quote::quote;
88
use syn::parse_macro_input;
99

10-
use crate::symbol::{MetricsSymbol, SettingSymbol};
10+
use crate::symbol::{GraphSymbol, MetricsSymbol, SettingSymbol};
1111

1212
mod args;
1313
mod cargo;
@@ -16,26 +16,21 @@ mod symbol;
1616
/// Create a Metric instance that will be shown in the probe-plotter utility's graph
1717
///
1818
/// ```
19-
/// make_metric!(NAME_AS_SHOWN_IN_GRAPH: DataType = defalt_value, "expression to convert from raw value (x) to the value to plot")
19+
/// make_metric!(NAME_AS_SHOWN_IN_GRAPH: DataType = defalt_value, "expression to convert from raw value (NAME_AS_SHOWN_IN_GRAPH) to the value to plot")
2020
/// ```
2121
///
2222
/// Note that similar to `cortex_m::singleton!`, this should only be called once per metric. The macro will only return Some() the first time, then None.
2323
///
2424
/// ```
25-
/// let mut metric_foo = probe_plotter::make_metric!(FOO: i32 = 0, "x * 3.0").unwrap();
25+
/// let mut metric_foo = probe_plotter::make_metric!(FOO: i32 = 0, "FOO * 3.0").unwrap();
2626
///
27-
/// metric_foo.set(42); // The value 42 will be available for the host after this call. The value will be plotted as x * 3 = 42 * 3 = 126
27+
/// metric_foo.set(42); // The value 42 will be available for the host after this call. The value will be plotted as FOO * 3 = 42 * 3 = 126
2828
/// ```
2929
#[proc_macro]
3030
pub fn make_metric(args: TokenStream) -> TokenStream {
3131
let args = parse_macro_input!(args as args::MetricArgs);
3232

33-
let sym_name = MetricsSymbol::new(
34-
args.ty.to_string(),
35-
args.name.to_string(),
36-
args.expression_string.value(),
37-
)
38-
.mangle();
33+
let sym_name = MetricsSymbol::new(args.ty.to_string(), args.name.to_string()).mangle();
3934

4035
let name = args.name;
4136
let ty = args.ty;
@@ -116,6 +111,68 @@ pub fn make_setting(args: TokenStream) -> TokenStream {
116111
.into()
117112
}
118113

114+
/// Register a graph for the probe-plotter utility's to plot.
115+
///
116+
/// This has access to all metrics and settings which may be used in the expression
117+
///
118+
/// ```
119+
/// make_graph!(GRAPH_TITLE = "expression to convert from raw values to the value to plot, may refer to any metrics or settings")
120+
/// ```
121+
///
122+
/// ```
123+
/// probe_plotter::make_graph!(FOO_GRAPH = "FOO * 3.0");
124+
///
125+
///
126+
/// let mut metric_foo = probe_plotter::make_metric!(FOO: i32 = 0).unwrap();
127+
///
128+
/// metric_foo.set(42); // The value 42 will be available for the host after this call. The value will be plotted as FOO * 3 = 42 * 3 = 126
129+
/// ```
130+
#[proc_macro]
131+
pub fn make_graph(args: TokenStream) -> TokenStream {
132+
let args = parse_macro_input!(args as args::GraphArgs);
133+
134+
let sym_name = GraphSymbol::new(args.name.to_string(), args.expression_string.value()).mangle();
135+
let section = linker_section(false, &sym_name);
136+
let section_for_macos = linker_section(true, &sym_name);
137+
138+
let name = args.name;
139+
quote!(
140+
#[cfg_attr(target_os = "macos", unsafe(link_section = #section_for_macos))]
141+
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = #section))]
142+
#[unsafe(export_name = #sym_name)]
143+
static mut #name: u8 = 42;
144+
145+
#[allow(unsafe_code)]
146+
unsafe {
147+
// TODO: Find better way to ensure the compiler considers this static used
148+
// the #[used] attribute does not seem enough
149+
let mut x = &raw const #name as usize;
150+
core::arch::asm!("mov {0}, {0}", inout(reg) x);
151+
}
152+
)
153+
.into()
154+
}
155+
/*
156+
quote!(
157+
#[cfg_attr(target_os = "macos", unsafe(link_section = #section_for_macos))]
158+
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = #section))]
159+
#[used]
160+
#[unsafe(export_name = #sym_name)]
161+
static #name: u8 = 0;
162+
)
163+
.into()
164+
165+
166+
let name = args.name;
167+
quote!(
168+
#[used]
169+
#[unsafe(export_name = #sym_name)]
170+
static mut #name: (i8, bool) =
171+
(0, false);
172+
)
173+
.into()
174+
*/
175+
119176
pub(crate) fn crate_local_disambiguator() -> u64 {
120177
// We want a deterministic, but unique-per-macro-invocation identifier. For that we
121178
// hash the call site `Span`'s debug representation, which contains a counter that
@@ -128,3 +185,17 @@ fn hash(string: &str) -> u64 {
128185
string.hash(&mut hasher);
129186
hasher.finish()
130187
}
188+
189+
/// work around restrictions on length and allowed characters imposed by macos linker
190+
/// returns (note the comma character for macos):
191+
/// under macos: ".defmt," + 16 character hex digest of symbol's hash
192+
/// otherwise: ".defmt." + prefix + symbol
193+
pub(crate) fn linker_section(for_macos: bool, symbol: &str) -> String {
194+
let mut sub_section = format!(".{symbol}");
195+
196+
if for_macos {
197+
sub_section = format!(",{:x}", hash(&sub_section));
198+
}
199+
200+
format!(".defmt{sub_section}")
201+
}

macros/src/symbol.rs

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,33 +18,28 @@ pub struct MetricsSymbol {
1818
/// Variable name
1919
name: String,
2020

21-
/// Expression used to calculate the value to plot
22-
expression_string: String,
23-
2421
/// Crate name obtained via CARGO_CRATE_NAME (added since a Cargo package can contain many crates).
2522
crate_name: String,
2623
}
2724

2825
impl MetricsSymbol {
29-
pub fn new(ty: String, name: String, expr: String) -> Self {
26+
pub fn new(ty: String, name: String) -> Self {
3027
Self {
3128
// `CARGO_PKG_NAME` is set to the invoking package's name.
3229
package: cargo::package_name(),
3330
disambiguator: super::crate_local_disambiguator(),
3431
ty,
3532
name,
36-
expression_string: expr,
3733
crate_name: cargo::crate_name(),
3834
}
3935
}
4036

4137
pub fn mangle(&self) -> String {
4238
format!(
43-
r#"{{"type":"Metric","package":"{}","ty":"{}","name":"{}","expr":"{}","disambiguator":"{}","crate_name":"{}"}}"#,
39+
r#"{{"type":"Metric","package":"{}","ty":"{}","name":"{}","disambiguator":"{}","crate_name":"{}"}}"#,
4440
json_escape(&self.package),
4541
json_escape(&self.ty),
4642
json_escape(&self.name),
47-
json_escape(&self.expression_string),
4843
self.disambiguator,
4944
json_escape(&self.crate_name),
5045
)
@@ -104,6 +99,48 @@ impl SettingSymbol {
10499
}
105100
}
106101

102+
pub struct GraphSymbol {
103+
/// Name of the Cargo package in which the symbol is being instantiated. Used for avoiding
104+
/// symbol name collisions.
105+
package: String,
106+
107+
/// Unique identifier that disambiguates otherwise equivalent invocations in the same crate.
108+
disambiguator: u64,
109+
110+
/// Variable name
111+
name: String,
112+
113+
/// Expression used to calculate the value to plot
114+
expression_string: String,
115+
116+
/// Crate name obtained via CARGO_CRATE_NAME (added since a Cargo package can contain many crates).
117+
crate_name: String,
118+
}
119+
120+
impl GraphSymbol {
121+
pub fn new(name: String, expr: String) -> Self {
122+
Self {
123+
// `CARGO_PKG_NAME` is set to the invoking package's name.
124+
package: cargo::package_name(),
125+
disambiguator: super::crate_local_disambiguator(),
126+
name,
127+
expression_string: expr,
128+
crate_name: cargo::crate_name(),
129+
}
130+
}
131+
132+
pub fn mangle(&self) -> String {
133+
format!(
134+
r#"{{"type":"Graph","package":"{}","name":"{}","expr":"{}","disambiguator":"{}","crate_name":"{}"}}"#,
135+
json_escape(&self.package),
136+
json_escape(&self.name),
137+
json_escape(&self.expression_string),
138+
self.disambiguator,
139+
json_escape(&self.crate_name),
140+
)
141+
}
142+
}
143+
107144
fn json_escape(string: &str) -> String {
108145
use std::fmt::Write;
109146

probe-plotter-tools/src/bin/viewer.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
use std::{sync::mpsc, thread, time::Duration};
44

5-
use probe_plotter_tools::{gui::MyApp, metric::Status, parse_elf_file, setting::Setting};
5+
use probe_plotter_tools::{graph::Status, gui::MyApp, parse_elf_file, setting::Setting};
66
use rerun::external::{eframe, re_crash_handler, re_grpc_server, re_log, re_viewer, tokio};
77
use shunting::MathContext;
88

@@ -16,7 +16,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
1616
.nth(2)
1717
.unwrap_or_else(|| "stm32g474retx".to_owned());
1818

19-
let (mut metrics, mut settings) = parse_elf_file(&elf_path);
19+
let (mut metrics, mut settings, mut graphs) = parse_elf_file(&elf_path);
20+
21+
dbg!(&metrics, &settings, &graphs);
2022

2123
let main_thread_token = rerun::MainThreadToken::i_promise_i_am_on_the_main_thread();
2224

@@ -65,20 +67,29 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
6567
initial_settings_sender.send(settings).unwrap();
6668

6769
let mut math_ctx = MathContext::new();
70+
let mut was_any_change = false;
6871
loop {
6972
for mut setting in settings_update_receiver.try_iter() {
7073
setting.write(setting.value, &mut core).unwrap();
7174
}
7275

7376
for m in &mut metrics {
7477
m.read(&mut core, &mut math_ctx).unwrap();
75-
let (x, s) = m.compute(&mut math_ctx);
76-
if let Status::New = s {
77-
rec.log(m.name.clone(), &rerun::Scalars::single(x)).unwrap();
78-
} else {
79-
std::thread::sleep(Duration::from_millis(1));
78+
}
79+
80+
let mut is_any_change = false;
81+
for g in &mut graphs {
82+
let (x, s) = g.compute(&mut math_ctx);
83+
if s == Status::New || was_any_change {
84+
// Force update of all values if one was changed last time
85+
rec.log(g.name.clone(), &rerun::Scalars::single(x)).unwrap();
86+
if s == Status::New {
87+
is_any_change = true;
88+
}
8089
}
8190
}
91+
was_any_change = is_any_change;
92+
std::thread::sleep(Duration::from_millis(1));
8293
}
8394
});
8495

0 commit comments

Comments
 (0)