minot-cli 0.5.0__tar.gz → 0.5.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {minot_cli-0.5.0 → minot_cli-0.5.2}/Cargo.lock +15 -8
- {minot_cli-0.5.0 → minot_cli-0.5.2}/Cargo.toml +14 -15
- {minot_cli-0.5.0 → minot_cli-0.5.2}/PKG-INFO +1 -1
- {minot_cli-0.5.0 → minot_cli-0.5.2}/minot/Cargo.toml +29 -5
- {minot_cli-0.5.0 → minot_cli-0.5.2}/minot/src/app.rs +1 -1
- {minot_cli-0.5.0 → minot_cli-0.5.2}/minot/src/coord.rs +2 -1
- {minot_cli-0.5.0 → minot_cli-0.5.2}/minot/src/minot.rs +46 -5
- {minot_cli-0.5.0 → minot_cli-0.5.2}/minot/src/runner.rs +39 -7
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_sea/Cargo.toml +1 -1
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_sea/src/client.rs +320 -71
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_wind/Cargo.toml +2 -2
- {minot_cli-0.5.0 → minot_cli-0.5.2}/ratpub/Cargo.toml +2 -2
- {minot_cli-0.5.0 → minot_cli-0.5.2}/minot/README.md +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/minot/build.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/minot/src/event.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/minot/src/handler.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/minot/src/tui.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/minot/src/ui.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_bagread/Cargo.toml +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_bagread/README.md +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_bagread/src/lib.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_bagread/src/qos.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_bagread/test/metadata-v5.yml +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_bagread/test/metadata-v9.yml +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_mtc/Cargo.toml +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_mtc/README.md +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_mtc/src/lib.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_net/Cargo.toml +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_net/README.md +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_net/src/lib.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_sea/README.md +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_sea/src/coordinator.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_sea/src/lib.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_sea/src/net.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_sea/src/ship.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_wind/README.md +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_wind/src/lib.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_wind/src/ratpub.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_wind/src/ros1.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_wind/src/ros2.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/mt_wind/src/ros2_r2r.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/pyproject.toml +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/ratpub/README.md +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/ratpub/examples/pub.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/ratpub/examples/smol.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/ratpub/examples/sub.rs +0 -0
- {minot_cli-0.5.0 → minot_cli-0.5.2}/ratpub/src/lib.rs +0 -0
|
@@ -527,6 +527,12 @@ dependencies = [
|
|
|
527
527
|
"serde",
|
|
528
528
|
]
|
|
529
529
|
|
|
530
|
+
[[package]]
|
|
531
|
+
name = "bytesize"
|
|
532
|
+
version = "2.3.1"
|
|
533
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
534
|
+
checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3"
|
|
535
|
+
|
|
530
536
|
[[package]]
|
|
531
537
|
name = "camino"
|
|
532
538
|
version = "1.2.2"
|
|
@@ -2487,9 +2493,10 @@ dependencies = [
|
|
|
2487
2493
|
|
|
2488
2494
|
[[package]]
|
|
2489
2495
|
name = "minot"
|
|
2490
|
-
version = "0.5.
|
|
2496
|
+
version = "0.5.2"
|
|
2491
2497
|
dependencies = [
|
|
2492
2498
|
"anyhow",
|
|
2499
|
+
"bytesize",
|
|
2493
2500
|
"chrono",
|
|
2494
2501
|
"clap",
|
|
2495
2502
|
"clap_complete",
|
|
@@ -2583,7 +2590,7 @@ dependencies = [
|
|
|
2583
2590
|
|
|
2584
2591
|
[[package]]
|
|
2585
2592
|
name = "mt_bagread"
|
|
2586
|
-
version = "0.5.
|
|
2593
|
+
version = "0.5.2"
|
|
2587
2594
|
dependencies = [
|
|
2588
2595
|
"anyhow",
|
|
2589
2596
|
"camino",
|
|
@@ -2603,7 +2610,7 @@ dependencies = [
|
|
|
2603
2610
|
|
|
2604
2611
|
[[package]]
|
|
2605
2612
|
name = "mt_mtc"
|
|
2606
|
-
version = "0.5.
|
|
2613
|
+
version = "0.5.2"
|
|
2607
2614
|
dependencies = [
|
|
2608
2615
|
"anyhow",
|
|
2609
2616
|
"ariadne",
|
|
@@ -2616,7 +2623,7 @@ dependencies = [
|
|
|
2616
2623
|
|
|
2617
2624
|
[[package]]
|
|
2618
2625
|
name = "mt_net"
|
|
2619
|
-
version = "0.5.
|
|
2626
|
+
version = "0.5.2"
|
|
2620
2627
|
dependencies = [
|
|
2621
2628
|
"anyhow",
|
|
2622
2629
|
"rkyv 0.8.15",
|
|
@@ -2626,7 +2633,7 @@ dependencies = [
|
|
|
2626
2633
|
|
|
2627
2634
|
[[package]]
|
|
2628
2635
|
name = "mt_rat"
|
|
2629
|
-
version = "0.5.
|
|
2636
|
+
version = "0.5.2"
|
|
2630
2637
|
dependencies = [
|
|
2631
2638
|
"anyhow",
|
|
2632
2639
|
"async-trait",
|
|
@@ -2641,7 +2648,7 @@ dependencies = [
|
|
|
2641
2648
|
|
|
2642
2649
|
[[package]]
|
|
2643
2650
|
name = "mt_sea"
|
|
2644
|
-
version = "0.5.
|
|
2651
|
+
version = "0.5.2"
|
|
2645
2652
|
dependencies = [
|
|
2646
2653
|
"anyhow",
|
|
2647
2654
|
"async-trait",
|
|
@@ -2657,7 +2664,7 @@ dependencies = [
|
|
|
2657
2664
|
|
|
2658
2665
|
[[package]]
|
|
2659
2666
|
name = "mt_wind"
|
|
2660
|
-
version = "0.5.
|
|
2667
|
+
version = "0.5.2"
|
|
2661
2668
|
dependencies = [
|
|
2662
2669
|
"anyhow",
|
|
2663
2670
|
"async-trait",
|
|
@@ -3781,7 +3788,7 @@ dependencies = [
|
|
|
3781
3788
|
|
|
3782
3789
|
[[package]]
|
|
3783
3790
|
name = "ratpub"
|
|
3784
|
-
version = "0.5.
|
|
3791
|
+
version = "0.5.2"
|
|
3785
3792
|
dependencies = [
|
|
3786
3793
|
"anyhow",
|
|
3787
3794
|
"async-trait",
|
|
@@ -4,15 +4,13 @@ members = ["minot", "ratpub", "mt_bagread", "mt_mtc", "mt_sea", "mt_wind", "mt_n
|
|
|
4
4
|
resolver = "3"
|
|
5
5
|
|
|
6
6
|
[workspace.package]
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.2"
|
|
8
8
|
keywords = ["robotics"]
|
|
9
9
|
license = "MIT OR Apache-2.0"
|
|
10
10
|
repository = "https://codeberg.org/stelzo/minot"
|
|
11
11
|
edition = "2024"
|
|
12
12
|
authors = ["Christopher Sieh <stelzo@steado.de>"]
|
|
13
|
-
categories = [
|
|
14
|
-
"science::robotics",
|
|
15
|
-
]
|
|
13
|
+
categories = ["science::robotics"]
|
|
16
14
|
documentation = "https://uos.github.io/minot"
|
|
17
15
|
homepage = "https://uos.github.io/minot"
|
|
18
16
|
rust-version = "1.85"
|
|
@@ -30,12 +28,12 @@ exclude = [
|
|
|
30
28
|
]
|
|
31
29
|
|
|
32
30
|
[workspace.dependencies]
|
|
33
|
-
mt_sea = { path = "mt_sea", version = "0.5.
|
|
34
|
-
ratpub = { path = "ratpub", version = "0.5.
|
|
35
|
-
mt_bagread = { path = "mt_bagread", version = "0.5.
|
|
36
|
-
mt_net = { path = "mt_net", version = "0.5.
|
|
37
|
-
mt_wind = { path = "mt_wind", version = "0.5.
|
|
38
|
-
mt_mtc = { path = "mt_mtc", version = "0.5.
|
|
31
|
+
mt_sea = { path = "mt_sea", version = "0.5.2" }
|
|
32
|
+
ratpub = { path = "ratpub", version = "0.5.2" }
|
|
33
|
+
mt_bagread = { path = "mt_bagread", version = "0.5.2" }
|
|
34
|
+
mt_net = { path = "mt_net", version = "0.5.2" }
|
|
35
|
+
mt_wind = { path = "mt_wind", version = "0.5.2" }
|
|
36
|
+
mt_mtc = { path = "mt_mtc", version = "0.5.2" }
|
|
39
37
|
|
|
40
38
|
# Tools
|
|
41
39
|
anyhow = "1.0"
|
|
@@ -51,7 +49,7 @@ futures = "0.3.31"
|
|
|
51
49
|
ratatui = "0.29.0"
|
|
52
50
|
rosbag2-rs = "0.2.1"
|
|
53
51
|
chrono = "0.4.40"
|
|
54
|
-
tui-logger = "0.16.0"
|
|
52
|
+
tui-logger = "0.16.0"
|
|
55
53
|
regex = "1.12"
|
|
56
54
|
once_cell = "1.21"
|
|
57
55
|
rust_decimal = "1.37.1"
|
|
@@ -64,6 +62,7 @@ serde_yml = "0.0.12"
|
|
|
64
62
|
cdr = "0.2.4"
|
|
65
63
|
rand = "0.8"
|
|
66
64
|
cdr-encoding = "0.10"
|
|
65
|
+
bytesize = "2.3"
|
|
67
66
|
|
|
68
67
|
# IO
|
|
69
68
|
futures-lite = "2.6"
|
|
@@ -97,10 +96,10 @@ ros2-interfaces-jazzy-rkyv = { version = "0.0.5", features = [
|
|
|
97
96
|
roslibrust = { version = "0.18", features = ["ros1", "macro"] }
|
|
98
97
|
|
|
99
98
|
[profile.release]
|
|
100
|
-
lto = "fat"
|
|
101
|
-
codegen-units = 1
|
|
102
|
-
strip = "symbols"
|
|
103
|
-
opt-level = "s"
|
|
99
|
+
lto = "fat" # Link Time Optimization
|
|
100
|
+
codegen-units = 1 # Slower compile times but compiler sees all generated code and can optimize over everything across modules. Also decreases binary size.
|
|
101
|
+
strip = "symbols" # Significantly reduce binary size
|
|
102
|
+
opt-level = "s" # Optimize for size but keep some performance
|
|
104
103
|
debug = 0
|
|
105
104
|
|
|
106
105
|
[profile.colcon]
|
|
@@ -38,6 +38,7 @@ clap_complete.workspace = true
|
|
|
38
38
|
regex.workspace = true
|
|
39
39
|
once_cell.workspace = true
|
|
40
40
|
rust_decimal.workspace = true
|
|
41
|
+
bytesize.workspace = true
|
|
41
42
|
|
|
42
43
|
[[bin]]
|
|
43
44
|
name = "minot"
|
|
@@ -47,12 +48,11 @@ path = "src/minot.rs"
|
|
|
47
48
|
name = "minot-coord"
|
|
48
49
|
path = "src/coord.rs"
|
|
49
50
|
|
|
50
|
-
# Note: All following features only embed existing nodes that can be built and run separately through the network.
|
|
51
51
|
[features]
|
|
52
|
-
default = ["embed-coord"
|
|
52
|
+
default = ["embed-coord"]
|
|
53
53
|
|
|
54
|
-
# Enable shared memory for zero-copy transfer of large messages (same-machine only)
|
|
55
|
-
|
|
54
|
+
# Experimental: Enable shared memory for zero-copy transfer of large messages (same-machine only)
|
|
55
|
+
shm = ["mt_sea/shm", "mt_wind?/shm"]
|
|
56
56
|
|
|
57
57
|
# Use the official C API for ROS2 publishing (r2r crate). Enable for maximum compatibility at the cost of needing an actively sourced ROS2 installation.
|
|
58
58
|
embed-ros2-c = ["embed-ros2-c-turbine"]
|
|
@@ -77,6 +77,30 @@ embed-ros1-turbine = ["dep:mt_wind", "mt_wind/ros1-native"]
|
|
|
77
77
|
embed-ratpub-turbine = ["dep:mt_wind", "mt_wind/ratpub"]
|
|
78
78
|
|
|
79
79
|
[package.metadata.docs.rs]
|
|
80
|
-
features = [
|
|
80
|
+
features = [
|
|
81
|
+
"embed-coord",
|
|
82
|
+
"embed-ratpub",
|
|
83
|
+
"embed-ros1-native",
|
|
84
|
+
"embed-ros2-c",
|
|
85
|
+
"embed-ros2-c-humble",
|
|
86
|
+
"embed-ros2-native",
|
|
87
|
+
]
|
|
81
88
|
default-target = "x86_64-unknown-linux-gnu"
|
|
82
89
|
rustdoc-args = ["--cfg", "docsrs"]
|
|
90
|
+
|
|
91
|
+
[package.metadata.deb]
|
|
92
|
+
name = "minot"
|
|
93
|
+
section = "science"
|
|
94
|
+
priority = "optional"
|
|
95
|
+
assets = [
|
|
96
|
+
[
|
|
97
|
+
"target/release/minot",
|
|
98
|
+
"usr/bin/minot",
|
|
99
|
+
"755",
|
|
100
|
+
],
|
|
101
|
+
[
|
|
102
|
+
"target/release/minot-coord",
|
|
103
|
+
"usr/bin/minot-coord",
|
|
104
|
+
"755",
|
|
105
|
+
],
|
|
106
|
+
]
|
|
@@ -700,7 +700,8 @@ pub fn get_clients(eval: &Evaluated) -> anyhow::Result<HashSet<String>> {
|
|
|
700
700
|
pub async fn main() -> anyhow::Result<()> {
|
|
701
701
|
mt_sea::init_logging();
|
|
702
702
|
|
|
703
|
-
let
|
|
703
|
+
let args = Args::parse();
|
|
704
|
+
let filepath = args.file;
|
|
704
705
|
|
|
705
706
|
let eval = if let Some(fp) = filepath {
|
|
706
707
|
let rules_file = std::fs::canonicalize(&fp)?;
|
|
@@ -39,12 +39,32 @@ const EMBED_RATPUB_TURBINE_NAME: &'static str = "embedded_ratpub_turbine";
|
|
|
39
39
|
#[derive(Debug, Clone, Subcommand)]
|
|
40
40
|
pub enum ServeCommand {}
|
|
41
41
|
|
|
42
|
+
#[cfg(feature = "shm")]
|
|
43
|
+
use bytesize::ByteSize;
|
|
44
|
+
|
|
42
45
|
#[derive(Parser, Debug)]
|
|
43
46
|
#[command(version, about, author, long_about = None)]
|
|
44
47
|
/// Minot — A versatile toolset for debugging and verifying stateful robot perception software.
|
|
45
48
|
pub(crate) struct Args {
|
|
46
49
|
#[command(subcommand)]
|
|
47
50
|
pub command: Commands,
|
|
51
|
+
|
|
52
|
+
/// Disable POSIX shared memory for zero-copy transfer.
|
|
53
|
+
/// SHM is enabled by default for better performance with large messages.
|
|
54
|
+
#[cfg(feature = "shm")]
|
|
55
|
+
#[arg(long, global = true)]
|
|
56
|
+
pub no_shm: bool,
|
|
57
|
+
|
|
58
|
+
/// Size of the shared memory buffer pool (e.g., "7mb", "64MiB", "1gb").
|
|
59
|
+
/// Default is 7MB which is within typical Linux /dev/shm limits.
|
|
60
|
+
#[cfg(feature = "shm")]
|
|
61
|
+
#[arg(long, global = true, default_value = "7mb", value_parser = parse_bytesize)]
|
|
62
|
+
pub shm_size: ByteSize,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
#[cfg(feature = "shm")]
|
|
66
|
+
fn parse_bytesize(s: &str) -> Result<ByteSize, String> {
|
|
67
|
+
s.parse::<ByteSize>().map_err(|e| e.to_string())
|
|
48
68
|
}
|
|
49
69
|
|
|
50
70
|
#[derive(Debug, Clone, Parser)]
|
|
@@ -115,8 +135,11 @@ async fn tui(path: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
|
|
|
115
135
|
Ok(log::LevelFilter::Info)
|
|
116
136
|
}?;
|
|
117
137
|
tui_logger::init_logger(log_level).unwrap();
|
|
118
|
-
// tui_logger::set_env_filter_from_string(&log_level);
|
|
119
138
|
tui_logger::set_default_level(log_level);
|
|
139
|
+
tui_logger::set_level_for_target("zenoh", log::LevelFilter::Warn);
|
|
140
|
+
tui_logger::set_level_for_target("zenoh_transport", log::LevelFilter::Warn);
|
|
141
|
+
tui_logger::set_level_for_target("zenoh_link", log::LevelFilter::Warn);
|
|
142
|
+
tui_logger::set_level_for_target("zenoh_protocol", log::LevelFilter::Warn);
|
|
120
143
|
|
|
121
144
|
#[cfg(feature = "embed-coord")]
|
|
122
145
|
{
|
|
@@ -671,9 +694,11 @@ struct StdioLogger {
|
|
|
671
694
|
|
|
672
695
|
impl log::Log for StdioLogger {
|
|
673
696
|
fn enabled(&self, metadata: &log::Metadata) -> bool {
|
|
674
|
-
//
|
|
675
|
-
|
|
676
|
-
|
|
697
|
+
// Filter out verbose zenoh logs (only show warnings and errors)
|
|
698
|
+
let target = metadata.target();
|
|
699
|
+
if target.starts_with("zenoh") && metadata.level() > log::Level::Warn {
|
|
700
|
+
return false;
|
|
701
|
+
}
|
|
677
702
|
metadata.level() <= log::max_level()
|
|
678
703
|
}
|
|
679
704
|
|
|
@@ -704,7 +729,12 @@ fn init_stdio_logger(
|
|
|
704
729
|
) -> Result<(), log::SetLoggerError> {
|
|
705
730
|
let logger = StdioLogger { sender };
|
|
706
731
|
log::set_boxed_logger(Box::new(logger))?;
|
|
707
|
-
|
|
732
|
+
// Respect RUST_LOG if set, otherwise use the provided default
|
|
733
|
+
let effective_level = std::env::var("RUST_LOG")
|
|
734
|
+
.ok()
|
|
735
|
+
.and_then(|s| s.parse().ok())
|
|
736
|
+
.unwrap_or(level);
|
|
737
|
+
log::set_max_level(effective_level);
|
|
708
738
|
Ok(())
|
|
709
739
|
}
|
|
710
740
|
|
|
@@ -1346,6 +1376,17 @@ fn start_logging_thread(rx: std::sync::mpsc::Receiver<String>) {
|
|
|
1346
1376
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
1347
1377
|
let args = Args::parse();
|
|
1348
1378
|
|
|
1379
|
+
// Set SHM environment variables for mt_sea to read
|
|
1380
|
+
// SHM is enabled by default, --no-shm disables it
|
|
1381
|
+
// SAFETY: We're setting env vars at the start of main before spawning any threads
|
|
1382
|
+
#[cfg(feature = "shm")]
|
|
1383
|
+
unsafe {
|
|
1384
|
+
if args.no_shm {
|
|
1385
|
+
std::env::set_var("MINOT_SHM_DISABLED", "1");
|
|
1386
|
+
}
|
|
1387
|
+
std::env::set_var("MINOT_SHM_SIZE", args.shm_size.as_u64().to_string());
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1349
1390
|
match args.command {
|
|
1350
1391
|
Commands::Tui(tui_args) => tui(tui_args.file).await,
|
|
1351
1392
|
Commands::Serve => serve().await,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
use anyhow::anyhow;
|
|
2
|
-
use log::{info, warn};
|
|
2
|
+
use log::{debug, error, info, warn};
|
|
3
3
|
use serde::{Deserialize, Serialize};
|
|
4
4
|
use std::path::PathBuf;
|
|
5
5
|
use std::process::Stdio;
|
|
@@ -8,6 +8,36 @@ use tokio::process::Command;
|
|
|
8
8
|
|
|
9
9
|
type BoxError = Box<dyn std::error::Error>;
|
|
10
10
|
|
|
11
|
+
/// Initialize a stderr logger for the runner process with a minimal format
|
|
12
|
+
/// that pelorus can parse: `[LEVEL] target: message`
|
|
13
|
+
fn init_runner_logger() {
|
|
14
|
+
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info,zenoh=warn"))
|
|
15
|
+
.format(|buf, record| {
|
|
16
|
+
use std::io::Write;
|
|
17
|
+
writeln!(
|
|
18
|
+
buf,
|
|
19
|
+
"[{}] {}: {}",
|
|
20
|
+
record.level(),
|
|
21
|
+
record.target(),
|
|
22
|
+
record.args()
|
|
23
|
+
)
|
|
24
|
+
})
|
|
25
|
+
.target(env_logger::Target::Stderr)
|
|
26
|
+
.init();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// Re-emit a log record received from serve's JSON protocol at the correct level.
|
|
30
|
+
fn forward_serve_log(level: &str, target: &str, message: &str) {
|
|
31
|
+
match level {
|
|
32
|
+
"ERROR" => error!(target: target, "{}", message),
|
|
33
|
+
"WARN" => warn!(target: target, "{}", message),
|
|
34
|
+
"INFO" => info!(target: target, "{}", message),
|
|
35
|
+
"DEBUG" => debug!(target: target, "{}", message),
|
|
36
|
+
"TRACE" => log::trace!(target: target, "{}", message),
|
|
37
|
+
_ => info!(target: target, "{}", message),
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
11
41
|
#[derive(Serialize, Debug)]
|
|
12
42
|
#[serde(tag = "type")]
|
|
13
43
|
#[allow(dead_code)]
|
|
@@ -58,6 +88,8 @@ struct ServerResponse {
|
|
|
58
88
|
}
|
|
59
89
|
|
|
60
90
|
pub async fn run(file_path: PathBuf, minot_path: PathBuf, sync: bool) -> Result<(), BoxError> {
|
|
91
|
+
init_runner_logger();
|
|
92
|
+
|
|
61
93
|
let file_content = std::fs::read_to_string(&file_path)
|
|
62
94
|
.map_err(|e| format!("Failed to read file {:?}: {}", file_path, e))?;
|
|
63
95
|
|
|
@@ -172,17 +204,17 @@ pub async fn run(file_path: PathBuf, minot_path: PathBuf, sync: bool) -> Result<
|
|
|
172
204
|
message,
|
|
173
205
|
target,
|
|
174
206
|
} => {
|
|
175
|
-
|
|
207
|
+
forward_serve_log(&level, &target, &message);
|
|
176
208
|
}
|
|
177
209
|
ServerMessage::CommandResponse(resp) => {
|
|
178
|
-
|
|
210
|
+
debug!("Received command response: {:?}", resp);
|
|
179
211
|
}
|
|
180
212
|
ServerMessage::ServerReady => {
|
|
181
213
|
warn!("Received unexpected ServerReady");
|
|
182
214
|
}
|
|
183
215
|
}
|
|
184
216
|
} else {
|
|
185
|
-
|
|
217
|
+
eprint!("{}", line);
|
|
186
218
|
}
|
|
187
219
|
}
|
|
188
220
|
|
|
@@ -222,18 +254,18 @@ where
|
|
|
222
254
|
message,
|
|
223
255
|
target,
|
|
224
256
|
} => {
|
|
225
|
-
|
|
257
|
+
forward_serve_log(level, target, message);
|
|
226
258
|
}
|
|
227
259
|
_ => {
|
|
228
260
|
if predicate(&msg) {
|
|
229
261
|
return Ok(msg);
|
|
230
262
|
} else {
|
|
231
|
-
|
|
263
|
+
debug!("Ignored unexpected message while waiting: {:?}", msg);
|
|
232
264
|
}
|
|
233
265
|
}
|
|
234
266
|
}
|
|
235
267
|
} else {
|
|
236
|
-
|
|
268
|
+
eprint!("{}", line);
|
|
237
269
|
}
|
|
238
270
|
}
|
|
239
271
|
}
|
|
@@ -6,6 +6,8 @@ use mt_net::COMPARE_NODE_NAME;
|
|
|
6
6
|
use rkyv::{api::high::from_bytes, to_bytes, util::AlignedVec};
|
|
7
7
|
use zenoh::Wait;
|
|
8
8
|
|
|
9
|
+
#[cfg(feature = "shm")]
|
|
10
|
+
use std::sync::RwLock as StdRwLock;
|
|
9
11
|
#[cfg(feature = "shm")]
|
|
10
12
|
use zenoh::shm::{
|
|
11
13
|
BlockOn, GarbageCollect, PosixShmProviderBackend, ShmProvider, ShmProviderBuilder,
|
|
@@ -20,13 +22,28 @@ pub type CoordSender = tokio::sync::broadcast::Sender<(Packet, Option<std::net::
|
|
|
20
22
|
pub type RecvBuffer = HashMap<u32, Vec<(Vec<u8>, VariableType, String)>>;
|
|
21
23
|
|
|
22
24
|
#[cfg(feature = "shm")]
|
|
23
|
-
///
|
|
24
|
-
const
|
|
25
|
+
/// Within typical Linux /dev/shm limits
|
|
26
|
+
const DEFAULT_SHM_BUFFER_SIZE: usize = 7 * 1024 * 1024;
|
|
25
27
|
|
|
26
28
|
#[cfg(feature = "shm")]
|
|
27
|
-
/// Messages larger than this use SHM, smaller use network (8 KB threshold)
|
|
28
29
|
const SHM_SIZE_THRESHOLD: usize = 8 * 1024;
|
|
29
30
|
|
|
31
|
+
#[cfg(feature = "shm")]
|
|
32
|
+
fn is_shm_enabled() -> bool {
|
|
33
|
+
!std::env::var("MINOT_SHM_DISABLED")
|
|
34
|
+
.map(|v| v == "1" || v.to_lowercase() == "true")
|
|
35
|
+
.unwrap_or(false)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/// Get SHM buffer size from environment variable, falling back to default
|
|
39
|
+
#[cfg(feature = "shm")]
|
|
40
|
+
fn get_shm_buffer_size() -> usize {
|
|
41
|
+
std::env::var("MINOT_SHM_SIZE")
|
|
42
|
+
.ok()
|
|
43
|
+
.and_then(|v| v.parse::<usize>().ok())
|
|
44
|
+
.unwrap_or(DEFAULT_SHM_BUFFER_SIZE)
|
|
45
|
+
}
|
|
46
|
+
|
|
30
47
|
/// Copy bytes into an aligned buffer for rkyv deserialization
|
|
31
48
|
fn align_bytes(bytes: &[u8]) -> AlignedVec {
|
|
32
49
|
let mut aligned = AlignedVec::with_capacity(bytes.len());
|
|
@@ -34,6 +51,13 @@ fn align_bytes(bytes: &[u8]) -> AlignedVec {
|
|
|
34
51
|
aligned
|
|
35
52
|
}
|
|
36
53
|
|
|
54
|
+
/// SHM state with dynamic resizing capability
|
|
55
|
+
#[cfg(feature = "shm")]
|
|
56
|
+
struct ShmState {
|
|
57
|
+
provider: std::sync::Arc<ShmProvider<PosixShmProviderBackend>>,
|
|
58
|
+
capacity: usize,
|
|
59
|
+
}
|
|
60
|
+
|
|
37
61
|
pub struct Client {
|
|
38
62
|
pub coordinator_receive: std::sync::Arc<std::sync::RwLock<Option<CoordSender>>>,
|
|
39
63
|
pub coordinator_send:
|
|
@@ -42,14 +66,14 @@ pub struct Client {
|
|
|
42
66
|
rm_rules_on_disconnect: bool,
|
|
43
67
|
pub updated_raw_recv: tokio::sync::broadcast::Sender<u32>,
|
|
44
68
|
pub raw_recv_buff: std::sync::Arc<std::sync::RwLock<RecvBuffer>>,
|
|
45
|
-
/// Dedicated channel for wind packets - uses MPSC to avoid race conditions.
|
|
46
|
-
/// Wind packets are forwarded here from the coordinator receiver and buffered until consumed.
|
|
47
69
|
pub wind_receiver: std::sync::Arc<tokio::sync::Mutex<tokio::sync::mpsc::Receiver<Packet>>>,
|
|
48
70
|
wind_sender: tokio::sync::mpsc::Sender<Packet>,
|
|
49
71
|
session: std::sync::Arc<zenoh::Session>,
|
|
50
72
|
domain_id: u16,
|
|
51
73
|
#[cfg(feature = "shm")]
|
|
52
|
-
|
|
74
|
+
shm_state: StdRwLock<Option<ShmState>>,
|
|
75
|
+
#[cfg(feature = "shm")]
|
|
76
|
+
shm_initialized: std::sync::atomic::AtomicBool,
|
|
53
77
|
}
|
|
54
78
|
|
|
55
79
|
impl std::fmt::Debug for Client {
|
|
@@ -62,7 +86,279 @@ impl std::fmt::Debug for Client {
|
|
|
62
86
|
}
|
|
63
87
|
}
|
|
64
88
|
|
|
89
|
+
#[cfg(feature = "shm")]
|
|
90
|
+
fn create_shm_provider(size: usize) -> Result<ShmState, String> {
|
|
91
|
+
match ShmProviderBuilder::default_backend(size).wait() {
|
|
92
|
+
Ok(provider) => Ok(ShmState {
|
|
93
|
+
provider: std::sync::Arc::new(provider),
|
|
94
|
+
capacity: size,
|
|
95
|
+
}),
|
|
96
|
+
Err(e) => Err(e.to_string()),
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
#[cfg(feature = "shm")]
|
|
101
|
+
fn format_shm_error(err_str: &str, requested_size: usize) -> String {
|
|
102
|
+
#[cfg(target_os = "linux")]
|
|
103
|
+
{
|
|
104
|
+
let shm_mb = requested_size / (1024 * 1024);
|
|
105
|
+
if err_str.contains("ENOMEM")
|
|
106
|
+
|| err_str.contains("Cannot allocate")
|
|
107
|
+
|| err_str.contains("No space")
|
|
108
|
+
{
|
|
109
|
+
format!(
|
|
110
|
+
"Insufficient shared memory. Requested {} bytes.\n\
|
|
111
|
+
Possible fixes:\n\
|
|
112
|
+
- Increase /dev/shm size: `sudo mount -o remount,size={}M /dev/shm`\n\
|
|
113
|
+
- Or set ulimit: `ulimit -l unlimited` (may require root)\n\
|
|
114
|
+
- Or add to /etc/fstab: `tmpfs /dev/shm tmpfs defaults,size={}M 0 0`\n\
|
|
115
|
+
- Or disable SHM: `--no-shm` or `MINOT_SHM_DISABLED=1`",
|
|
116
|
+
requested_size,
|
|
117
|
+
shm_mb.max(16),
|
|
118
|
+
shm_mb.max(16)
|
|
119
|
+
)
|
|
120
|
+
} else if err_str.contains("EACCES") || err_str.contains("Permission denied") {
|
|
121
|
+
"Permission denied.\n\
|
|
122
|
+
Possible fixes:\n\
|
|
123
|
+
- Check /dev/shm permissions: `ls -la /dev/shm`\n\
|
|
124
|
+
- Ensure user has write access to /dev/shm\n\
|
|
125
|
+
- Or disable SHM: `--no-shm` or `MINOT_SHM_DISABLED=1`"
|
|
126
|
+
.to_string()
|
|
127
|
+
} else {
|
|
128
|
+
format!(
|
|
129
|
+
"Error: {}\nTo disable SHM: `--no-shm` or `MINOT_SHM_DISABLED=1`",
|
|
130
|
+
err_str
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
#[cfg(target_os = "macos")]
|
|
136
|
+
{
|
|
137
|
+
if err_str.contains("ENOMEM")
|
|
138
|
+
|| err_str.contains("Cannot allocate")
|
|
139
|
+
|| err_str.contains("No space")
|
|
140
|
+
{
|
|
141
|
+
format!(
|
|
142
|
+
"Insufficient shared memory. Requested {} bytes.\n\
|
|
143
|
+
Possible fixes:\n\
|
|
144
|
+
- Increase shmmax: `sudo sysctl -w kern.sysv.shmmax={}`\n\
|
|
145
|
+
- Increase shmall: `sudo sysctl -w kern.sysv.shmall={}`\n\
|
|
146
|
+
- Or disable SHM: `--no-shm` or `MINOT_SHM_DISABLED=1`\n\
|
|
147
|
+
Note: macOS has stricter SHM limits than Linux.",
|
|
148
|
+
requested_size,
|
|
149
|
+
requested_size,
|
|
150
|
+
requested_size / 4096
|
|
151
|
+
)
|
|
152
|
+
} else if err_str.contains("EACCES") || err_str.contains("Permission denied") {
|
|
153
|
+
"Permission denied.\n\
|
|
154
|
+
Possible fixes:\n\
|
|
155
|
+
- Check System Preferences > Security & Privacy settings\n\
|
|
156
|
+
- Or disable SHM: `--no-shm` or `MINOT_SHM_DISABLED=1`"
|
|
157
|
+
.to_string()
|
|
158
|
+
} else {
|
|
159
|
+
format!(
|
|
160
|
+
"Error: {}\nTo disable SHM: `--no-shm` or `MINOT_SHM_DISABLED=1`",
|
|
161
|
+
err_str
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
65
167
|
impl Client {
|
|
168
|
+
/// Get or create SHM provider, initializing on first call
|
|
169
|
+
#[cfg(feature = "shm")]
|
|
170
|
+
fn get_or_init_shm(&self) -> Option<std::sync::Arc<ShmProvider<PosixShmProviderBackend>>> {
|
|
171
|
+
if !is_shm_enabled() {
|
|
172
|
+
return None;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Fast path: already initialized
|
|
176
|
+
if self
|
|
177
|
+
.shm_initialized
|
|
178
|
+
.load(std::sync::atomic::Ordering::Acquire)
|
|
179
|
+
{
|
|
180
|
+
return self
|
|
181
|
+
.shm_state
|
|
182
|
+
.read()
|
|
183
|
+
.ok()?
|
|
184
|
+
.as_ref()
|
|
185
|
+
.map(|s| s.provider.clone());
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Slow path: initialize
|
|
189
|
+
let mut state = self.shm_state.write().ok()?;
|
|
190
|
+
if state.is_none() {
|
|
191
|
+
let initial_size = get_shm_buffer_size();
|
|
192
|
+
match create_shm_provider(initial_size) {
|
|
193
|
+
Ok(shm_state) => {
|
|
194
|
+
info!(
|
|
195
|
+
"SHM enabled with initial buffer size: {} bytes",
|
|
196
|
+
initial_size
|
|
197
|
+
);
|
|
198
|
+
*state = Some(shm_state);
|
|
199
|
+
self.shm_initialized
|
|
200
|
+
.store(true, std::sync::atomic::Ordering::Release);
|
|
201
|
+
}
|
|
202
|
+
Err(e) => {
|
|
203
|
+
warn!(
|
|
204
|
+
"Failed to create SHM provider: {}\nFalling back to network transport.",
|
|
205
|
+
format_shm_error(&e, initial_size)
|
|
206
|
+
);
|
|
207
|
+
return None;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
state.as_ref().map(|s| s.provider.clone())
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/// Try to grow the SHM pool to accommodate a larger message
|
|
215
|
+
#[cfg(feature = "shm")]
|
|
216
|
+
fn try_grow_shm(
|
|
217
|
+
&self,
|
|
218
|
+
required_size: usize,
|
|
219
|
+
) -> Option<std::sync::Arc<ShmProvider<PosixShmProviderBackend>>> {
|
|
220
|
+
let mut state = self.shm_state.write().ok()?;
|
|
221
|
+
|
|
222
|
+
let current_capacity = state.as_ref().map(|s| s.capacity).unwrap_or(0);
|
|
223
|
+
|
|
224
|
+
// Double the required size (like Vec growth strategy)
|
|
225
|
+
let new_capacity = (required_size * 2).max(current_capacity * 2);
|
|
226
|
+
|
|
227
|
+
debug!(
|
|
228
|
+
"Growing SHM pool from {} to {} bytes",
|
|
229
|
+
current_capacity, new_capacity
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
match create_shm_provider(new_capacity) {
|
|
233
|
+
Ok(new_state) => {
|
|
234
|
+
info!("SHM pool grown to {} bytes", new_capacity);
|
|
235
|
+
*state = Some(new_state);
|
|
236
|
+
state.as_ref().map(|s| s.provider.clone())
|
|
237
|
+
}
|
|
238
|
+
Err(e) => {
|
|
239
|
+
warn!(
|
|
240
|
+
"Cannot grow SHM pool to {} bytes: {}\nFalling back to network transport for large messages.",
|
|
241
|
+
new_capacity,
|
|
242
|
+
format_shm_error(&e, new_capacity)
|
|
243
|
+
);
|
|
244
|
+
None
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/// Get current SHM capacity
|
|
250
|
+
#[cfg(feature = "shm")]
|
|
251
|
+
fn shm_capacity(&self) -> usize {
|
|
252
|
+
self.shm_state
|
|
253
|
+
.read()
|
|
254
|
+
.ok()
|
|
255
|
+
.and_then(|s| s.as_ref().map(|state| state.capacity))
|
|
256
|
+
.unwrap_or(0)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/// Try to send via SHM with automatic pool growth. Returns:
|
|
260
|
+
/// - Some(Ok(())) if sent successfully via SHM
|
|
261
|
+
/// - Some(Err(e)) if there was an error during sending
|
|
262
|
+
/// - None if SHM is unavailable and caller should fall back to network
|
|
263
|
+
#[cfg(feature = "shm")]
|
|
264
|
+
#[allow(clippy::too_many_arguments)]
|
|
265
|
+
async fn try_shm_send(
|
|
266
|
+
&self,
|
|
267
|
+
total_len: usize,
|
|
268
|
+
id_bytes: &[u8; 4],
|
|
269
|
+
variable_type: VariableType,
|
|
270
|
+
padded_name: &[u8; 64],
|
|
271
|
+
data: &[u8],
|
|
272
|
+
data_key: &str,
|
|
273
|
+
id: u32,
|
|
274
|
+
) -> Option<anyhow::Result<()>> {
|
|
275
|
+
// Get or initialize SHM provider
|
|
276
|
+
let mut shm_provider = self.get_or_init_shm()?;
|
|
277
|
+
|
|
278
|
+
// Try allocation, growing pool if needed (up to 2 attempts)
|
|
279
|
+
for attempt in 0..2 {
|
|
280
|
+
let shm_result = shm_provider
|
|
281
|
+
.alloc(total_len)
|
|
282
|
+
.with_policy::<BlockOn<GarbageCollect>>()
|
|
283
|
+
.await;
|
|
284
|
+
|
|
285
|
+
match shm_result {
|
|
286
|
+
Ok(mut shm_buf) => {
|
|
287
|
+
// Copy data into SHM buffer
|
|
288
|
+
shm_buf[0..4].copy_from_slice(id_bytes);
|
|
289
|
+
shm_buf[4] = variable_type.into();
|
|
290
|
+
shm_buf[5..69].copy_from_slice(padded_name);
|
|
291
|
+
shm_buf[69..total_len].copy_from_slice(data);
|
|
292
|
+
|
|
293
|
+
let shm_immut: zenoh::shm::ZShm = shm_buf.into();
|
|
294
|
+
debug!("Sending {} bytes via SHM to {}", total_len, data_key);
|
|
295
|
+
|
|
296
|
+
// Send with retry loop
|
|
297
|
+
loop {
|
|
298
|
+
let replies =
|
|
299
|
+
match self.session.get(data_key).payload(shm_immut.clone()).wait() {
|
|
300
|
+
Ok(r) => r,
|
|
301
|
+
Err(e) => {
|
|
302
|
+
return Some(Err(anyhow!("Failed to send data query: {}", e)));
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
match replies.recv_async().await {
|
|
307
|
+
Ok(reply) => match reply.result() {
|
|
308
|
+
Ok(_sample) => {
|
|
309
|
+
debug!(
|
|
310
|
+
"Sent data id {} to {} via SHM (ACK received)",
|
|
311
|
+
id, data_key
|
|
312
|
+
);
|
|
313
|
+
return Some(Ok(()));
|
|
314
|
+
}
|
|
315
|
+
Err(err) => {
|
|
316
|
+
let err_payload = err.payload().to_bytes();
|
|
317
|
+
let err_msg = String::from_utf8_lossy(&err_payload);
|
|
318
|
+
warn!("Receiver error for id {}: {}", id, err_msg);
|
|
319
|
+
tokio::task::yield_now().await;
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
Err(_) => {
|
|
324
|
+
tokio::task::yield_now().await;
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
Err(_) if attempt == 0 => {
|
|
331
|
+
// First attempt failed - try to grow the pool
|
|
332
|
+
let current_capacity = self.shm_capacity();
|
|
333
|
+
if total_len > current_capacity {
|
|
334
|
+
debug!(
|
|
335
|
+
"Message ({} bytes) exceeds current SHM capacity ({} bytes), growing pool",
|
|
336
|
+
total_len, current_capacity
|
|
337
|
+
);
|
|
338
|
+
if let Some(new_provider) = self.try_grow_shm(total_len) {
|
|
339
|
+
shm_provider = new_provider;
|
|
340
|
+
continue; // Retry with larger pool
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
// Couldn't grow, fall back to network
|
|
344
|
+
warn!(
|
|
345
|
+
"SHM allocation failed for {} bytes, falling back to network transport",
|
|
346
|
+
total_len
|
|
347
|
+
);
|
|
348
|
+
return None;
|
|
349
|
+
}
|
|
350
|
+
Err(_) => {
|
|
351
|
+
// Second attempt also failed
|
|
352
|
+
warn!(
|
|
353
|
+
"SHM allocation failed after pool growth, falling back to network transport"
|
|
354
|
+
);
|
|
355
|
+
return None;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
None
|
|
360
|
+
}
|
|
361
|
+
|
|
66
362
|
pub async fn init(ship_kind: ShipKind, rm_rules_on_disconnect: bool) -> Self {
|
|
67
363
|
let domain_id = get_domain_id();
|
|
68
364
|
if domain_id > 0 {
|
|
@@ -76,14 +372,6 @@ impl Client {
|
|
|
76
372
|
.expect("Failed to open Zenoh session");
|
|
77
373
|
let session = std::sync::Arc::new(session);
|
|
78
374
|
|
|
79
|
-
#[cfg(feature = "shm")]
|
|
80
|
-
let shm_provider = {
|
|
81
|
-
let provider = ShmProviderBuilder::default_backend(SHM_BUFFER_SIZE)
|
|
82
|
-
.wait()
|
|
83
|
-
.expect("Failed to create SHM provider");
|
|
84
|
-
std::sync::Arc::new(provider)
|
|
85
|
-
};
|
|
86
|
-
|
|
87
375
|
let (updated_raw_recv, _) = tokio::sync::broadcast::channel(100);
|
|
88
376
|
let raw_recv_buff: std::sync::Arc<std::sync::RwLock<RecvBuffer>> =
|
|
89
377
|
std::sync::Arc::new(std::sync::RwLock::new(HashMap::new()));
|
|
@@ -218,7 +506,9 @@ impl Client {
|
|
|
218
506
|
session,
|
|
219
507
|
domain_id,
|
|
220
508
|
#[cfg(feature = "shm")]
|
|
221
|
-
|
|
509
|
+
shm_state: StdRwLock::new(None),
|
|
510
|
+
#[cfg(feature = "shm")]
|
|
511
|
+
shm_initialized: std::sync::atomic::AtomicBool::new(false),
|
|
222
512
|
}
|
|
223
513
|
}
|
|
224
514
|
|
|
@@ -441,63 +731,22 @@ impl Client {
|
|
|
441
731
|
|
|
442
732
|
#[cfg(feature = "shm")]
|
|
443
733
|
if total_len >= SHM_SIZE_THRESHOLD {
|
|
444
|
-
// Try
|
|
445
|
-
let
|
|
446
|
-
.
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
// Convert to immutable SHM buffer (can be cloned for retries)
|
|
459
|
-
let shm_immut: zenoh::shm::ZShm = shm_buf.into();
|
|
460
|
-
|
|
461
|
-
debug!("Sending {} bytes via SHM to {}", total_len, data_key);
|
|
462
|
-
|
|
463
|
-
// Use query with SHM payload
|
|
464
|
-
loop {
|
|
465
|
-
let replies = self
|
|
466
|
-
.session
|
|
467
|
-
.get(&data_key)
|
|
468
|
-
.payload(shm_immut.clone())
|
|
469
|
-
.wait()
|
|
470
|
-
.map_err(|e| anyhow!("Failed to send data query: {}", e))?;
|
|
471
|
-
|
|
472
|
-
match replies.recv_async().await {
|
|
473
|
-
Ok(reply) => match reply.result() {
|
|
474
|
-
Ok(_sample) => {
|
|
475
|
-
debug!(
|
|
476
|
-
"Sent data id {} to {} via SHM (ACK received)",
|
|
477
|
-
id, data_key
|
|
478
|
-
);
|
|
479
|
-
return Ok(());
|
|
480
|
-
}
|
|
481
|
-
Err(err) => {
|
|
482
|
-
let err_payload = err.payload().to_bytes();
|
|
483
|
-
let err_msg = String::from_utf8_lossy(&err_payload);
|
|
484
|
-
warn!("Receiver error for id {}: {}", id, err_msg);
|
|
485
|
-
tokio::task::yield_now().await;
|
|
486
|
-
continue;
|
|
487
|
-
}
|
|
488
|
-
},
|
|
489
|
-
Err(_) => {
|
|
490
|
-
tokio::task::yield_now().await;
|
|
491
|
-
continue;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
}
|
|
734
|
+
// Try SHM transfer with dynamic pool growth
|
|
735
|
+
if let Some(result) = self
|
|
736
|
+
.try_shm_send(
|
|
737
|
+
total_len,
|
|
738
|
+
&id_bytes,
|
|
739
|
+
variable_type,
|
|
740
|
+
&padded_name,
|
|
741
|
+
&data,
|
|
742
|
+
&data_key,
|
|
743
|
+
id,
|
|
744
|
+
)
|
|
745
|
+
.await
|
|
746
|
+
{
|
|
747
|
+
return result;
|
|
495
748
|
}
|
|
496
|
-
//
|
|
497
|
-
debug!(
|
|
498
|
-
"SHM allocation failed, falling back to network for {} bytes",
|
|
499
|
-
total_len
|
|
500
|
-
);
|
|
749
|
+
// If try_shm_send returns None, fall through to network
|
|
501
750
|
}
|
|
502
751
|
|
|
503
752
|
// Network transfer (small messages, SHM disabled, or SHM fallback)
|
|
@@ -35,8 +35,8 @@ cdr-encoding.workspace = true
|
|
|
35
35
|
ratpub = { workspace = true, optional = true }
|
|
36
36
|
|
|
37
37
|
[features]
|
|
38
|
-
default = ["ratpub"
|
|
39
|
-
|
|
38
|
+
default = ["ratpub"]
|
|
39
|
+
shm = ["mt_sea/shm", "ratpub?/shm"]
|
|
40
40
|
humble = ["mt_bagread/humble"]
|
|
41
41
|
ros2-c = ["dep:r2r", "dep:ros2-interfaces-jazzy-serde"]
|
|
42
42
|
ros2-native = ["dep:ros2-client", "dep:ros2-interfaces-jazzy-serde"]
|
|
@@ -28,8 +28,8 @@ ros2-interfaces-jazzy-rkyv.workspace = true
|
|
|
28
28
|
futures-lite = { workspace = true, optional = true }
|
|
29
29
|
|
|
30
30
|
[features]
|
|
31
|
-
default = [
|
|
32
|
-
|
|
31
|
+
default = []
|
|
32
|
+
shm = ["mt_sea/shm"]
|
|
33
33
|
smol = ["dep:futures-lite"]
|
|
34
34
|
|
|
35
35
|
[[example]]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|