Conversion functions.

This commit is contained in:
James Pace 2026-07-24 20:00:46 -04:00
parent 93a9f1baba
commit d1314797a5
7 changed files with 243 additions and 39 deletions

View File

@ -3,6 +3,7 @@ project(j7s_diagnostics_ros CXX)
find_package(ament_cmake REQUIRED) find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED) find_package(rclcpp REQUIRED)
find_package(diagnostic_msgs REQUIRED)
include(FetchContent) include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
@ -14,14 +15,11 @@ FetchContent_MakeAvailable(Corrosion)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
# Rust bindings.
corrosion_import_crate(MANIFEST_PATH j7s_diagnostics_cxx/Cargo.toml) corrosion_import_crate(MANIFEST_PATH j7s_diagnostics_cxx/Cargo.toml)
corrosion_add_cxxbridge(j7s_diagnostics_cxx_bridge corrosion_add_cxxbridge(j7s_diagnostics_cxx_bridge
CRATE j7s_diagnostics_cxx CRATE j7s_diagnostics_cxx
FILES lib.rs) FILES lib.rs)
add_executable(test_bin src/test.cpp)
target_link_libraries(test_bin PRIVATE j7s_diagnostics_cxx_bridge j7s_diagnostics_cxx)
corrosion_install(TARGETS j7s_diagnostics_cxx corrosion_install(TARGETS j7s_diagnostics_cxx
EXPORT export_${PROJECT_NAME} EXPORT export_${PROJECT_NAME}
ARCHIVE DESTINATION lib ARCHIVE DESTINATION lib
@ -29,8 +27,31 @@ corrosion_install(TARGETS j7s_diagnostics_cxx
RUNTIME DESTINATION bin RUNTIME DESTINATION bin
) )
# The rest of the stuff.
# Note that rust/cxx doesn't support shared libraries so
# this library (and anything that uses it) will have
# to be statically linked.
add_library(conversions STATIC src/conversions.cpp)
target_include_directories(conversions PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/${PROJECT_NAME}>)
target_link_libraries(conversions PUBLIC
j7s_diagnostics_cxx_bridge
j7s_diagnostics_cxx
${diagnostic_msgs_TARGETS}
)
add_executable(test_bin src/test.cpp)
target_include_directories(test_bin PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/${PROJECT_NAME}>)
target_link_libraries(test_bin PRIVATE
conversions
)
install(TARGETS install(TARGETS
test_bin conversions
#test_bin
DESTINATION lib/${PROJECT_NAME}) DESTINATION lib/${PROJECT_NAME})
ament_export_targets(export_${PROJECT_NAME}) ament_export_targets(export_${PROJECT_NAME})

View File

@ -0,0 +1,20 @@
#pragma once
#include "j7s_diagnostics_cxx_bridge/lib.h"
#include <diagnostic_msgs/msg/diagnostic_array.hpp>
#include <diagnostic_msgs/msg/diagnostic_status.hpp>
std::string convertString(const rust::String& rust);
j7s_diagnostics_cxx::DiagnosticLevel convertLevel(const uint8_t rosLevel);
uint8_t convertLevel(const j7s_diagnostics_cxx::DiagnosticLevel& level );
std::vector<diagnostic_msgs::msg::KeyValue> convertValues(const rust::Box<j7s_diagnostics_cxx::StringMap>& map);
rust::Box<j7s_diagnostics_cxx::StringMap> convertValues(const std::vector<diagnostic_msgs::msg::KeyValue>& map);
j7s_diagnostics_cxx::DiagnosticStatus fromMsg(const diagnostic_msgs::msg::DiagnosticStatus& msg);
diagnostic_msgs::msg::DiagnosticStatus toMsg(const j7s_diagnostics_cxx::DiagnosticStatus& status);

View File

@ -26,9 +26,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.6.2" version = "4.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd059f9da4f5c36b3787f65d38ccaab1cc315f07b01f89abc8359ee6a8205011" checksum = "d91e0c145792ef73a6ad36d27c75ac09f1832222a3c209689d90f534685ee5b7"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
] ]
@ -160,7 +160,7 @@ dependencies = [
[[package]] [[package]]
name = "j7s_diagnostics" name = "j7s_diagnostics"
version = "0.1.0" version = "0.1.0"
source = "git+https://git.jpace121.net/public/j7s_diagnostics.git?branch=main#f40b64151807cbca03a021e66cb3bf3558e4619d" source = "git+https://git.jpace121.net/public/j7s_diagnostics.git?branch=main#5f5e9c3b8aa82fd6d14f7a7931604a225fd6b551"
dependencies = [ dependencies = [
"limbo_graph", "limbo_graph",
"thiserror", "thiserror",
@ -258,9 +258,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "3.0.1" version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5edbec4ed188954a10c12c038215f8ce7606b2d5c973cd8dc43e8795065c5f2f" checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View File

@ -2,7 +2,7 @@
use anyhow::anyhow; use anyhow::anyhow;
use std::collections::BTreeMap; use std::collections::BTreeMap;
#[cxx::bridge] #[cxx::bridge(namespace = "j7s_diagnostics_cxx")]
mod ffi { mod ffi {
// Shared structs whose fields can be seen in both C++ and Rust. // Shared structs whose fields can be seen in both C++ and Rust.
#[derive(Clone, PartialOrd, PartialEq, Default)] #[derive(Clone, PartialOrd, PartialEq, Default)]
@ -21,13 +21,16 @@ mod ffi {
name: String, name: String,
message: String, message: String,
hardware_id: String, hardware_id: String,
values: Box<StringMap> values: Box<StringMap>,
} }
extern "Rust" { extern "Rust" {
// Functions in here can be seen on both sides, but are // Functions in here can be seen on both sides, but are
// defined in rust. // defined in rust.
// types can be seen in both languages, but only opaquely in C++. // types can be seen in both languages, but only opaquely in C++.
#[Self = "DiagnosticStatus"]
fn build() -> DiagnosticStatus;
type OptionalString; type OptionalString;
#[Self = "OptionalString"] #[Self = "OptionalString"]
fn some(val: String) -> Box<OptionalString>; fn some(val: String) -> Box<OptionalString>;
@ -36,12 +39,12 @@ mod ffi {
fn is_some(self: &OptionalString) -> bool; fn is_some(self: &OptionalString) -> bool;
fn value(self: &OptionalString) -> Result<String>; fn value(self: &OptionalString) -> Result<String>;
type StringMap; type StringMap;
#[Self = "StringMap"] #[Self = "StringMap"]
fn build() -> Box<StringMap>; fn build() -> Box<StringMap>;
fn get(self: &StringMap, key: &String) -> Box<OptionalString>; fn get(self: &StringMap, key: &String) -> Box<OptionalString>;
fn insert(self: &mut StringMap, key: String, value: String); fn insert(self: &mut StringMap, key: String, value: String);
fn keys(self: &StringMap) -> Vec<String>;
type DiagnosticTree; type DiagnosticTree;
#[Self = "DiagnosticTree"] #[Self = "DiagnosticTree"]
@ -53,12 +56,63 @@ mod ffi {
} }
} }
fn diagnostic_status_to_rust(cpp: &crate::ffi::DiagnosticStatus) -> j7s_diagnostics::DiagnosticStatus { fn diagnostic_level_to_rust(cpp: &crate::ffi::DiagnosticLevel) -> j7s_diagnostics::DiagnosticLevel {
todo!() match cpp {
&crate::ffi::DiagnosticLevel::UNSET => j7s_diagnostics::DiagnosticLevel::UNSET,
&crate::ffi::DiagnosticLevel::OK => j7s_diagnostics::DiagnosticLevel::OK,
&crate::ffi::DiagnosticLevel::WARN => j7s_diagnostics::DiagnosticLevel::WARN,
&crate::ffi::DiagnosticLevel::ERROR => j7s_diagnostics::DiagnosticLevel::ERROR,
&crate::ffi::DiagnosticLevel::STALE => j7s_diagnostics::DiagnosticLevel::STALE,
_ => j7s_diagnostics::DiagnosticLevel::UNSET,
}
} }
fn diagnostic_status_from_rust(rust: &j7s_diagnostics::DiagnosticStatus) -> crate::ffi::DiagnosticStatus { fn diagnostic_level_from_rust(
todo!() rust: &j7s_diagnostics::DiagnosticLevel,
) -> crate::ffi::DiagnosticLevel {
match rust {
&j7s_diagnostics::DiagnosticLevel::UNSET => crate::ffi::DiagnosticLevel::UNSET,
&j7s_diagnostics::DiagnosticLevel::OK => crate::ffi::DiagnosticLevel::OK,
&j7s_diagnostics::DiagnosticLevel::WARN => crate::ffi::DiagnosticLevel::WARN,
&j7s_diagnostics::DiagnosticLevel::ERROR => crate::ffi::DiagnosticLevel::ERROR,
&j7s_diagnostics::DiagnosticLevel::STALE => crate::ffi::DiagnosticLevel::STALE,
}
}
fn diagnostic_status_to_rust(
cpp: &crate::ffi::DiagnosticStatus,
) -> j7s_diagnostics::DiagnosticStatus {
j7s_diagnostics::DiagnosticStatus::new(
diagnostic_level_to_rust(&cpp.level),
cpp.name.clone(),
cpp.message.clone(),
cpp.hardware_id.clone(),
cpp.values.to_btree(),
)
}
fn diagnostic_status_from_rust(
rust: &j7s_diagnostics::DiagnosticStatus,
) -> crate::ffi::DiagnosticStatus {
crate::ffi::DiagnosticStatus {
level: diagnostic_level_from_rust(&rust.level()),
name: rust.name(),
message: rust.message(),
hardware_id: rust.hardware_id(),
values: Box::new(StringMap::from_btree(rust.values())),
}
}
impl crate::ffi::DiagnosticStatus {
fn build() -> Self {
Self {
level: crate::ffi::DiagnosticLevel::default(),
name: String::default(),
message: String::default(),
hardware_id: String::default(),
values: StringMap::build(),
}
}
} }
#[derive(Clone)] #[derive(Clone)]
@ -69,13 +123,14 @@ pub struct DiagnosticTree {
impl DiagnosticTree { impl DiagnosticTree {
pub fn build() -> Box<DiagnosticTree> { pub fn build() -> Box<DiagnosticTree> {
let graph = j7s_diagnostics::DiagnosticGraph::new(); let graph = j7s_diagnostics::DiagnosticGraph::new();
let tree = DiagnosticTree { let tree = DiagnosticTree { graph: graph };
graph: graph
};
Box::new(tree) Box::new(tree)
} }
pub fn add_statuses(self: &mut DiagnosticTree, statuses: &Vec<crate::ffi::DiagnosticStatus>) -> anyhow::Result<()> { pub fn add_statuses(
self: &mut DiagnosticTree,
statuses: &Vec<crate::ffi::DiagnosticStatus>,
) -> anyhow::Result<()> {
let mut converted_statuses = Vec::new(); let mut converted_statuses = Vec::new();
for status in statuses.iter() { for status in statuses.iter() {
converted_statuses.push(diagnostic_status_to_rust(&status)); converted_statuses.push(diagnostic_status_to_rust(&status));
@ -99,21 +154,17 @@ impl DiagnosticTree {
#[derive(Clone)] #[derive(Clone)]
pub struct OptionalString { pub struct OptionalString {
val: Option<String> val: Option<String>,
} }
impl OptionalString { impl OptionalString {
pub fn some(val: String) -> Box<Self> { pub fn some(val: String) -> Box<Self> {
let raw = Self { let raw = Self { val: Some(val) };
val: Some(val)
};
Box::new(raw) Box::new(raw)
} }
pub fn none() -> Box<Self> { pub fn none() -> Box<Self> {
let raw = Self { let raw = Self { val: None };
val: None
};
Box::new(raw) Box::new(raw)
} }
@ -131,17 +182,29 @@ impl OptionalString {
#[derive(Clone)] #[derive(Clone)]
pub struct StringMap { pub struct StringMap {
map: BTreeMap<String, String> map: BTreeMap<String, String>,
} }
impl StringMap { impl StringMap {
pub fn build() -> Box<Self> { pub fn build() -> Box<Self> {
let raw = Self { let raw = Self {
map: BTreeMap::<String, String>::new() map: BTreeMap::<String, String>::new(),
}; };
Box::new(raw) Box::new(raw)
} }
pub fn from_btree(map: BTreeMap<String, String>) -> Self {
Self { map: map }
}
fn keys(&self) -> Vec<String> {
self.map.keys().map(|s| s.clone()).collect()
}
pub fn to_btree(&self) -> BTreeMap<String, String> {
self.map.clone()
}
pub fn get(&self, key: &String) -> Box<OptionalString> { pub fn get(&self, key: &String) -> Box<OptionalString> {
match self.map.get(key) { match self.map.get(key) {
None => OptionalString::none(), None => OptionalString::none(),

View File

@ -9,6 +9,8 @@
<buildtool_depend>ament_cmake</buildtool_depend> <buildtool_depend>ament_cmake</buildtool_depend>
<depend>diagnostic_msgs</depend>
<export> <export>
<build_type>ament_cmake</build_type> <build_type>ament_cmake</build_type>
</export> </export>

99
src/conversions.cpp Normal file
View File

@ -0,0 +1,99 @@
#include <j7s_diagnostics_ros/conversions.hpp>
std::string convertString(const rust::String& rust)
{
auto copy = rust;
return std::string(copy.c_str());
}
j7s_diagnostics_cxx::DiagnosticLevel convertLevel(const uint8_t rosLevel)
{
switch(rosLevel)
{
case diagnostic_msgs::msg::DiagnosticStatus::OK:
return j7s_diagnostics_cxx::DiagnosticLevel::OK;
case diagnostic_msgs::msg::DiagnosticStatus::WARN:
return j7s_diagnostics_cxx::DiagnosticLevel::WARN;
case diagnostic_msgs::msg::DiagnosticStatus::ERROR:
return j7s_diagnostics_cxx::DiagnosticLevel::ERROR;
case diagnostic_msgs::msg::DiagnosticStatus::STALE:
default:
return j7s_diagnostics_cxx::DiagnosticLevel::STALE;
}
}
uint8_t convertLevel(const j7s_diagnostics_cxx::DiagnosticLevel& level )
{
switch(level)
{
case j7s_diagnostics_cxx::DiagnosticLevel::OK:
return diagnostic_msgs::msg::DiagnosticStatus::OK;
case j7s_diagnostics_cxx::DiagnosticLevel::WARN:
return diagnostic_msgs::msg::DiagnosticStatus::WARN;
case j7s_diagnostics_cxx::DiagnosticLevel::ERROR:
return diagnostic_msgs::msg::DiagnosticStatus::ERROR;
case j7s_diagnostics_cxx::DiagnosticLevel::STALE:
case j7s_diagnostics_cxx::DiagnosticLevel::UNSET:
default:
return diagnostic_msgs::msg::DiagnosticStatus::STALE;
}
}
std::vector<diagnostic_msgs::msg::KeyValue> convertValues(const rust::Box<j7s_diagnostics_cxx::StringMap>& map)
{
std::vector<diagnostic_msgs::msg::KeyValue> toReturn;
const rust::Vec<rust::String> keys = map->keys();
if(keys.empty())
{
return toReturn;
}
for(const auto& key : keys)
{
const auto value = map->get(key);
diagnostic_msgs::msg::KeyValue msg;
msg.key = convertString(key);
msg.value = std::string(value->value().c_str());
toReturn.emplace_back(msg);
}
return toReturn;
}
rust::Box<j7s_diagnostics_cxx::StringMap> convertValues(const std::vector<diagnostic_msgs::msg::KeyValue>& map)
{
rust::Box<j7s_diagnostics_cxx::StringMap> toReturn = j7s_diagnostics_cxx::StringMap::build();
for(const auto& pair: map)
{
toReturn->insert(rust::String(pair.key), rust::String(pair.value));
}
return toReturn;
}
j7s_diagnostics_cxx::DiagnosticStatus fromMsg(const diagnostic_msgs::msg::DiagnosticStatus& msg)
{
auto toReturn = j7s_diagnostics_cxx::DiagnosticStatus::build();
toReturn.level = convertLevel(msg.level);
toReturn.name = rust::String(msg.name);
toReturn.message = rust::String(msg.message);
toReturn.hardware_id = rust::String(msg.hardware_id);
toReturn.values = convertValues(msg.values);
return toReturn;
}
diagnostic_msgs::msg::DiagnosticStatus toMsg(const j7s_diagnostics_cxx::DiagnosticStatus& status)
{
diagnostic_msgs::msg::DiagnosticStatus toReturn;
toReturn.level = convertLevel(status.level);
toReturn.name = convertString(status.name);
toReturn.message = convertString(status.message);
toReturn.hardware_id = convertString(status.hardware_id);
toReturn.values = convertValues(status.values);
return toReturn;
}

View File

@ -1,17 +1,16 @@
#include "j7s_diagnostics_cxx_bridge/lib.h"
#include <iostream> #include <iostream>
#include <j7s_diagnostics_ros/conversions.hpp>
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
const auto my_string = OptionalString::some(rust::String("test")); const auto my_string = j7s_diagnostics_cxx::OptionalString::some(rust::String("test"));
if(my_string->is_some()) if(my_string->is_some())
{ {
auto as_string = my_string->value(); auto as_string = my_string->value();
std::cout << as_string.c_str() << std::endl; std::cout << as_string.c_str() << std::endl;
} }
auto my_map = StringMap::build(); auto my_map = j7s_diagnostics_cxx::StringMap::build();
my_map->insert(rust::String("a"), rust::String("b")); my_map->insert(rust::String("a"), rust::String("b"));
return 0; return 0;