From 470ad6fdb14cfb1e1de633dcff636ca3d3c43897 Mon Sep 17 00:00:00 2001 From: hzy <641773357@qq.com> Date: Fri, 23 May 2025 17:16:10 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat=20=E5=88=9D=E5=A7=8B=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 完成rust语言接口封装 - 完成示例工程的编写 - 完成说用说明 --- Cargo.toml | 10 ++ build.rs | 94 +++++++++++++++ doc/README.MD | 61 ++++++++++ example/Cargo.toml | 19 +++ example/build.rs | 67 +++++++++++ example/example_type.cpp | 28 +++++ example/example_type.h | 29 +++++ example/example_type.idl | 6 + example/example_typeTypeSupport.cpp | 175 ++++++++++++++++++++++++++++ example/example_typeTypeSupport.h | 14 +++ example/pub_example.rs | 122 +++++++++++++++++++ example/sub_example.rs | 125 ++++++++++++++++++++ src/lib.rs | 5 + 13 files changed, 755 insertions(+) create mode 100644 Cargo.toml create mode 100644 build.rs create mode 100644 doc/README.MD create mode 100644 example/Cargo.toml create mode 100644 example/build.rs create mode 100644 example/example_type.cpp create mode 100644 example/example_type.h create mode 100644 example/example_type.idl create mode 100644 example/example_typeTypeSupport.cpp create mode 100644 example/example_typeTypeSupport.h create mode 100644 example/pub_example.rs create mode 100644 example/sub_example.rs create mode 100644 src/lib.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..33c46b1 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "travodds_rust" +version = "0.1.0" +edition = "2024" +build = "build.rs" + +[dependencies] + +[build-dependencies] +bindgen = "*" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..c01048a --- /dev/null +++ b/build.rs @@ -0,0 +1,94 @@ +use std::env; +use std::path::PathBuf; +use bindgen::callbacks::{ParseCallbacks, EnumVariantValue}; + +#[derive(Debug)] +struct RenameCallbacks; + +impl ParseCallbacks for RenameCallbacks { + fn item_name(&self, original_item_name: &str) -> Option { + let mut renamed = original_item_name.to_string(); + if original_item_name == "DDS_CString" { + renamed = "DDSCString".to_string(); + } + // 替换DDS_LongLong为LLong + renamed = renamed.replace("DDS_LongLong", "LLong"); + // 替换DDS_LongDouble为LDouble + renamed = renamed.replace("DDS_LongDouble", "LDouble"); + // 替换DDS_为空 + renamed = renamed.replace("DDS_", ""); + // 替换_iterater为Iterator + renamed = renamed.replace("_iterator", "Iterator"); + Some(renamed) + } + + fn enum_variant_name( + &self, + _enum_name: Option<&str>, + original_variant_name: &str, + _variant_value: EnumVariantValue, + ) -> Option { + Some(match original_variant_name { + // 完全匹配重命名 + + // 模式匹配重命名 + s if s.starts_with("DDS_") => { + s.trim_start_matches("DDS_").to_string() + }, + + // 保留其他名称不变 + _ => original_variant_name.to_string(), + }) + } +} + +fn main() { + // Get travodds_c library path from environment variable + let travodds_c_path = env::var("TRAVODDS_C_PATH").unwrap_or_else(|_| { + println!("Please set the TRAVODDS_C_PATH environment variable to the path of the travodds_c library."); + std::process::exit(1); + }); + // Tell cargo to look for shared libraries in the specified directory + println!("cargo:rustc-link-search={}/lib", travodds_c_path); + + // Link to travodds_c library (update as needed) + if cfg!(debug_assertions) { + println!("cargo:rustc-link-lib=travoddscd"); + } else { + // Release mode link travodds + println!("cargo:rustc-link-lib=travodds"); + } + + // Add include path for travodds_c headers + let travodds_include = format!("{}/include", travodds_c_path); + + let bindings = bindgen::Builder::default() + // Use all headers in travodds_c/include + .clang_arg(format!("-I{}", travodds_include)) + .header(format!("{}/dds_basetype.h", travodds_include)) + .header(format!("{}/dds_c_string.h", travodds_include)) + .header(format!("{}/dds_c_wstring.h", travodds_include)) + .header(format!("{}/dds_builtin_topic.h", travodds_include)) + .header(format!("{}/dds_infrastructure.h", travodds_include)) + .header(format!("{}/dds_domain_participant_factory.h", travodds_include)) + .header(format!("{}/dds_domain_participant.h", travodds_include)) + .header(format!("{}/dds_topic.h", travodds_include)) + .header(format!("{}/dds_typesupport.h", travodds_include)) + .header(format!("{}/dds_publisher.h", travodds_include)) + .header(format!("{}/dds_subscriber.h", travodds_include)) + .header(format!("{}/dds_datawriter.h", travodds_include)) + .header(format!("{}/dds_datareader.h", travodds_include)) + .header(format!("{}/dds_waitset.h", travodds_include)) + .header(format!("{}/dds_condition.h", travodds_include)) + .rustified_enum(".*") + .parse_callbacks(Box::new(RenameCallbacks)) + .derive_default(true) + .generate() + .expect("Unable to generate bindings"); + + // Write the bindings to the src directory + let out_path = PathBuf::from(env::current_dir().unwrap()).join("src"); + bindings + .write_to_file(out_path.join("travodds.rs")) + .expect("Couldn't write bindings!"); +} \ No newline at end of file diff --git a/doc/README.MD b/doc/README.MD new file mode 100644 index 0000000..9bffa28 --- /dev/null +++ b/doc/README.MD @@ -0,0 +1,61 @@ +# travodds_rust + +## 1. 概述 + +本开发库为travodds开发库(C++)接口的rust语言封装库,使用本库使得用户能够在rust的工程中使用travodds。 + +## 2. 编译安装 + +- 安装依赖: + - [travodds核心库](https://gitee.com/agiros-dds/travodds.git); + - [travodds-c库](https://gitee.com/agiros-dds/travodds-c.git); + - rust + - rust-bindgen + +- `git clone https://gitee.com/agiros-dds/travodds-rust.git` +- 设置环境变量 + - cmd: `set TRAVODDS_C_PATH=/path/to/travodds_c && set TRAVODDS_PATH=/path/to/travodds` + - powershell: `$env:TRAVODDS_C_PATH="/path/to/travodds";$env:TRAVODDS_PATH="/path/to/travodds_c";` + - Linux: `export TRAVODDS_C_PATH=/path/to/travodds_c && EXPORT TRAVODDS_PATH=/path/to/travodds` +- 编译生成rust绑定:`cargo build` +- 编译成功后在src目录中: + - travodds.rs用于rust开发的travodds接口绑定; + +## 3. 使用示例 + +### 3.1. 源码结构 + +- 示例代码在example目录中; +- example_type.idl为示例IDL文件; +- 以下文件为编译器自动生成的文件: + - example_type.h + - example_type.cpp + - example_typeTypeSupport.h + - example_typeTypeSupport.cpp + - build.rs + - Cargo.toml(略有修改) +- 发送端示例代码:pub_example.rs +- 接收端示例代码:sub_example.rs + +### 3.2. 编译示例 + +- 设置环境变量:TRAVODDS_PATH、TRAVODDS_C_PATH +- `cargo build` +- 运行成功后在target/debug下会有pub_example/sub_example的可执行程序; + +### 3.3. 运行示例 + +- 发送端运行pub_example,观察到1s中发送一包序号递增的数据; +- 接收端运行sub_example,观察到1s中接收到一包序号递增的数据; + +## 4. 使用流程 + +- 前提:travodds、travodds_rust、travoddsgen安装完成; +- 定义IDL文件; +- 使用travoddsgen生成rust语言接口,详细参见travoddsgen手册; +- 将生成的代码生成类型开发库; +- 在业务工程用使用类型开发库以及本库进行接口进行开发; + +## 5. 接口说明 + +- TODO 待补充 diff --git a/example/Cargo.toml b/example/Cargo.toml new file mode 100644 index 0000000..8a98f6b --- /dev/null +++ b/example/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "example_type" +version = "0.1.0" +edition = "2024" +build = "build.rs" + +[[bin]] +name = "pub_example" +path = "pub_example.rs" + +[[bin]] +name = "sub_example" +path = "sub_example.rs" + +[dependencies] + +[build-dependencies] +cc = "1.0" +bindgen = "*" diff --git a/example/build.rs b/example/build.rs new file mode 100644 index 0000000..7172b90 --- /dev/null +++ b/example/build.rs @@ -0,0 +1,67 @@ +use std::env; +use std::path::PathBuf; +use bindgen::callbacks::{ParseCallbacks}; + +#[derive(Debug)] +struct RenameCallbacks; + +impl ParseCallbacks for RenameCallbacks { + fn item_name(&self, original_item_name: &str) -> Option { + let mut renamed = original_item_name.to_string(); + if original_item_name == "DDS_CString" { + renamed = "DDSCString".to_string(); + } + renamed = renamed.replace("DDS_LongLong", "LLong"); + renamed = renamed.replace("DDS_LongDouble", "LDouble"); + renamed = renamed.replace("DDS_", ""); + renamed = renamed.replace("_iterator", "Iterator"); + Some(renamed) + } +} + +fn main() { + let travodds_c_path = env::var("TRAVODDS_C_PATH").unwrap_or_else(|_| { + println!("Please set the TRAVODDS_C_PATH environment variable to the path of the travodds_c library."); + std::process::exit(1); + }); + let travodds_path = env::var("TRAVODDS_PATH").unwrap_or_else(|_| { + println!("Please set the TRAVODDS_PATH environment variable to the path of the travodds_c library."); + std::process::exit(1); + }); + println!("cargo:rustc-link-search=native={}/lib", travodds_c_path); + println!("cargo:rustc-link-search=native={}/lib", travodds_path); + println!("cargo:rustc-link-lib=static=travoddscz"); + println!("cargo:rustc-link-lib=static=libagidds-1.1"); + + cc::Build::new() + .file("example_type.cpp") + .file("example_typeTypeSupport.cpp") + .include(format!("{}/include", travodds_c_path)) + .include(format!("{}/include", travodds_path)) + .compile("example_type"); + + let bindings = bindgen::Builder::default() + .clang_arg(format!("-I{}/include", travodds_c_path)) + .header("example_type.h") + .header("example_typeTypeSupport.h") + .blocklist_file(".*dds_basetype.h") + .blocklist_file(".*dds_c_string.h") + .blocklist_file(".*dds_c_wstring.h") + .blocklist_file(".*dds_c_sequence.h") + .blocklist_file(".*dds_c_map.h") + .blocklist_file(".*stdint.h") + .blocklist_file(".*stddef.h") + .blocklist_file(".*stdbool.h") + .blocklist_file(".*corecrt.h") + .blocklist_file(".*vcruntime.h") + .blocklist_file(".*sal.h") + .blocklist_file(".*vadefs.h") + .parse_callbacks(Box::new(RenameCallbacks)) + .generate() + .expect("Unable to generate bindings"); + let out_path = PathBuf::from(env::current_dir().unwrap()); + bindings + .write_to_file(out_path.join("example_type.rs")) + .expect("Couldn't write example_type bindings!"); + println!("cargo:rustc-link-lib=static=example_type"); +} diff --git a/example/example_type.cpp b/example/example_type.cpp new file mode 100644 index 0000000..5141533 --- /dev/null +++ b/example/example_type.cpp @@ -0,0 +1,28 @@ +#include "example_type.h" +#include "dds_basetype.h" +#include "dds_c_string.h" +#include "dds_c_sequence.cpp" +#include "dds_c_map.cpp" + +#ifdef __cplusplus +extern "C" { +#endif + +DDSCSequenceImpl(example_type); + +example_type* example_type_create() { + return new example_type(); +} + +void example_type_destroy(example_type* obj) { + delete obj; +} + +example_type* example_type_copy(example_type* obj, const example_type* other) { + *obj = *other; + return obj; +} + +#ifdef __cplusplus +} +#endif diff --git a/example/example_type.h b/example/example_type.h new file mode 100644 index 0000000..6ed38f5 --- /dev/null +++ b/example/example_type.h @@ -0,0 +1,29 @@ +#ifndef example_type_H +#define example_type_H + +#include "dds_c_sequence.h" +#include "dds_c_map.h" +#include "dds_c_string.h" +#include "dds_c_wstring.h" +#include "dds_basetype.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct example_type { + DDS_CString color; + DDS_Long x; + DDS_Long y; +} example_type; + +DDSCSequence(example_type); + +example_type* example_type_create(); +void example_type_destroy(example_type* self); +example_type* example_type_copy(example_type* self, const example_type* other); + +#ifdef __cplusplus +} +#endif +#endif /* example_type_H */ diff --git a/example/example_type.idl b/example/example_type.idl new file mode 100644 index 0000000..61eadc9 --- /dev/null +++ b/example/example_type.idl @@ -0,0 +1,6 @@ +struct example_type +{ + string<128> color; + long x; + long y; +}; \ No newline at end of file diff --git a/example/example_typeTypeSupport.cpp b/example/example_typeTypeSupport.cpp new file mode 100644 index 0000000..68a3b04 --- /dev/null +++ b/example/example_typeTypeSupport.cpp @@ -0,0 +1,175 @@ +#include "example_type.h" +#include "dcps/topic/typesupport.h" + +USING_AGIDDS_NAMESPACE + +#ifdef __cplusplus +extern "C" { +#endif + +class example_typeTypeSupport : public AGIDDS::TypeSupport { +public: + static example_typeTypeSupport* get_instance(); + + virtual void* create_data() override; + + virtual void delete_data(void* data) override; + + virtual int copy_data(void* dst, void* src) override; + + virtual unsigned int get_serialized_data_size(void* data, unsigned int currentAlignment) override; + + virtual unsigned int get_max_serialized_data_size(void* data, unsigned int currentAlignment) override; + + virtual int serialize_data(void* data, AGIDDSCdrSerializer* cdr, int endian) override; + + virtual int deserialize_data(void* data, AGIDDSCdrDeserializer* cdr, int endian) override; + + virtual AGIDDS::TypeObject* get_typeobject() override; + + virtual AGIDDS::ReturnCode_t get_instancehandle(void* data, AGIDDSCdrSerializer* cdr, AGIDDS::InstanceHandle_t& iHandle, bool forceMd5 = false) override; + + virtual bool has_key() override; + + virtual const char* get_typename() override; + + virtual int MakeKey(const void* data, AGIDDS::InstanceHandle_t& iHandle, bool forceMd5 = false) override; + + int serialize_key(void* data, AGIDDSCdrSerializer* cdr, int endian); + +private: + example_typeTypeSupport() = default; +}; + +example_typeTypeSupport* example_typeTypeSupport::get_instance() { + static example_typeTypeSupport instance; + return &instance; +} + +void* example_typeTypeSupport::create_data() { + return new example_type(); +} + +void example_typeTypeSupport::delete_data(void* data) { + delete static_cast(data); +} + +int example_typeTypeSupport::copy_data(void* dst, void* src) { + example_type* dstData = static_cast(dst); + example_type* srcData = static_cast(src); + *dstData = *srcData; + return 0; +} + +unsigned int example_typeTypeSupport::get_serialized_data_size(void* data, unsigned int currentAlignment) { + example_type* structData = static_cast(data); + unsigned int initialAlignment = currentAlignment; + unsigned int tmpAlignment = 0; + currentAlignment += AGIDDSCdrSerializer::getStringSize(structData->color.size() + 1, currentAlignment); + currentAlignment += AGIDDSCdrSerializer::getBaseTypeSize(sizeof(DDS_Long), currentAlignment); + currentAlignment += AGIDDSCdrSerializer::getBaseTypeSize(sizeof(DDS_Long), currentAlignment); + return currentAlignment - initialAlignment; +} + +unsigned int example_typeTypeSupport::get_max_serialized_data_size(void* data, unsigned int currentAlignment) { + example_type* structData = static_cast(data); + unsigned int initialAlignment = currentAlignment; + unsigned int tmpAlignment = 0; + currentAlignment += AGIDDSCdrSerializer::getStringSize(128, currentAlignment); + currentAlignment += AGIDDSCdrSerializer::getBaseTypeSize(sizeof(DDS_Long), currentAlignment); + currentAlignment += AGIDDSCdrSerializer::getBaseTypeSize(sizeof(DDS_Long), currentAlignment); + return currentAlignment - initialAlignment; +} + +int example_typeTypeSupport::serialize_data(void* data, AGIDDSCdrSerializer* cdr, int endian) { + example_type* structData = static_cast(data); + uint32_t tmpLength = 0; + if (!cdr->serializeString(structData->color)) { + fprintf(stderr, "Serialization failed for field: structData->color\n"); + return -1; + } + if (!cdr->serializeBaseType(structData->x)) { + fprintf(stderr, "Serialization failed for field: structData->x\n"); + return -1; + } + if (!cdr->serializeBaseType(structData->y)) { + fprintf(stderr, "Serialization failed for field: structData->y\n"); + return -1; + } + return 0; +} + +int example_typeTypeSupport::deserialize_data(void* data, AGIDDSCdrDeserializer* cdr, int endian) { + example_type* structData = static_cast(data); + unsigned int tmpLength = 0; + char tmpCharEnum = 0; + short tmpShortEnum = 0; + int tmpIntEnum = 0; + if (!cdr->deserializeString(structData->color)) { + fprintf(stderr, "Deserialization failed for field: structData->color\n"); + return -1; + } + if (!cdr->deserializeBaseType(structData->x)) { + fprintf(stderr, "Deserialization failed for field: structData->x\n"); + return -1; + } + if (!cdr->deserializeBaseType(structData->y)) { + fprintf(stderr, "Deserialization failed for field: structData->y\n"); + return -1; + } + return 0; +} + +TypeObject* example_typeTypeSupport::get_typeobject() { + return nullptr; +} + +int example_typeTypeSupport::serialize_key(void* data, AGIDDSCdrSerializer* cdr, int endian) { + example_type* structData = static_cast(data); + bool memberHasKey = false; + return 0; +} + +int example_typeTypeSupport::MakeKey(const void* data, InstanceHandle_t& iHandle, bool forceMd5) { + unsigned int serializedSize = get_serialized_data_size((void*)data, 0); + SerializedBuffer buffer; + buffer.buffer_size = serializedSize; + buffer.buffer = new char[buffer.buffer_size]; + AGIDDSCdrSerializer cdr(&buffer); + ReturnCode_t ret = get_instancehandle((void*)data, &cdr, iHandle, forceMd5); + delete[] buffer.buffer; + return ret == RETCODE_OK ? 0 : -1; +} + +ReturnCode_t example_typeTypeSupport::get_instancehandle(void* data, AGIDDSCdrSerializer* cdr, InstanceHandle_t& iHandle, bool forceMd5) { + if (!has_key()) { + iHandle = HANDLE_NIL; + return RETCODE_OK; + } + int ret = serialize_key(data, cdr, forceMd5); + if (ret != 0) { + fprintf(stderr, "Failed to serialize key.\n"); + return RETCODE_ERROR; + } + if (!cdr->getKeyHash((char*)&iHandle, forceMd5)) { + fprintf(stderr, "Failed to get key hash\n"); + return RETCODE_ERROR; + } + return RETCODE_OK; +} + +bool example_typeTypeSupport::has_key() { + return false; +} + +const char* example_typeTypeSupport::get_typename() { + return "example_type"; +} + +void* example_typeTypeSupport_get_instance() { + return example_typeTypeSupport::get_instance(); +} + +#ifdef __cplusplus +} +#endif diff --git a/example/example_typeTypeSupport.h b/example/example_typeTypeSupport.h new file mode 100644 index 0000000..ef4d1e6 --- /dev/null +++ b/example/example_typeTypeSupport.h @@ -0,0 +1,14 @@ +#ifndef example_typeTypeSupport_H +#define example_typeTypeSupport_H + + +#ifdef __cplusplus +extern "C" { +#endif + +void* example_typeTypeSupport_get_instance(); + +#ifdef __cplusplus +} +#endif +#endif /* example_typeTypeSupport_H */ diff --git a/example/pub_example.rs b/example/pub_example.rs new file mode 100644 index 0000000..f37ff6c --- /dev/null +++ b/example/pub_example.rs @@ -0,0 +1,122 @@ +use std::{ffi::CString, ptr, thread, time::Duration}; + +include!("../src/travodds.rs"); +include!("example_type.rs"); + +fn main() { + unsafe { + // 1. 获取 DomainParticipantFactory 实例 + let factory = DomainParticipantFactory_get_instance(); + if factory.is_null() { + println!("Failed to get DomainParticipantFactory instance"); + return; + } + + // 2. 创建 DomainParticipant + let participant = DomainParticipantFactory_create_participant( + factory, + 0, + ptr::null_mut(), + ptr::null_mut(), + 0, + ); + if participant.is_null() { + println!("Failed to create DomainParticipant"); + return; + } + + // 3. 注册类型 + let type_name = CString::new("example_type").unwrap(); + let mut ts = example_typeTypeSupport_get_instance(); + let ret = DomainParticipant_registe_type(participant, type_name.as_ptr(), ts); + if ret != RETCODE_OK { + println!("Failed to register type"); + DomainParticipantFactory_delete_participant(factory, participant); + return; + } + + // 4. 创建 Topic + let topic_name = CString::new("example_topic").unwrap(); + let topic = DomainParticipant_create_topic( + participant, + topic_name.as_ptr(), + type_name.as_ptr(), + ptr::null_mut(), + ptr::null_mut(), + 0, + ); + if topic.is_null() { + println!("Failed to create Topic"); + DomainParticipantFactory_delete_participant(factory, participant); + return; + } + + // 5. 创建 Publisher + let publisher = DomainParticipant_create_publisher( + participant, + ptr::null_mut(), + ptr::null_mut(), + 0, + ); + if publisher.is_null() { + println!("Failed to create Publisher"); + DomainParticipant_delete_topic(participant, topic); + DomainParticipantFactory_delete_participant(factory, participant); + return; + } + + // 6. 创建 DataWriter + let writer = Publisher_create_datawriter( + publisher, + topic, + ptr::null_mut(), + ptr::null_mut(), + 0, + ); + if writer.is_null() { + println!("Failed to create DataWriter"); + DomainParticipant_delete_publisher(participant, publisher); + DomainParticipant_delete_topic(participant, topic); + DomainParticipantFactory_delete_participant(factory, participant); + return; + } + + // 7. 构造数据并周期性发送 + let mut data = example_type_create(); + DDSCString_assign_cstr(&mut (*data).color, CString::new("red").unwrap().as_ptr()); + (*data).x = 1; + (*data).y = 2; + + // 转化为 void* 指针 + let data_ptr = data as *mut std::ffi::c_void; + + let mut handle = DataWriter_register_instance(writer, data_ptr); + + println!("Start sending data every 1s..."); + for i in 0..100 { + (*data).x = i + 1; + let ret = DataWriter_write(writer, data_ptr, &mut handle); + if ret != RETCODE_OK { + println!("Failed to write data at x={}", (*data).x); + } else { + // 获取 color 字符串 + let color_ptr = DDSCString_c_str(&(*data).color); + let color = if !color_ptr.is_null() { + std::ffi::CStr::from_ptr(color_ptr).to_string_lossy().into_owned() + } else { + String::from("") + }; + println!("Data written: color={}, x={}, y={}", color, (*data).x, (*data).y); + } + thread::sleep(Duration::from_secs(1)); + } + + Publisher_delete_datawriter(publisher, writer); + DomainParticipant_delete_publisher(participant, publisher); + DomainParticipant_delete_topic(participant, topic); + DomainParticipantFactory_delete_participant(factory, participant); + DomainParticipantFactory_finalize_instance(); + + example_type_destroy(data); + } +} \ No newline at end of file diff --git a/example/sub_example.rs b/example/sub_example.rs new file mode 100644 index 0000000..0020731 --- /dev/null +++ b/example/sub_example.rs @@ -0,0 +1,125 @@ +use std::{ffi::CString, ptr, thread, time::Duration}; + +include!("../src/travodds.rs"); +include!("example_type.rs"); + +fn main() { + unsafe { + // 1. 获取 DomainParticipantFactory 实例 + let factory = DomainParticipantFactory_get_instance(); + if factory.is_null() { + println!("Failed to get DomainParticipantFactory instance"); + return; + } + + // 2. 创建 DomainParticipant + let participant = DomainParticipantFactory_create_participant( + factory, + 0, + ptr::null_mut(), + ptr::null_mut(), + 0, + ); + if participant.is_null() { + println!("Failed to create DomainParticipant"); + return; + } + + // 3. 注册类型 + let type_name = CString::new("example_type").unwrap(); + let ts = example_typeTypeSupport_get_instance(); + let ret = DomainParticipant_registe_type(participant, type_name.as_ptr(), ts); + if ret != RETCODE_OK { + println!("Failed to register type"); + DomainParticipantFactory_delete_participant(factory, participant); + return; + } + + // 4. 创建 Topic + let topic_name = CString::new("example_topic").unwrap(); + let topic = DomainParticipant_create_topic( + participant, + topic_name.as_ptr(), + type_name.as_ptr(), + ptr::null_mut(), + ptr::null_mut(), + 0, + ); + if topic.is_null() { + println!("Failed to create Topic"); + DomainParticipantFactory_delete_participant(factory, participant); + return; + } + + let topic_desc = Topic_as_topicdescription(topic); + if topic_desc.is_null() { + println!("Failed to get TopicDescription"); + DomainParticipant_delete_topic(participant, topic); + DomainParticipantFactory_delete_participant(factory, participant); + return; + } + + // 5. 创建 Subscriber + let subscriber = DomainParticipant_create_subscriber( + participant, + ptr::null_mut(), + ptr::null_mut(), + 0, + ); + if subscriber.is_null() { + println!("Failed to create Subscriber"); + DomainParticipant_delete_topic(participant, topic); + DomainParticipantFactory_delete_participant(factory, participant); + return; + } + + // 6. 创建 DataReader + let reader = Subscriber_create_datareader( + subscriber, + topic_desc, + ptr::null_mut(), + ptr::null_mut(), + 0, + ); + if reader.is_null() { + println!("Failed to create DataReader"); + DomainParticipant_delete_subscriber(participant, subscriber); + DomainParticipant_delete_topic(participant, topic); + DomainParticipantFactory_delete_participant(factory, participant); + return; + } + + // 7. 等待数据 + let mut data = example_type_create(); + + let mut data_ptr = data as *mut std::ffi::c_void; + + for _ in 0..1000 { + let mut sample_info = SampleInfo::default(); + let ret = DataReader_take_next_sample(reader, data_ptr, &mut sample_info); + if ret == RETCODE_OK { + // 获取 color 字符串 + let color_ptr = DDSCString_c_str(&(*data).color); + let color = if !color_ptr.is_null() { + std::ffi::CStr::from_ptr(color_ptr).to_string_lossy().into_owned() + } else { + String::from("") + }; + println!("Received data: color={}, x={}, y={}", color, (*data).x, (*data).y); + } else if ret == RETCODE_NO_DATA { + println!("No data available"); + } else if ret == RETCODE_ERROR { + println!("Failed to take data"); + } + thread::sleep(Duration::from_secs(1)); + } + + Subscriber_delete_datareader(subscriber, reader); + DomainParticipant_delete_subscriber(participant, subscriber); + DomainParticipant_delete_topic(participant, topic); + DomainParticipantFactory_delete_participant(factory, participant); + DomainParticipantFactory_finalize_instance(); + + example_type_destroy(data); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..5e2ca51 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,5 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +include!("travodds.rs"); -- Gitee From 8d98d8e47fbd63e9ba7355fbb2b026f2f0b716e1 Mon Sep 17 00:00:00 2001 From: hzy <641773357@qq.com> Date: Tue, 10 Jun 2025 16:07:11 +0800 Subject: [PATCH 2/4] =?UTF-8?q?fix(build.rs)=20=E4=BF=AE=E8=AF=A5RenameCal?= =?UTF-8?q?lbacks=E7=9A=84item=5Fname=E5=87=BD=E6=95=B0=E4=BB=A5=E5=85=BC?= =?UTF-8?q?=E5=AE=B9bindgen0.72.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.rs | 8 ++++---- doc/README.MD | 9 +++++++-- example/build.rs | 6 +++--- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/build.rs b/build.rs index c01048a..5153e19 100644 --- a/build.rs +++ b/build.rs @@ -1,14 +1,14 @@ use std::env; use std::path::PathBuf; -use bindgen::callbacks::{ParseCallbacks, EnumVariantValue}; +use bindgen::callbacks::{ParseCallbacks, EnumVariantValue, ItemInfo}; #[derive(Debug)] struct RenameCallbacks; impl ParseCallbacks for RenameCallbacks { - fn item_name(&self, original_item_name: &str) -> Option { - let mut renamed = original_item_name.to_string(); - if original_item_name == "DDS_CString" { + fn item_name(&self, original_item_name: ItemInfo<'_>) -> Option { + let mut renamed = String::from(original_item_name.name); + if original_item_name.name == "DDS_CString" { renamed = "DDSCString".to_string(); } // 替换DDS_LongLong为LLong diff --git a/doc/README.MD b/doc/README.MD index 9bffa28..ef6e1b3 100644 --- a/doc/README.MD +++ b/doc/README.MD @@ -9,13 +9,18 @@ - 安装依赖: - [travodds核心库](https://gitee.com/agiros-dds/travodds.git); - [travodds-c库](https://gitee.com/agiros-dds/travodds-c.git); - - rust + - rustup(>=1.87.0) + - Windows: [rustup-init.exe](https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe) + - Linux: `curl https://sh.rustup.rs -sSf | sh` - rust-bindgen + - libclang-dev + - Windows: [LLVM] + - Linux: `apt install libclang-dev` - `git clone https://gitee.com/agiros-dds/travodds-rust.git` - 设置环境变量 - cmd: `set TRAVODDS_C_PATH=/path/to/travodds_c && set TRAVODDS_PATH=/path/to/travodds` - - powershell: `$env:TRAVODDS_C_PATH="/path/to/travodds";$env:TRAVODDS_PATH="/path/to/travodds_c";` + - powershell: `$env:TRAVODDS_C_PATH="/path/to/travodds";$env:TRAVODDS_PATH="/path/to/travodds_c";` - Linux: `export TRAVODDS_C_PATH=/path/to/travodds_c && EXPORT TRAVODDS_PATH=/path/to/travodds` - 编译生成rust绑定:`cargo build` - 编译成功后在src目录中: diff --git a/example/build.rs b/example/build.rs index 7172b90..f38f3ba 100644 --- a/example/build.rs +++ b/example/build.rs @@ -6,9 +6,9 @@ use bindgen::callbacks::{ParseCallbacks}; struct RenameCallbacks; impl ParseCallbacks for RenameCallbacks { - fn item_name(&self, original_item_name: &str) -> Option { - let mut renamed = original_item_name.to_string(); - if original_item_name == "DDS_CString" { + fn item_name(&self, original_item_name: ItemInfo<'_>) -> Option { + let mut renamed = String::from(original_item_name.name); + if original_item_name.name == "DDS_CString" { renamed = "DDSCString".to_string(); } renamed = renamed.replace("DDS_LongLong", "LLong"); -- Gitee From d5d8b03e0cfab2c2f8a8749c7ffe215a55e26dc0 Mon Sep 17 00:00:00 2001 From: hzy <641773357@qq.com> Date: Fri, 13 Jun 2025 13:41:37 +0800 Subject: [PATCH 3/4] =?UTF-8?q?fix(build.rs)=20=E4=BF=AE=E5=A4=8D=E4=BA=86?= =?UTF-8?q?=E5=9C=A8Linux=E4=B8=8A=E7=A4=BA=E4=BE=8B=E7=94=B1=E4=BA=8E?= =?UTF-8?q?=E7=AC=A6=E5=8F=B7=E5=86=B2=E7=AA=81=E3=80=81=E9=93=BE=E6=8E=A5?= =?UTF-8?q?=E5=BA=95=E5=B1=82=E7=9A=84=E5=BA=93=E5=90=8D=E4=B8=8D=E6=AD=A3?= =?UTF-8?q?=E7=A1=AE=E4=BB=A5=E5=8F=8A=E9=9C=80=E8=A6=81=E6=89=8B=E5=8A=A8?= =?UTF-8?q?=E9=93=BE=E6=8E=A5c++=E5=BA=93=E7=9A=84=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E7=BC=96=E8=AF=91=E4=B8=8D=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/README.MD | 4 ++-- example/build.rs | 29 +++++++++++++++-------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/doc/README.MD b/doc/README.MD index ef6e1b3..62a8076 100644 --- a/doc/README.MD +++ b/doc/README.MD @@ -12,10 +12,10 @@ - rustup(>=1.87.0) - Windows: [rustup-init.exe](https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe) - Linux: `curl https://sh.rustup.rs -sSf | sh` - - rust-bindgen - libclang-dev - Windows: [LLVM] - - Linux: `apt install libclang-dev` + - Ubuntu: `apt install libclang-dev` + - CentOS: `yum install clang-devel` - `git clone https://gitee.com/agiros-dds/travodds-rust.git` - 设置环境变量 diff --git a/example/build.rs b/example/build.rs index f38f3ba..7406472 100644 --- a/example/build.rs +++ b/example/build.rs @@ -1,6 +1,6 @@ use std::env; use std::path::PathBuf; -use bindgen::callbacks::{ParseCallbacks}; +use bindgen::callbacks::{ParseCallbacks, ItemInfo}; #[derive(Debug)] struct RenameCallbacks; @@ -17,6 +17,15 @@ impl ParseCallbacks for RenameCallbacks { renamed = renamed.replace("_iterator", "Iterator"); Some(renamed) } + + fn will_parse_macro(&self, _name: &str) -> MacroParsingBehavior { + println!("Ignoring macro: {}", _name); + MacroParsingBehavior::Ignore + } + + fn header_file(&self, _filename: &str) { + println!("+++++++++++++++++++Parsing header file: {}", _filename); + } } fn main() { @@ -31,7 +40,8 @@ fn main() { println!("cargo:rustc-link-search=native={}/lib", travodds_c_path); println!("cargo:rustc-link-search=native={}/lib", travodds_path); println!("cargo:rustc-link-lib=static=travoddscz"); - println!("cargo:rustc-link-lib=static=libagidds-1.1"); + println!("cargo:rustc-link-lib=static=travoddsstatic"); + println!("cargo:rustc-link-lib=static=stdc++"); cc::Build::new() .file("example_type.cpp") @@ -44,18 +54,9 @@ fn main() { .clang_arg(format!("-I{}/include", travodds_c_path)) .header("example_type.h") .header("example_typeTypeSupport.h") - .blocklist_file(".*dds_basetype.h") - .blocklist_file(".*dds_c_string.h") - .blocklist_file(".*dds_c_wstring.h") - .blocklist_file(".*dds_c_sequence.h") - .blocklist_file(".*dds_c_map.h") - .blocklist_file(".*stdint.h") - .blocklist_file(".*stddef.h") - .blocklist_file(".*stdbool.h") - .blocklist_file(".*corecrt.h") - .blocklist_file(".*vcruntime.h") - .blocklist_file(".*sal.h") - .blocklist_file(".*vadefs.h") + .allowlist_file(".*example_type.h") + .allowlist_file(".*example_typeTypeSupport.h") + .allowlist_recursively(false) .parse_callbacks(Box::new(RenameCallbacks)) .generate() .expect("Unable to generate bindings"); -- Gitee From a3b5ffbce2fa286f8ad855c0776e1b109cad2385 Mon Sep 17 00:00:00 2001 From: hzy <641773357@qq.com> Date: Fri, 13 Jun 2025 14:00:14 +0800 Subject: [PATCH 4/4] =?UTF-8?q?example(build.rs)=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=A4=9A=E4=BD=99=E7=9A=84=E8=B0=83=E8=AF=95=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/build.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/example/build.rs b/example/build.rs index 7406472..bb939b6 100644 --- a/example/build.rs +++ b/example/build.rs @@ -17,15 +17,6 @@ impl ParseCallbacks for RenameCallbacks { renamed = renamed.replace("_iterator", "Iterator"); Some(renamed) } - - fn will_parse_macro(&self, _name: &str) -> MacroParsingBehavior { - println!("Ignoring macro: {}", _name); - MacroParsingBehavior::Ignore - } - - fn header_file(&self, _filename: &str) { - println!("+++++++++++++++++++Parsing header file: {}", _filename); - } } fn main() { -- Gitee