diff --git a/docs/guide/advanced.md b/docs/guide/advanced.md new file mode 100644 index 0000000..1120bc9 --- /dev/null +++ b/docs/guide/advanced.md @@ -0,0 +1,33 @@ +# Advanced operations + +This section collects less common operations that are still useful in +production applications. + +## Write parameters + +[`crate::Output::write_with_params`] accepts a [`crate::WriteParams`] struct +that can change the write action (`write`, `dispose`, `unregister`) and attach +identity or timestamp metadata. + +The available constructors are: + +* [`crate::WriteParams::write`] +* [`crate::WriteParams::dispose`] +* [`crate::WriteParams::unregister`] + +## Waiting and matching + +* [`crate::Connector::wait_for_data`]: wait for any input to have data. +* [`crate::Input::wait_for_publications`]: wait for matched writers. +* [`crate::Output::wait_for_subscriptions`]: wait for matched readers. +* [`crate::Output::wait`]: wait for acknowledgments. + +Both `Input` and `Output` provide JSON helpers to inspect matches: + +* [`crate::Input::display_matched_publications`] +* [`crate::Output::display_matched_subscriptions`] + +## Loan management + +If you call [`crate::Input::take`], you can return the loan with +[`crate::Input::return_loan`] after processing to release native resources. diff --git a/docs/guide/configuration.md b/docs/guide/configuration.md new file mode 100644 index 0000000..9fdc221 --- /dev/null +++ b/docs/guide/configuration.md @@ -0,0 +1,121 @@ +# Configuration + +RTI Connector for Rust uses an XML configuration file to define participants, +readers, writers, topics, types, and QoS. The XML schema is the same one used +by RTI Connext DDS XML-Based Application Creation. + +For background on the XML format, see the +[RTI XML-Based Application Creation guide](https://community.rti.com/static/documentation/connext-dds/current/doc/manuals/connext_dds_professional/xml_application_creation/index.htm). + +## Loading a configuration + +Create a connector by passing a participant name and the XML file path: + +```rust +use rtiddsconnector::Connector; + +fn load_config() -> rtiddsconnector::ConnectorFallible { + let _connector = Connector::new("MyLibrary::MyParticipant", "App.xml")?; + Ok(()) +} +``` + +The `config_name` must match a `` element in the XML. + +## XML tags and Connector API mapping + +The table below summarizes the most common XML tags and how they map to the +Connector API: + +| XML Tag | DDS Concept | Connector API | +| --- | --- | --- | +| `` | Data types | Types used by `Input` and `Output` | +| ``, ``, ``, `` | Domain, Topic | Defines the domain and topics used by `Connector` | +| ``, `` | DomainParticipant | Loaded by `Connector::new` | +| ``, `` | Publisher, DataWriter | Each `` defines an `Output` | +| ``, `` | Subscriber, DataReader | Each `` defines an `Input` | +| ``, `` | QoS | QoS for `Connector`, `Output`, and `Input` | + +## Types + +Types are defined under `` and associated with topics. Example: + +```xml + + + + + + + + +``` + +You can define types in IDL and convert them to XML with `rtiddsgen`: + +``` +rtiddsgen -convertToXml MyTypes.idl +``` + +## Domain and topics + +Domains register types and define topics: + +```xml + + + + + + +``` + +## Participants, readers, and writers + +Participants contain publishers and subscribers, which in turn manage individual +writers and readers: + +```xml + + + + + + + + + + + + + +``` + +## QoS profiles + +QoS can be configured at the profile level or per-entity. Example profile: + +```xml + + + + + RELIABLE_RELIABILITY_QOS + + + + + RELIABLE_RELIABILITY_QOS + + + + +``` + +## Examples in this repo + +This repository includes XML examples you can adapt. For an example +configuration file, see `examples/shapes/Shapes.xml`: + +* `examples/MyApplication.xml` +* `examples/shapes/Shapes.xml` diff --git a/docs/guide/connector.md b/docs/guide/connector.md new file mode 100644 index 0000000..c30d852 --- /dev/null +++ b/docs/guide/connector.md @@ -0,0 +1,70 @@ +# Connector lifecycle + +This chapter covers creating a connector, acquiring inputs and outputs, and +cleaning up native resources. + +[`crate::Connector`] represents a DDS `DomainParticipant` configured from XML. +It owns native resources and creates `Input` and `Output` handles. + +## Importing the crate + +To import the crate: + +```rust +use rtiddsconnector::{self, Connector}; +``` + +## Creating a connector + +To create a connector, pass an XML file and a configuration name to +[`crate::Connector::new`]: + +```rust +use rtiddsconnector::Connector; + +fn create_connector() -> rtiddsconnector::ConnectorFallible { + let _connector = Connector::new("MyLibrary::MyParticipant", "App.xml")?; + Ok(()) +} +``` + +The XML file defines your types, QoS profiles, and DDS entities. The call above +loads a `` from a ``. For example: + +```xml + + + ... + + +``` + +See a complete example in `examples/MyApplication.xml`. + +> **Note:** Operations on the same `Connector` or its contained entities are not +> protected for multi-threaded access. See +> [Threading and ownership](crate::guide::threading) for guidance. + +## Closing a connector + +There is no explicit `close()` method in Rust. Instead, `Connector`, `Input`, and +`Output` are released when they go out of scope. The crate uses RAII to free +native resources. + +To force cleanup of Connext global resources at the end of a scope (for example, +in tests), use [`crate::GlobalsDropGuard`]. + +## Getting inputs and outputs + +Once you have created a connector, use [`crate::Connector::get_input`] and +[`crate::Connector::get_output`] to retrieve inputs and outputs. + +> **Note:** If the `` you load contains both `` +> and `` tags for the same topic and they have matching QoS, inputs +> may receive data before you call `get_input`. To avoid this, configure the +> `` that contains the `` with +> `//` set to +> `false`. Then inputs will only receive data after you call `get_input`. + +See [Publishing data](crate::guide::output) and [Reading data](crate::guide::input) +for the workflow that follows. diff --git a/docs/guide/data.md b/docs/guide/data.md new file mode 100644 index 0000000..6290e36 --- /dev/null +++ b/docs/guide/data.md @@ -0,0 +1,96 @@ +# Data access and Serde + +RTI Connector exposes data as JSON under the hood, while also providing typed +accessors for common primitive fields. + +## Complex types and XML definitions + +The types you read and write can include nested structs, sequences, arrays, and +unions. These types are defined in XML following RTI's XML-Based Application +Creation format. For details, see +[RTI XML-Based Application Creation](https://community.rti.com/static/documentation/connext-dds/current/doc/manuals/connext_dds_professional/xml_application_creation/xml_based_app_creation_guide/UnderstandingXMLBased/XMLTagsConfigEntities.htm). + +## JSON vs member access + +You can access data member-by-member or as JSON. JSON access is convenient when +working with large structures; member access is convenient when you only need a +few fields. + +* Set with JSON: [`crate::Instance::set_as_json`] +* Get JSON: [`crate::Sample::get_value_json`] +* Member access: [`crate::Instance::set_number`], [`crate::Instance::set_string`], + [`crate::Sample::get_number`], [`crate::Sample::get_string`] + +## Accessing basic members + +Use typed setters/getters for numbers, booleans, and strings: + +```rust +use rtiddsconnector::{Instance, Sample}; + +fn set_basic(instance: &mut Instance) -> rtiddsconnector::ConnectorFallible { + instance.set_number("my_long", 2.0)?; + instance.set_boolean("my_boolean", true)?; + instance.set_string("my_string", "Hello, World!")?; + Ok(()) +} + +fn get_basic(sample: &Sample) -> rtiddsconnector::ConnectorFallible { + let _n = sample.get_number("my_long")?; + let _b = sample.get_boolean("my_boolean")?; + let _s = sample.get_string("my_string")?; + Ok(()) +} +``` + +## Accessing complex members + +Examples of field-name syntax for nested members, arrays, sequences, and unions +are available in the [Accessing the data (field-name syntax examples)](https://community.rti.com/static/documentation/connector/current/api/javascript/data.html#) +chapter of the Connector for JavaScript API documentation. + +## Type-independent access with SelectedValue + +For dynamic access, use [`crate::Instance::set_value`] and +[`crate::Sample::get_value`], which operate on [`crate::SelectedValue`]: + +```rust +use rtiddsconnector::{Instance, Sample, SelectedValue}; + +fn set_dynamic(instance: &mut Instance) -> rtiddsconnector::ConnectorFallible { + instance.set_value("my_double", SelectedValue::Number(2.14))?; + instance.set_value("my_boolean", SelectedValue::Boolean(true))?; + instance.set_value("my_string", SelectedValue::String("Hello".to_string()))?; + Ok(()) +} + +fn get_dynamic(sample: &Sample) -> rtiddsconnector::ConnectorFallible { + let _value = sample.get_value("my_double")?; + Ok(()) +} +``` + +## Performance guidance + +Typed getters and setters are generally faster than dynamic access with +`SelectedValue`. If you intend to access most or all members of a sample, +using JSON (`set_as_json`/`get_value_json`) can be more convenient and efficient +than setting or getting fields one by one. + +## 64-bit integer limitations + +RTI Connector uses a single number representation internally. This means that +64-bit integers may lose precision when converted to the internal numeric type. +If you need exact 64-bit integer values, consider representing them as strings +in your data model and converting explicitly in your application. + +## Typed serialization + +If you want to work with Rust structs, use Serde: + +* [`crate::Instance::serialize`]: serialize a struct and set it into the + instance. +* [`crate::Sample::deserialize`]: deserialize a sample into a struct. + +These methods allow you to keep strongly-typed models in your application while +still using the dynamic RTI Connector API. diff --git a/docs/guide/errors.md b/docs/guide/errors.md new file mode 100644 index 0000000..c2da76d --- /dev/null +++ b/docs/guide/errors.md @@ -0,0 +1,52 @@ +# Error handling + +Most operations return [`crate::ConnectorResult`] or +[`crate::ConnectorFallible`]. When an operation fails, you receive a +[`crate::ConnectorError`]. + +## Common patterns + +```rust +use rtiddsconnector::{Connector, ConnectorError}; + +fn try_connect() -> Result<(), ConnectorError> { + let _connector = Connector::new("MyLibrary::MyParticipant", "App.xml")?; + Ok(()) +} +``` + +Use helper methods to detect common cases: + +* [`crate::ConnectorError::is_timeout`] +* [`crate::ConnectorError::is_entity_not_found`] +* [`crate::ConnectorError::is_field_not_found`] +* [`crate::ConnectorError::is_native_error`] + +To inspect the last native error message, call +[`crate::ConnectorError::last_error_message`]. + +## Timeout example + +This example waits for data and treats a timeout as a non-fatal outcome. + +```rust +use rtiddsconnector::Input; + +fn wait_with_timeout(input: &Input) -> rtiddsconnector::ConnectorFallible { + match input.wait_with_timeout(std::time::Duration::from_secs(2)) { + Ok(()) => Ok(()), + Err(e) if e.is_timeout() => { + println!("Timed out waiting for data"); + Ok(()) + } + Err(e) => Err(e), + } +} +``` + +## Native error details + +If an operation fails because of a native RTI Connector error, the +[`crate::ConnectorError`] will include the last error message from the native library. +This can provide additional context when debugging configuration or data access +issues. diff --git a/docs/guide/getting_started.md b/docs/guide/getting_started.md new file mode 100644 index 0000000..faa88ee --- /dev/null +++ b/docs/guide/getting_started.md @@ -0,0 +1,79 @@ +# Getting started + +This crate is intended to be consumed from Cargo as a Git dependency. The +examples in `snippets/` and `examples/` are also included in the +[rticonnextdds-connector-rust repository](https://github.com/rticommunity/rticonnextdds-connector-rust). + +## Add the dependency + +```console +cargo add --git https://github.com/rticommunity/rticonnextdds-connector-rs +``` + +This command adds the Connector for Rust repository to your project. If you +need a specific branch, add `--branch `. + +## Running the examples + +The repository includes: + +* `examples/shapes` (publisher/subscriber example) +* `snippets` (read-only code snippets used in the docs) + +See `examples/shapes/README.md` for usage details. + +## Minimal example + +This is the typical flow for using Connector: create a connector, +obtain an output and input, write one sample, then read samples back. For +example: + +```rust +use rtiddsconnector::{Connector, GlobalsDropGuard, Input, Output}; + +fn main() -> rtiddsconnector::ConnectorFallible { + let _globals = GlobalsDropGuard; + + let connector = Connector::new("MyLibrary::MyParticipant", "/path/to/App.xml")?; + let mut output: Output<'_> = connector.get_output("MyPublisher::MyWriter")?; + let mut input: Input<'_> = connector.get_input("MySubscriber::MyReader")?; + + let mut instance = output.instance(); + instance.set_number("x", 100.0)?; + instance.set_number("y", 200.0)?; + instance.set_string("color", "BLUE")?; + output.write()?; + + input.wait_with_timeout(std::time::Duration::from_secs(5))?; + input.take()?; + + for sample in input.into_iter().valid_only() { + println!("Sample: {}", sample); + } + + Ok(()) +} +``` + +## Advanced: Configure the native libraries + +At build time, `build.rs` locates the RTI Connector C libraries in this order: + +1. `RTI_CONNECTOR_VERSION` downloads the target-specific libraries from the + RTI Connector GitHub releases. +2. `RTI_CONNECTOR_DIR` uses a local directory containing the libraries. +3. `CARGO_MANIFEST_DIR/rticonnextdds-connector` uses a local directory next to + the crate. +4. `CONNECTOR_VERSION` file falls back to a version file in the crate root. + +If none of the methods above is successful, the build will fail with an error +message indicating that the C libraries could not be found. + +Once your application has been built, `cargo` commands will automatically pick +up the linker instructions generated by `build.rs`, so no additional +configuration is needed to link the RTI Connext C libraries when using +`cargo test` or similar. + +At runtime, ensure the native libraries are discoverable in your system's +library path (for example `DYLD_LIBRARY_PATH` on macOS, `LD_LIBRARY_PATH` on +Linux, or `PATH` on Windows). diff --git a/docs/guide/index.md b/docs/guide/index.md new file mode 100644 index 0000000..2b25e2c --- /dev/null +++ b/docs/guide/index.md @@ -0,0 +1,17 @@ +# User Guide + +This guide provides a walkthrough-style introduction to RTI Connector for Rust, +covering setup, configuration, and common publishing/reading workflows. Each +section links to the relevant API reference where appropriate. + +## Contents + +* [Getting started](crate::guide::getting_started) +* [Configuration](crate::guide::configuration) +* [Connector lifecycle](crate::guide::connector) +* [Publishing data](crate::guide::output) +* [Reading data](crate::guide::input) +* [Data access and Serde](crate::guide::data) +* [Error handling](crate::guide::errors) +* [Threading and ownership](crate::guide::threading) +* [Advanced operations](crate::guide::advanced) diff --git a/docs/guide/input.md b/docs/guide/input.md new file mode 100644 index 0000000..3e6de3b --- /dev/null +++ b/docs/guide/input.md @@ -0,0 +1,115 @@ +# Reading data (Input) + +[`crate::Input`] wraps a DDS `DataReader` and provides an iterator-based +interface over samples returned by the RTI Connector API. + +## Getting the input + +To read or take samples, first get a reference to the input: + +```rust +use rtiddsconnector::{Connector, Input}; + +fn get_input(connector: &Connector) -> rtiddsconnector::ConnectorResult> { + connector.get_input("MySubscriber::MyReader") +} +``` + +## Reading or taking the data + +Call [`crate::Input::take`] to access and remove samples: + +```rust +input.take()?; +``` + +Or call [`crate::Input::read`] to access samples but leave them available for a +future `read` or `take`: + +```rust +input.read()?; +``` + +Use [`crate::Input::wait`] or [`crate::Input::wait_with_timeout`] to block until +new data is available on a specific input. These methods do not read data; call +`read` or `take` afterward. + +If you want to wait for data on any input owned by a connector, use +[`crate::Connector::wait_for_data`] or +[`crate::Connector::wait_for_data_with_timeout`]. These methods do not read +samples; call `read` or `take` afterward. + +## Accessing the data samples + +After calling [`crate::Input::read`] or [`crate::Input::take`], iterate over the +samples: + +```rust +for sample in input.into_iter() { + if sample.is_valid()? { + println!("{}", sample); + } +} +``` + +To skip invalid samples, use `valid_only()`: + +```rust +for sample in input.into_iter().valid_only() { + println!("{}", sample); +} +``` + +`Sample` provides typed accessors and JSON access: + +* [`crate::Sample::get_number`] +* [`crate::Sample::get_boolean`] +* [`crate::Sample::get_string`] +* [`crate::Sample::get_value_json`] + +`Sample` implements `Display` to print the full JSON representation of the +sample. + +If you need to access meta-data fields (SampleInfo), see [Accessing sample meta-data](#accessing-sample-meta-data). + +## Returning the loan + +If you used [`crate::Input::take`], return the loan when you are done using +[`crate::Input::return_loan`]. This allows the underlying reader to reuse +resources sooner. + +## Accessing sample meta-data + +Every sample contains an associated SampleInfo with meta-data about the +sample: + +```rust +for sample in input.into_iter() { + let source_timestamp = sample.get_info("source_timestamp")?; + println!("source_timestamp: {:?}", source_timestamp); +} +``` + +See [`crate::Sample::get_info`] for the list of available meta-data fields. + +*Connext DDS* can produce samples with invalid data, which contain meta-data +only. For more information about this, see the Valid Data flag in the RTI +Connext DDS Core Libraries User's Manual: +. +These samples indicate a change in the instance state. Samples with invalid data +still provide the following information: + +* The SampleInfo +* When an instance is disposed (`sample.get_info("instance_state")` is + `NOT_ALIVE_DISPOSED`), the sample data contains the value of the key that has + been disposed. You can access the key fields only. + +## Matching with a publication + +Use [`crate::Input::wait_for_publications`] or +[`crate::Input::wait_for_publications_with_timeout`] to detect when a +compatible publication is matched or unmatched. These methods return the change +in the number of matched publications since the last call. + +You can inspect the current list with +[`crate::Input::display_matched_publications`], which returns JSON. diff --git a/docs/guide/output.md b/docs/guide/output.md new file mode 100644 index 0000000..c10233a --- /dev/null +++ b/docs/guide/output.md @@ -0,0 +1,119 @@ +# Publishing data (Output) + +[`crate::Output`] wraps a DDS DataWriter and exposes a single mutable +[`crate::Instance`] representing the next sample to write. + +## Getting the output + +To write a data sample, first look up an output: + +```rust +use rtiddsconnector::{Connector, Output}; + +fn get_output(connector: &Connector) -> rtiddsconnector::ConnectorResult> { + connector.get_output("MyPublisher::MyWriter") +} +``` + +## Populating the data sample + +The next step is to set the `Instance` fields. You can set them member-by-member: + +```rust +use rtiddsconnector::Output; + +fn set_fields(output: &mut Output) -> rtiddsconnector::ConnectorFallible { + let mut instance = output.instance(); + instance.set_number("x", 1.0)?; + instance.set_number("y", 2.0)?; + instance.set_number("shapesize", 30.0)?; + instance.set_string("color", "BLUE")?; + Ok(()) +} +``` + +Or using JSON: + +```rust +use rtiddsconnector::Output; + +fn set_json(output: &mut Output) -> rtiddsconnector::ConnectorFallible { + let mut instance = output.instance(); + instance.set_as_json(r#"{"x":1,"y":2,"shapesize":30,"color":"BLUE"}"#)?; + Ok(()) +} +``` + +For strongly-typed models, see [Data access and Serde](crate::guide::data) and +[`crate::Instance::serialize`]. + +Field names correspond to the type assigned to the output in XML. For example: + +```xml + + + + + + +``` + +## Writing the data sample + +To write the values that have been set in the instance, call +[`crate::Output::write`]: + +```rust +use rtiddsconnector::Output; + +fn write_once(output: &mut Output) -> rtiddsconnector::ConnectorFallible { + output.write() +} +``` + +If the DataWriter QoS is reliable, you can use [`crate::Output::wait`] or +[`crate::Output::wait_with_timeout`] to wait for acknowledgments: + +```rust +use rtiddsconnector::Output; + +fn write_and_wait(output: &mut Output) -> rtiddsconnector::ConnectorFallible { + output.write()?; + output.wait() +} +``` + +To write with parameters such as a source timestamp, use [`crate::WriteParams`] +with [`crate::Output::write_with_params`]: + +```rust +use rtiddsconnector::{Output, WriteParams}; + +fn write_with_timestamp(output: &mut Output, ts: i64) -> rtiddsconnector::ConnectorFallible { + let params = WriteParams::write().with_source_timestamp(ts); + output.write_with_params(¶ms) +} +``` + +It is also possible to dispose or unregister an instance: + +```rust +use rtiddsconnector::{Output, WriteParams}; + +fn dispose_instance(output: &mut Output) -> rtiddsconnector::ConnectorFallible { + let params = WriteParams::dispose(); + output.write_with_params(¶ms) +} +``` + +In these two cases, only the key fields are relevant. + +## Matching with a subscription + +Use [`crate::Output::wait_for_subscriptions`] or +[`crate::Output::wait_for_subscriptions_with_timeout`] to detect when a +compatible subscription is matched or unmatched. These methods return the +change in the number of matches since the last call. + +You can inspect the current list of matched subscriptions as JSON with +[`crate::Output::display_matched_subscriptions`]. diff --git a/docs/guide/threading.md b/docs/guide/threading.md new file mode 100644 index 0000000..e97d2ae --- /dev/null +++ b/docs/guide/threading.md @@ -0,0 +1,25 @@ +# Threading and ownership + +The underlying RTI Connector C API is not thread-safe. The Rust bindings provide +synchronization around the native connector and enforce single-threaded +ownership of `Input` and `Output` handles. + +## Ownership rules + +Use the following ownership rules: + +* `Connector::get_input` and `Connector::get_output` return exclusive handles. +* If an entity is already owned by another thread, you will receive an error. +* Use `Connector::take_input` and `Connector::take_output` to block until the + entity is free. + +## Practical guidance about threading + +Keep each `Input` or `Output` on a single thread at a time. If you need to share +work, consider a worker thread that owns the handle and communicates via +channels with the rest of your application. + +While the connector uses internal locks for native access, this is not a +guarantee of safe concurrent access to the same `Input` or `Output`. Treat the +API as single-threaded unless you control synchronization at the application +level. diff --git a/docs/lib.md b/docs/lib.md index b442ffa..da6ed54 100644 --- a/docs/lib.md +++ b/docs/lib.md @@ -1,11 +1,11 @@ -# A simple DDS API for Rust (Experimental) +# RTI Connector for Rust The `rtiddsconnector` crate provides a lightweight interface with which to access DDS domains from Rust using [RTI Connext][rti-pro] C libraries and the simplified _RTI Connector_ API. The API offered by `rtiddsconnector` does **NOT** match the -[standard DDS API][omg-dds]. Rather, it offers a simplified interface developed +[standard DDS API][omg-dds]. Rather, it offers a simplified interface developed for faster and easier integration in any programming language with access to native C code. @@ -37,78 +37,17 @@ the _RTI Connector_ API: [omg-dds-xml]: https://www.omg.org/spec/DDS-XML/ "OMG DDS XML Specification" [gh-connector]: https://github.com/rticommunity/rticonnextdds-connector "RTI Connector on Github" -## Overview +# User Guide -The `rtiddsconnector` crate exposes the _RTI Connector_ API through the following main abstractions: +Start here for a tour of the crate: -* [`Connector`]: Represents a DDS _DomainParticipant_, and is used to create - `Input` and `Output` objects for reading and writing data. -* [`Input`]: Represents a DDS _DataReader_, and is used to read data samples - from DDS _Topics_. -* [`Output`]: Represents a DDS _DataWriter_, and is used to write data samples - to DDS _Topics_. +* [Getting started](crate::guide::getting_started) +* [Configuration](crate::guide::configuration) +* [Connector lifecycle](crate::guide::connector) +* [Publishing data](crate::guide::output) +* [Reading data](crate::guide::input) +* [Data access and Serde](crate::guide::data) +* [Error handling](crate::guide::errors) +* [Threading and ownership](crate::guide::threading) +* [Advanced operations](crate::guide::advanced) -In addition to these main abstractions, the crate also exposes, among others, the following types: - -* [`Sample`]: A trait representing a data sample read from an `Input`. -* [`SampleIterator`]: An iterator over valid samples read from an `Input`. -* [`Instance`]: A struct representing a data sample to be written to an `Output`. -* [`ConnectorError`]: A struct representing errors that can occur when using the _Connector_ API. -* [`ConnectorResult`]: A type alias for `Result`, used for error handling. -* [`ConnectorFallible`]: A type alias for `Result>`, used for fallible operations. -* [`GlobalsDropGuard`]: A struct that ensures proper cleanup of global resources used by the _Connector_ API. - -### Typed Data Support - -This crate provides Serde-based serialization and deserialization support -for working with strongly-typed data structures instead of raw JSON. -It extends the `Instance` and `Sample` types with methods to serialize -Rust structs to JSON and deserialize JSON to Rust structs, respectively. - -See the documentation for [`Instance::serialize`] and [`Sample::deserialize`]. - -### Error Handling - -Because many operations in the _RTI Connector_ API can fail due to various reasons, most of them handled -externally in the underlying C implementation, the `rtiddsconnector` crate provides -the [`ConnectorError`] struct as an opaque representation of errors that can occur when using the API. - -The [`ConnectorResult`] type alias is used throughout the crate to represent operations that can -succeed or fail with a [`ConnectorError`]. Most public methods in the crate return a [`ConnectorResult`], -where `T` is the expected return type of the operation. - -### Thread safety - -The `rtiddsconnector` crate attempts to provide safe abstractions over the underlying -C implementation, which is not thread-safe. This means that while the crate attempts to use -Rust's safety guarantees, developers must be careful when using multiple threads. - -### Build-time linking - -Because `rtiddsconnector` is built on top of [RTI Connext][rti-pro] C libraries, -applications using this crate must ensure that the required C libraries are -available at runtime. - -The `build.rs` script included in the crate can automatically configure -the linker to find the required libraries, namely: - -1. If the `RTI_CONNECTOR_VERSION` environment variable is defined, it will attempt to download - the target-specific C libraries from the [corresponding Github release][gh-connector-releases]. -2. If the `RTI_CONNECTOR_DIR` environment variable is defined, it will use the C libraries - found in the specified directory. -3. If neither of the above environment variables is defined, it will attempt to find the C libraries - in a default location based on common installation paths. - -If neither of these methods is successful, the build will fail with an error message -indicating that the C libraries could not be found. - -Once your application has been built, `cargo` commands will automatically pick up the -linker instructions generated by `build.rs`, so no additional configuration is needed -to link the [RTI Connext][rti-pro] C libraries when using `cargo test` or similar. - -However, when deploying your application, make sure that the required C libraries -are available in the system's library path, or in a location specified by -the appropriate environment variable (e.g., `LD_LIBRARY_PATH` on Linux, -`DYLD_LIBRARY_PATH` on macOS, or `PATH` on Windows). - -[gh-connector-releases]: https://github.com/rticommunity/rticonnextdds-connector/releases "RTI Connector Releases on Github" diff --git a/src/input.rs b/src/input.rs index aa66807..6f681bd 100644 --- a/src/input.rs +++ b/src/input.rs @@ -21,6 +21,18 @@ use crate::{ /// ```rust #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/snippets/input/using_sample.rs"))] /// ``` +/// +/// In addition to the data itself, each instance contains metadata which can be +/// accessed with [`Sample::get_info`] and related methods. +/// The list of available info fields include, but is not limited to: +/// +/// - `valid_data`: A boolean indicating whether the sample contains valid data. +/// - `source_timestamp`: A string representing the source timestamp of the sample. +/// - `reception_timestamp`: A string representing the reception timestamp of the sample. +/// - `instance_state`: A string representing the instance state of the sample. +/// - `view_state`: A string representing the view state of the sample. +/// - `sample_state`: A string representing the sample state of the sample. +/// - `identity`: A string representing the identity of the sample publisher. #[derive(Debug)] pub struct Sample<'a> { /// The index of the sample within the [`Input`]'s samples cache. diff --git a/src/lib.rs b/src/lib.rs index 42b5ea2..013e93d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,3 +27,77 @@ mod ffi; mod input; mod output; mod result; + +#[cfg(doc)] +pub mod guide { + #![doc = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/docs/guide/index.md" + ))] + #![doc(alias = "user guide")] + + #[doc(alias = "getting started")] + pub mod getting_started { + #![doc = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/docs/guide/getting_started.md" + ))] + } + + pub mod configuration { + #![doc = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/docs/guide/configuration.md" + ))] + } + + pub mod connector { + #![doc = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/docs/guide/connector.md" + ))] + } + + pub mod input { + #![doc = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/docs/guide/input.md" + ))] + } + + pub mod output { + #![doc = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/docs/guide/output.md" + ))] + } + + pub mod data { + #![doc = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/docs/guide/data.md" + ))] + } + + pub mod errors { + #![doc = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/docs/guide/errors.md" + ))] + } + + pub mod threading { + #![doc = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/docs/guide/threading.md" + ))] + } + + pub mod advanced { + #![doc = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/docs/guide/advanced.md" + ))] + } +} +