Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
f822a92
feat(tf): add ros-z-tf crate with TF2 transform buffer
YuanYuYuan May 9, 2026
b9052e7
feat(tf): implement TransientLocal replay, broadcaster, wait_for_tran…
YuanYuYuan May 10, 2026
4cb5435
fix(tf): normalize static timestamps, fix lookup_transform_full arg o…
YuanYuYuan May 11, 2026
e632307
fix(tf): suppress dead_code lint on SubInner variants held for Drop
YuanYuYuan May 11, 2026
f992ad5
fix(tf): remove needless struct update in compose_stamped
YuanYuYuan May 11, 2026
062e345
fix(tf): remove needless struct updates in test helpers
YuanYuYuan May 11, 2026
d356abf
fix(tf): cap KeepAll history to 1000, fix distro propagation, simplif…
YuanYuYuan May 11, 2026
9ccbc53
test(tf): replace inert broadcaster tests with zero_timestamps unit t…
YuanYuYuan May 11, 2026
b705fa4
fix(tf): remove needless_update and redundant refs in tf_integration …
YuanYuYuan May 11, 2026
1c940df
fix(tf): remove unnecessary tf2_msgs protobuf exclusion — prost handl…
YuanYuYuan May 11, 2026
6b561ad
docs(codegen): fix misleading protobuf_excluded_packages doc comment
YuanYuYuan May 11, 2026
cfd1695
fix(codegen): replicate prost/heck word boundaries in convert_to_pros…
YuanYuYuan May 11, 2026
6310895
refactor: rename ros-z-tf → hiroz-tf
YuanYuYuan May 26, 2026
735ea70
fix(tf/rebase): remove SubInner dead code left by conflict resolution
YuanYuYuan May 26, 2026
ed9174a
fix(tf): define is_transient_local, remove unused SubscriberBuilderEx…
YuanYuYuan May 26, 2026
074bd06
chore: retrigger CI after GitHub Actions CDN transient failure
YuanYuYuan May 26, 2026
342d2cc
chore: retrigger CI after GitHub Actions outage resolution
YuanYuYuan May 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ members = [
"crates/hiroz-schema",
"crates/hiroz-msgs",
"crates/hiroz-tests",
"crates/hiroz-tf",
"crates/hiroz-console",
"crates/hiroz-bridge",
"crates/hiroz/examples/protobuf_demo",
Expand Down
5 changes: 5 additions & 0 deletions crates/hiroz-codegen/assets/jazzy/dependencies.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
"std_msgs"
]
},
"tf2_msgs": {
"dependencies": [
"geometry_msgs"
]
},
"sensor_msgs": {
"dependencies": [
"builtin_interfaces",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
geometry_msgs/TransformStamped[] transforms
1 change: 1 addition & 0 deletions crates/hiroz-codegen/src/bin/export_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ fn main() -> Result<()> {
external_crate: None,
local_packages: std::collections::HashSet::new(),
json_out: Some(json_path.clone()),
protobuf_excluded_packages: std::collections::HashSet::new(),
};

let generator = hiroz_codegen::MessageGenerator::new(config);
Expand Down
17 changes: 17 additions & 0 deletions crates/hiroz-codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ pub struct GeneratorConfig {

/// Output JSON definitions for external generators (Go, Python, etc.)
pub json_out: Option<PathBuf>,

/// Packages to skip during protobuf generation (CDR generation is unaffected).
/// Useful when a package's message types are intentionally not exposed via protobuf.
pub protobuf_excluded_packages: std::collections::HashSet<String>,
}

/// Message generator that orchestrates parsing, resolution, and code generation
Expand Down Expand Up @@ -507,6 +511,18 @@ impl MessageGenerator {
fn generate_protobuf_types(&self, messages: &[ResolvedMessage]) -> Result<()> {
use crate::protobuf_generator::ProtobufMessageGenerator;

let filtered: Vec<ResolvedMessage> = messages
.iter()
.filter(|m| {
!self
.config
.protobuf_excluded_packages
.contains(&m.parsed.package)
})
.cloned()
.collect();
let messages = filtered.as_slice();

let proto_dir = self.config.output_dir.join("proto");
let generator = ProtobufMessageGenerator::new(&proto_dir);

Expand Down Expand Up @@ -704,6 +720,7 @@ pub fn generate_user_messages(output_dir: &Path, is_humble: bool) -> Result<()>
external_crate: Some("hiroz_msgs".to_string()),
local_packages,
json_out: None,
protobuf_excluded_packages: std::collections::HashSet::new(),
};

let generator = MessageGenerator::new(config);
Expand Down
54 changes: 46 additions & 8 deletions crates/hiroz-codegen/src/protobuf_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,15 +322,53 @@ impl ::hiroz::msg::ZMessage for {proto_type} {{
Ok(impls)
}

/// Convert ROS message name to prost naming convention
/// Convert a ROS PascalCase message name to prost's UpperCamelCase convention.
///
/// Prost (via heck) splits words at:
/// - lowercase → uppercase transitions ("fooBar" → ["foo","Bar"])
/// - uppercase-run boundary before a lowercase letter
/// ("TFMessage" → ["TF","Message"], "ColorRGBA" → ["Color","RGBA"])
///
/// Each word is then title-cased (first char upper, rest lower).
fn convert_to_prost_naming(&self, name: &str) -> String {
// Handle specific known cases where prost naming differs
match name {
"MultiDOFJointState" => "MultiDofJointState".to_string(),
"ColorRGBA" => "ColorRgba".to_string(),
"UUID" => "Uuid".to_string(),
// Add more mappings as needed
_ => name.to_string(),
let chars: Vec<char> = name.chars().collect();
let mut words: Vec<String> = Vec::new();
let mut current = String::new();

for (i, &c) in chars.iter().enumerate() {
let is_word_start = if c.is_uppercase() {
let prev = if i > 0 { Some(chars[i - 1]) } else { None };
let next = chars.get(i + 1).copied();
match (prev, next) {
(None, _) => false, // first char: never a boundary
(Some(p), _) if p.is_lowercase() => true, // lower→upper
(Some(p), _) if p.is_ascii_digit() => true, // digit→upper
(Some(p), Some(n)) if p.is_uppercase() && n.is_lowercase() => true, // run→lower
_ => false,
}
} else {
false
};

if is_word_start && !current.is_empty() {
words.push(current.clone());
current = String::new();
}
current.push(c);
}
if !current.is_empty() {
words.push(current);
}

words
.iter()
.map(|w| {
let mut cs = w.chars();
match cs.next() {
None => String::new(),
Some(first) => first.to_uppercase().to_string() + &cs.as_str().to_lowercase(),
}
})
.collect()
}
}
2 changes: 2 additions & 0 deletions crates/hiroz-msgs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ std_msgs = []
geometry_msgs = ["std_msgs"]
sensor_msgs = ["std_msgs", "geometry_msgs"]
nav_msgs = ["std_msgs", "geometry_msgs"]
tf2_msgs = ["geometry_msgs"]
example_interfaces = []
action_tutorials_interfaces = []
test_msgs = []
Expand All @@ -63,6 +64,7 @@ all_msgs = [
"geometry_msgs",
"sensor_msgs",
"nav_msgs",
"tf2_msgs",
"example_interfaces",
"action_tutorials_interfaces",
"rcl_interfaces",
Expand Down
6 changes: 6 additions & 0 deletions crates/hiroz-msgs/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ fn main() -> Result<()> {
println!("cargo::rustc-check-cfg=cfg(has_example_interfaces)");
println!("cargo::rustc-check-cfg=cfg(has_test_msgs)");
println!("cargo::rustc-check-cfg=cfg(has_rcl_interfaces)");
println!("cargo::rustc-check-cfg=cfg(has_tf2_msgs)");

// Detect ROS version and emit cfg
let is_humble = detect_ros_version();
Expand All @@ -38,6 +39,7 @@ fn main() -> Result<()> {
external_crate: None, // All packages are local in hiroz-msgs
local_packages: std::collections::HashSet::new(), // All packages are local
json_out: None, // Not needed for Rust codegen
protobuf_excluded_packages: std::collections::HashSet::new(),
};

let generator = hiroz_codegen::MessageGenerator::new(config);
Expand Down Expand Up @@ -255,6 +257,10 @@ fn get_all_packages(is_humble: bool) -> Vec<&'static str> {
names.push("nav_msgs");
}

if env::var("CARGO_FEATURE_TF2_MSGS").is_ok() {
names.push("tf2_msgs");
}

if env::var("CARGO_FEATURE_EXAMPLE_INTERFACES").is_ok() {
names.push("example_interfaces");
}
Expand Down
19 changes: 14 additions & 5 deletions crates/hiroz-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ publish = false
[dependencies]
hiroz = { path = "../hiroz", default-features = false, features = ["protobuf"] }
hiroz-msgs = { path = "../hiroz-msgs", default-features = false, optional = true }
hiroz-tf = { path = "../hiroz-tf", default-features = false, optional = true }
hiroz-cdr = { path = "../hiroz-cdr" }
hiroz-schema = { path = "../hiroz-schema" }
protobuf_demo = { path = "../hiroz/examples/protobuf_demo" }
Expand Down Expand Up @@ -58,8 +59,16 @@ humble-jazzy-bridge-tests = [
# This enables testing hiroz with DDS-based ROS 2 nodes via zenoh-bridge-ros2dds
ros2dds-interop = ["ros-msgs", "hiroz/ros2dds", "hiroz/rmw-zenoh"]

# ROS 2 distro compatibility - propagate to hiroz and hiroz-msgs
humble = ["hiroz/humble", "hiroz-msgs/humble"]
jazzy = ["hiroz/jazzy", "hiroz-msgs/jazzy"]
rolling = ["hiroz/rolling", "hiroz-msgs/rolling"]
kilted = ["hiroz/kilted", "hiroz-msgs/kilted"]
# TF integration tests
tf-tests = [
"dep:hiroz-tf",
"dep:hiroz-msgs",
"hiroz-msgs/tf2_msgs",
"hiroz-msgs/geometry_msgs",
]

# ROS 2 distro compatibility - propagate to hiroz, hiroz-msgs, and hiroz-tf
humble = ["hiroz/humble", "hiroz-msgs/humble", "hiroz-tf?/humble"]
jazzy = ["hiroz/jazzy", "hiroz-msgs/jazzy", "hiroz-tf?/jazzy"]
rolling = ["hiroz/rolling", "hiroz-msgs/rolling", "hiroz-tf?/rolling"]
kilted = ["hiroz/kilted", "hiroz-msgs/kilted", "hiroz-tf?/kilted"]
Loading
Loading