From 9cacb540117a802e191aa67bb8944e41a45699f8 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 4 Aug 2025 02:14:03 +0200 Subject: [PATCH] Graph --- README.md | 18 ++-- examples/simple/src/main.rs | 15 ++- macros/src/args.rs | 35 ++++--- macros/src/lib.rs | 91 +++++++++++++++++-- macros/src/symbol.rs | 51 +++++++++-- .../src/bin/{viewer.rs => custom-viewer.rs} | 29 ++++-- .../src/{main.rs => bin/simple.rs} | 11 ++- probe-plotter-tools/src/graph.rs | 33 +++++++ probe-plotter-tools/src/lib.rs | 38 ++++---- probe-plotter-tools/src/metric.rs | 19 ---- probe-plotter-tools/src/setting.rs | 9 +- probe-plotter-tools/src/symbol.rs | 26 ++---- probe-plotter/src/lib.rs | 1 + 13 files changed, 267 insertions(+), 109 deletions(-) rename probe-plotter-tools/src/bin/{viewer.rs => custom-viewer.rs} (79%) rename probe-plotter-tools/src/{main.rs => bin/simple.rs} (78%) create mode 100644 probe-plotter-tools/src/graph.rs diff --git a/README.md b/README.md index b5036aa..bb907b1 100644 --- a/README.md +++ b/README.md @@ -10,15 +10,19 @@ 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 probe_plotter::{make_metric, make_setting}; +use probe_plotter::{make_graph, make_metric, make_setting}; #[entry] fn main() -> ! { - let mut sawtooth = make_metric!(SAWTOOTH: i32 = 42, "(SAWTOOTH / 10) % 100").unwrap(); - let mut sine = make_metric!(SINE: i32 = 42, "100 * sin(2 * pi * SINE / 4000)").unwrap(); + make_graph!(SAWTOOTH = "(SAWTOOTH / 10) % 100"); + make_graph!(SINE = "100 * sin(2 * pi * SINE / 4000)"); + make_graph!(SINE_TIMES_SAWTOOTH = "100 * sin(2 * pi * SINE / 4000) * (SAWTOOTH / 10) % 100)"); + make_graph!(SETTING = "SETTING"); + make_graph!(SETTING_ROUNDTRIP = "SETTING_ROUNDTRIP"); - let mut setting_roundtrip = - make_metric!(SETTING_ROUNDTRIP: i8 = 0, "SETTING_ROUNDTRIP").unwrap(); + let mut sawtooth = make_metric!(SAWTOOTH: i32 = 42).unwrap(); + let mut sine = make_metric!(SINE: i32 = 42).unwrap(); + let mut setting_roundtrip = make_metric!(SETTING_ROUNDTRIP: i8 = 0).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(); @@ -29,6 +33,8 @@ fn main() -> ! { sine.set(i); setting_roundtrip.set(setting.get()); + + cortex_m::asm::delay(100_000); } } } @@ -54,7 +60,7 @@ 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 +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 ``` diff --git a/examples/simple/src/main.rs b/examples/simple/src/main.rs index a79b1e2..2692c94 100644 --- a/examples/simple/src/main.rs +++ b/examples/simple/src/main.rs @@ -5,17 +5,22 @@ use cortex_m_rt::entry; use defmt_rtt as _; use panic_halt as _; -use probe_plotter::{make_metric, make_setting}; +use probe_plotter::{make_graph, make_metric, make_setting}; #[entry] fn main() -> ! { + make_graph!(SAWTOOTH = "(SAWTOOTH / 10) % 100"); + make_graph!(SINE = "100 * sin(2 * pi * SINE / 4000)"); + make_graph!(SINE_TIMES_SAWTOOTH = "100 * sin(2 * pi * SINE / 4000) * (SAWTOOTH / 10) % 100)"); + make_graph!(SETTING = "SETTING"); + make_graph!(SETTING_ROUNDTRIP = "SETTING_ROUNDTRIP"); + defmt::println!("Running..."); - let mut sawtooth = make_metric!(SAWTOOTH: i32 = 42, "(SAWTOOTH / 10) % 100").unwrap(); + let mut sawtooth = make_metric!(SAWTOOTH: i32 = 42).unwrap(); defmt::println!("foo initialized to: {}", sawtooth.get()); - let mut sine = make_metric!(SINE: i32 = 42, "100 * sin(2 * pi * SINE / 4000)").unwrap(); + let mut sine = make_metric!(SINE: i32 = 42).unwrap(); - let mut setting_roundtrip = - make_metric!(SETTING_ROUNDTRIP: i8 = 0, "SETTING_ROUNDTRIP").unwrap(); + let mut setting_roundtrip = make_metric!(SETTING_ROUNDTRIP: i8 = 0).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(); diff --git a/macros/src/args.rs b/macros/src/args.rs index 52b3e5c..7f7fb65 100644 --- a/macros/src/args.rs +++ b/macros/src/args.rs @@ -1,18 +1,16 @@ // Based on defmt use syn::{ - LitStr, RangeLimits, Token, + RangeLimits, Token, parse::{self, Parse, ParseStream}, spanned::Spanned, }; -//FOO: i32 = 0, "x * 3.0" -//FOO: i32 = 0 // defaults to "x" +//FOO: i32 = 0 pub(crate) struct MetricArgs { pub(crate) name: syn::Ident, pub(crate) ty: syn::Ident, pub(crate) initial_val: syn::Expr, - pub(crate) expression_string: syn::LitStr, } impl Parse for MetricArgs { @@ -23,20 +21,10 @@ impl Parse for MetricArgs { let _comma: Token![=] = input.parse()?; let initial_val = input.parse()?; - let comma: parse::Result = input.parse(); - let expression_string = input.parse(); - - let expression_string = match (comma, expression_string) { - (Ok(_), Ok(expr)) => expr, - (Ok(_), Err(e)) => return Err(e), - (Err(_), _) => LitStr::new(&name.to_string(), name.span()), - }; - Ok(Self { name, ty, initial_val, - expression_string, }) } } @@ -100,6 +88,25 @@ impl Parse for SettingArgs { } } +//FOO = "x * 3.0" +pub(crate) struct GraphArgs { + pub(crate) name: syn::Ident, + pub(crate) expression_string: syn::LitStr, +} + +impl Parse for GraphArgs { + fn parse(input: ParseStream) -> parse::Result { + let name: syn::Ident = input.parse()?; + let _comma: Token![=] = input.parse()?; + let expression_string = input.parse()?; + + Ok(Self { + name, + expression_string, + }) + } +} + // TODO: Clean up this mess fn expr_to_float_lit(e: syn::Expr) -> Result { let error_msg = "expected float or int literal"; diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 8673eeb..a993316 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -7,7 +7,7 @@ use proc_macro::{Span, TokenStream}; use quote::quote; use syn::parse_macro_input; -use crate::symbol::{MetricsSymbol, SettingSymbol}; +use crate::symbol::{GraphSymbol, MetricsSymbol, SettingSymbol}; mod args; mod cargo; @@ -16,26 +16,21 @@ mod symbol; /// Create a Metric instance that will be shown in the probe-plotter utility's graph /// /// ``` -/// make_metric!(NAME_AS_SHOWN_IN_GRAPH: DataType = defalt_value, "expression to convert from raw value (x) to the value to plot") +/// 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") /// ``` /// /// 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. /// /// ``` -/// let mut metric_foo = probe_plotter::make_metric!(FOO: i32 = 0, "x * 3.0").unwrap(); +/// let mut metric_foo = probe_plotter::make_metric!(FOO: i32 = 0, "FOO * 3.0").unwrap(); /// -/// 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 +/// 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 /// ``` #[proc_macro] pub fn make_metric(args: TokenStream) -> TokenStream { let args = parse_macro_input!(args as args::MetricArgs); - let sym_name = MetricsSymbol::new( - args.ty.to_string(), - args.name.to_string(), - args.expression_string.value(), - ) - .mangle(); + let sym_name = MetricsSymbol::new(args.ty.to_string(), args.name.to_string()).mangle(); let name = args.name; let ty = args.ty; @@ -116,6 +111,68 @@ pub fn make_setting(args: TokenStream) -> TokenStream { .into() } +/// Register a graph for the probe-plotter utility's to plot. +/// +/// This has access to all metrics and settings which may be used in the expression +/// +/// ``` +/// make_graph!(GRAPH_TITLE = "expression to convert from raw values to the value to plot, may refer to any metrics or settings") +/// ``` +/// +/// ``` +/// probe_plotter::make_graph!(FOO_GRAPH = "FOO * 3.0"); +/// +/// +/// let mut metric_foo = probe_plotter::make_metric!(FOO: i32 = 0).unwrap(); +/// +/// 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 +/// ``` +#[proc_macro] +pub fn make_graph(args: TokenStream) -> TokenStream { + let args = parse_macro_input!(args as args::GraphArgs); + + let sym_name = GraphSymbol::new(args.name.to_string(), args.expression_string.value()).mangle(); + let section = linker_section(false, &sym_name); + let section_for_macos = linker_section(true, &sym_name); + + let name = args.name; + quote!( + #[cfg_attr(target_os = "macos", unsafe(link_section = #section_for_macos))] + #[cfg_attr(not(target_os = "macos"), unsafe(link_section = #section))] + #[unsafe(export_name = #sym_name)] + static mut #name: u8 = 42; + + #[allow(unsafe_code)] + unsafe { + // TODO: Find better way to ensure the compiler considers this static used + // the #[used] attribute does not seem enough + let mut x = &raw const #name as usize; + core::arch::asm!("mov {0}, {0}", inout(reg) x); + } + ) + .into() +} +/* +quote!( + #[cfg_attr(target_os = "macos", unsafe(link_section = #section_for_macos))] + #[cfg_attr(not(target_os = "macos"), unsafe(link_section = #section))] + #[used] + #[unsafe(export_name = #sym_name)] + static #name: u8 = 0; + ) + .into() + + +let name = args.name; + quote!( + #[used] + #[unsafe(export_name = #sym_name)] + static mut #name: (i8, bool) = + (0, false); + ) + .into() + */ + pub(crate) fn crate_local_disambiguator() -> u64 { // We want a deterministic, but unique-per-macro-invocation identifier. For that we // hash the call site `Span`'s debug representation, which contains a counter that @@ -128,3 +185,17 @@ fn hash(string: &str) -> u64 { string.hash(&mut hasher); hasher.finish() } + +/// work around restrictions on length and allowed characters imposed by macos linker +/// returns (note the comma character for macos): +/// under macos: ".defmt," + 16 character hex digest of symbol's hash +/// otherwise: ".defmt." + prefix + symbol +pub(crate) fn linker_section(for_macos: bool, symbol: &str) -> String { + let mut sub_section = format!(".{symbol}"); + + if for_macos { + sub_section = format!(",{:x}", hash(&sub_section)); + } + + format!(".defmt{sub_section}") +} diff --git a/macros/src/symbol.rs b/macros/src/symbol.rs index 86fe30c..9bf05e3 100644 --- a/macros/src/symbol.rs +++ b/macros/src/symbol.rs @@ -18,33 +18,28 @@ pub struct MetricsSymbol { /// Variable name name: String, - /// Expression used to calculate the value to plot - expression_string: String, - /// Crate name obtained via CARGO_CRATE_NAME (added since a Cargo package can contain many crates). crate_name: String, } impl MetricsSymbol { - pub fn new(ty: String, name: String, expr: String) -> Self { + pub fn new(ty: String, name: String) -> Self { Self { // `CARGO_PKG_NAME` is set to the invoking package's name. package: cargo::package_name(), disambiguator: super::crate_local_disambiguator(), ty, name, - expression_string: expr, crate_name: cargo::crate_name(), } } pub fn mangle(&self) -> String { format!( - r#"{{"type":"Metric","package":"{}","ty":"{}","name":"{}","expr":"{}","disambiguator":"{}","crate_name":"{}"}}"#, + r#"{{"type":"Metric","package":"{}","ty":"{}","name":"{}","disambiguator":"{}","crate_name":"{}"}}"#, json_escape(&self.package), json_escape(&self.ty), json_escape(&self.name), - json_escape(&self.expression_string), self.disambiguator, json_escape(&self.crate_name), ) @@ -104,6 +99,48 @@ impl SettingSymbol { } } +pub struct GraphSymbol { + /// Name of the Cargo package in which the symbol is being instantiated. Used for avoiding + /// symbol name collisions. + package: String, + + /// Unique identifier that disambiguates otherwise equivalent invocations in the same crate. + disambiguator: u64, + + /// Variable name + name: String, + + /// Expression used to calculate the value to plot + expression_string: String, + + /// Crate name obtained via CARGO_CRATE_NAME (added since a Cargo package can contain many crates). + crate_name: String, +} + +impl GraphSymbol { + pub fn new(name: String, expr: String) -> Self { + Self { + // `CARGO_PKG_NAME` is set to the invoking package's name. + package: cargo::package_name(), + disambiguator: super::crate_local_disambiguator(), + name, + expression_string: expr, + crate_name: cargo::crate_name(), + } + } + + pub fn mangle(&self) -> String { + format!( + r#"{{"type":"Graph","package":"{}","name":"{}","expr":"{}","disambiguator":"{}","crate_name":"{}"}}"#, + json_escape(&self.package), + json_escape(&self.name), + json_escape(&self.expression_string), + self.disambiguator, + json_escape(&self.crate_name), + ) + } +} + fn json_escape(string: &str) -> String { use std::fmt::Write; diff --git a/probe-plotter-tools/src/bin/viewer.rs b/probe-plotter-tools/src/bin/custom-viewer.rs similarity index 79% rename from probe-plotter-tools/src/bin/viewer.rs rename to probe-plotter-tools/src/bin/custom-viewer.rs index caf31bf..0081d0f 100644 --- a/probe-plotter-tools/src/bin/viewer.rs +++ b/probe-plotter-tools/src/bin/custom-viewer.rs @@ -2,7 +2,7 @@ use std::{sync::mpsc, thread, time::Duration}; -use probe_plotter_tools::{gui::MyApp, metric::Status, parse_elf_file, setting::Setting}; +use probe_plotter_tools::{graph::Status, gui::MyApp, parse_elf_file, setting::Setting}; use rerun::external::{eframe, re_crash_handler, re_grpc_server, re_log, re_viewer, tokio}; use shunting::MathContext; @@ -16,7 +16,9 @@ async fn main() -> Result<(), Box> { .nth(2) .unwrap_or_else(|| "stm32g474retx".to_owned()); - let (mut metrics, mut settings) = parse_elf_file(&elf_path); + let (mut metrics, mut settings, mut graphs) = parse_elf_file(&elf_path); + + dbg!(&metrics, &settings, &graphs); let main_thread_token = rerun::MainThreadToken::i_promise_i_am_on_the_main_thread(); @@ -65,20 +67,31 @@ async fn main() -> Result<(), Box> { initial_settings_sender.send(settings).unwrap(); let mut math_ctx = MathContext::new(); + let mut was_any_change = false; loop { for mut setting in settings_update_receiver.try_iter() { - setting.write(setting.value, &mut core).unwrap(); + setting + .write(setting.value, &mut core, &mut math_ctx) + .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)); + } + + let mut is_any_change = false; + for g in &mut graphs { + let (x, s) = g.compute(&math_ctx); + if s == Status::New || was_any_change { + // Force update of all values if one was changed last time + rec.log(g.name.clone(), &rerun::Scalars::single(x)).unwrap(); + if s == Status::New { + is_any_change = true; + } } } + was_any_change = is_any_change; + std::thread::sleep(Duration::from_millis(1)); } }); diff --git a/probe-plotter-tools/src/main.rs b/probe-plotter-tools/src/bin/simple.rs similarity index 78% rename from probe-plotter-tools/src/main.rs rename to probe-plotter-tools/src/bin/simple.rs index 851a117..58abfcc 100644 --- a/probe-plotter-tools/src/main.rs +++ b/probe-plotter-tools/src/bin/simple.rs @@ -1,4 +1,4 @@ -use probe_plotter_tools::{metric::Status, parse_elf_file}; +use probe_plotter_tools::{graph::Status, parse_elf_file}; use shunting::MathContext; use std::time::Duration; @@ -14,7 +14,7 @@ fn main() { let mut session = probe_rs::Session::auto_attach(target, Default::default()).unwrap(); let mut core = session.core(0).unwrap(); - let (mut metrics, _settings) = parse_elf_file(&elf_path); + let (mut metrics, _settings, mut graphs) = parse_elf_file(&elf_path); for m in &metrics { println!("{}: {}", m.name, m.address); } @@ -31,9 +31,12 @@ fn main() { loop { for m in &mut metrics { m.read(&mut core, &mut math_ctx).unwrap(); - let (x, s) = m.compute(&mut math_ctx); + } + + for g in &graphs { + let (x, s) = g.compute(&mut math_ctx); if let Status::New = s { - rec.log(m.name.clone(), &rerun::Scalars::single(x)).unwrap(); + rec.log(g.name.clone(), &rerun::Scalars::single(x)).unwrap(); } else { std::thread::sleep(Duration::from_millis(1)); } diff --git a/probe-plotter-tools/src/graph.rs b/probe-plotter-tools/src/graph.rs new file mode 100644 index 0000000..cc3dd00 --- /dev/null +++ b/probe-plotter-tools/src/graph.rs @@ -0,0 +1,33 @@ +use shunting::MathContext; +use std::fmt; + +pub struct Graph { + pub name: String, + pub expr: shunting::RPNExpr, + pub last_value: f64, +} + +impl fmt::Debug for Graph { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Graph").field("expr", &self.expr).finish() + } +} + +#[derive(Debug, PartialEq)] +pub enum Status { + SameAsLast, + New, +} + +impl Graph { + pub fn compute(&mut self, math_ctx: &MathContext) -> (f64, Status) { + let new = math_ctx.eval(&self.expr).unwrap(); + let status = if new == self.last_value { + Status::SameAsLast + } else { + Status::New + }; + self.last_value = new; + (new, status) + } +} diff --git a/probe-plotter-tools/src/lib.rs b/probe-plotter-tools/src/lib.rs index dad1840..daf246d 100644 --- a/probe-plotter-tools/src/lib.rs +++ b/probe-plotter-tools/src/lib.rs @@ -1,3 +1,4 @@ +pub mod graph; pub mod gui; pub mod metric; pub mod setting; @@ -10,7 +11,7 @@ use probe_rs::{Core, MemoryInterface}; use serde::Deserialize; use shunting::{MathContext, ShuntingParser}; -use crate::{metric::Metric, setting::Setting, symbol::Symbol}; +use crate::{graph::Graph, metric::Metric, setting::Setting, symbol::Symbol}; pub fn read_value(core: &mut Core, address: u64, ty: Type) -> Result { let x = match ty { @@ -41,11 +42,12 @@ pub enum Type { } // Most of this is taken from https://github.com/knurling-rs/defmt/blob/8e517f8d7224237893e39337a61de8ef98b341f2/decoder/src/elf2table/mod.rs and modified -pub fn parse(elf_bytes: &[u8]) -> (Vec, Vec) { +pub fn parse(elf_bytes: &[u8]) -> (Vec, Vec, Vec) { let elf = object::File::parse(elf_bytes).unwrap(); let mut metrics = Vec::new(); let mut settings = Vec::new(); + let mut graphs = Vec::new(); for entry in elf.symbols() { let Ok(name) = entry.name() else { @@ -59,19 +61,11 @@ pub fn parse(elf_bytes: &[u8]) -> (Vec, Vec) { // TODO: Why does this assert not succeed? //assert_eq!(entry.size(), 4); match sym { - Symbol::Metric { name, expr, ty } => { - let expr = ShuntingParser::parse_str(&expr).unwrap(); - let math_ctx = MathContext::new(); - math_ctx.setvar(&name, shunting::MathOp::Number(0.0)); - math_ctx - .eval(&expr) - .expect("Use the metrics name as name for the value in the expression"); + Symbol::Metric { name, ty } => { metrics.push(Metric { name, - expr, ty, address: entry.address(), - last_value: f64::NAN, }); } Symbol::Setting { @@ -89,20 +83,32 @@ pub fn parse(elf_bytes: &[u8]) -> (Vec, Vec) { step_size, }); } + Symbol::Graph { name, expr } => { + let expr = ShuntingParser::parse_str(&expr).unwrap(); + let math_ctx = MathContext::new(); + math_ctx.setvar(&name, shunting::MathOp::Number(0.0)); + math_ctx + .eval(&expr) + .expect("Use the metrics name as name for the value in the expression"); + + graphs.push(Graph { + name, + expr, + last_value: f64::NAN, + }) + } } } - (metrics, settings) + (metrics, settings, graphs) } -pub fn parse_elf_file(elf_path: &str) -> (Vec, Vec) { +pub fn parse_elf_file(elf_path: &str) -> (Vec, Vec, Vec) { let mut buffer = Vec::new(); std::fs::File::open(elf_path) .unwrap() .read_to_end(&mut buffer) .unwrap(); - let (metrics, settings) = parse(&buffer); - - (metrics, settings) + parse(&buffer) } diff --git a/probe-plotter-tools/src/metric.rs b/probe-plotter-tools/src/metric.rs index 80e656b..68508cc 100644 --- a/probe-plotter-tools/src/metric.rs +++ b/probe-plotter-tools/src/metric.rs @@ -5,28 +5,20 @@ use crate::{Type, read_value}; pub struct Metric { pub name: String, - pub expr: shunting::RPNExpr, pub ty: Type, pub address: u64, - pub last_value: f64, } impl fmt::Debug for Metric { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Metric") .field("name", &self.name) - .field("expr", &self.expr) .field("ty", &self.ty) .field("address", &self.address) .finish() } } -pub enum Status { - SameAsLast, - New, -} - impl Metric { pub fn read( &mut self, @@ -38,15 +30,4 @@ impl Metric { Ok(()) } - - pub fn compute(&mut self, math_ctx: &mut MathContext) -> (f64, Status) { - let new = math_ctx.eval(&self.expr).unwrap(); - let status = if new == self.last_value { - Status::SameAsLast - } else { - Status::New - }; - self.last_value = new; - (new, status) - } } diff --git a/probe-plotter-tools/src/setting.rs b/probe-plotter-tools/src/setting.rs index ad564d6..8e7e99d 100644 --- a/probe-plotter-tools/src/setting.rs +++ b/probe-plotter-tools/src/setting.rs @@ -1,6 +1,7 @@ use std::ops::RangeInclusive; use probe_rs::MemoryInterface; +use shunting::MathContext; use crate::{Type, read_value}; @@ -20,7 +21,13 @@ impl Setting { Ok(()) } - pub fn write(&mut self, x: f64, core: &mut probe_rs::Core) -> Result<(), probe_rs::Error> { + pub fn write( + &mut self, + x: f64, + core: &mut probe_rs::Core, + math_ctx: &mut MathContext, + ) -> Result<(), probe_rs::Error> { + math_ctx.setvar(&self.name, shunting::MathOp::Number(x)); match self.ty { Type::u8 => core.write_word_8( self.address, diff --git a/probe-plotter-tools/src/symbol.rs b/probe-plotter-tools/src/symbol.rs index 618b56c..2acfbd2 100644 --- a/probe-plotter-tools/src/symbol.rs +++ b/probe-plotter-tools/src/symbol.rs @@ -9,9 +9,6 @@ pub enum Symbol { Metric { name: String, - /// Exproession to apply before plotting - expr: String, - /// Type of value, i32, u8 etc. ty: Type, }, @@ -27,28 +24,19 @@ pub enum Symbol { /// Step size step_size: f64, }, -} + Graph { + name: String, -impl Symbol { - pub fn name(&self) -> &str { - match self { - Symbol::Metric { name, .. } => name, - Symbol::Setting { name, .. } => name, - } - } - pub fn ty(&self) -> Type { - match self { - Symbol::Metric { ty, .. } => *ty, - Symbol::Setting { ty, .. } => *ty, - } - } + /// Exproession to apply before plotting + expr: String, + }, } #[derive(Debug)] pub struct InvalidSymbolError; impl Symbol { - pub fn demangle(raw: &str) -> Result { - serde_json::from_str(raw).map_err(|_| InvalidSymbolError) + pub fn demangle(raw: &str) -> Result { + serde_json::from_str(raw) } } diff --git a/probe-plotter/src/lib.rs b/probe-plotter/src/lib.rs index 7883029..ef9d8ae 100644 --- a/probe-plotter/src/lib.rs +++ b/probe-plotter/src/lib.rs @@ -3,5 +3,6 @@ pub mod metric; pub mod setting; +pub use macros::make_graph; pub use metric::{Metric, make_metric}; pub use setting::{Setting, make_setting};