diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 202c178..82138dc 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -2,7 +2,7 @@ ARG REPO=axisecp ARG SDK=acap-native-sdk # Keep in sync with `install-sdk.sh` and `on-host-workflow.yml`. ARG UBUNTU_VERSION=24.04 -# Keep in sync with `install-sdk.sh`. +# Keep in sync with `install-sdk.sh` and `AXIS_OS_VERSION` below. ARG VERSION=12.11.0 ARG BASE_IMAGE=debian:trixie-20260223 @@ -22,6 +22,7 @@ ENV \ SYSROOT_ARMV7HF=/opt/axis/acapsdk/sysroots/armv7hf # The above makes the below easier to read ENV \ + AXIS_OS_VERSION=12.11 \ CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER="aarch64-linux-gnu-gcc" \ CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-C link-args=--sysroot=${SYSROOT_AARCH64}" \ CC_aarch64_unknown_linux_gnu="aarch64-linux-gnu-gcc" \ diff --git a/Cargo.lock b/Cargo.lock index a2b2082..00cfd7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -743,6 +743,15 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +[[package]] +name = "datahub-sys" +version = "0.0.0" +dependencies = [ + "bindgen 0.69.5", + "libc", + "pkg-config", +] + [[package]] name = "digest" version = "0.10.7" @@ -1833,6 +1842,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "object_consumer" +version = "0.0.0" +dependencies = [ + "acap-logging", + "datahub-sys", + "libc", + "log", +] + [[package]] name = "object_detection" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index a36ea87..4378a2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,6 +60,7 @@ bbox = { path = "crates/bbox" } bbox-sys = { path = "crates/bbox-sys" } cargo-acap-build = { path = "crates/cargo-acap-build" } cli-version = { path = "crates/cli-version" } +datahub-sys = { path = "crates/datahub-sys" } larod-sys = { path = "crates/larod-sys" } licensekey = { path = "crates/licensekey" } licensekey-sys = { path = "crates/licensekey-sys" } diff --git a/apps-aarch64.checksum b/apps-aarch64.checksum index 754f20f..a587d39 100644 --- a/apps-aarch64.checksum +++ b/apps-aarch64.checksum @@ -9,6 +9,7 @@ eea87093d21a9c5975146e7e901b2af719751516 target-aarch64/acap/event_subscribe_1_ b094d400939f066dd4db602469c1f4fb242d4ca2 target-aarch64/acap/hello_world_0_0_0_aarch64.eap 0fec22df9046a67af4c0bb3f05e64a9e348d0cd6 target-aarch64/acap/inspect_env_0_0_0_aarch64.eap 44d11b7400ed107e3b091e3cd9b770336679c331 target-aarch64/acap/licensekey_handler_0_0_0_aarch64.eap +8982d13060a23b8e87fdbb803c5246d8afae0228 target-aarch64/acap/object_consumer_0_0_0_aarch64.eap 4fe894e74fb465817c3333dd4f3edfc7cca61766 target-aarch64/acap/object_detection_1_0_0_aarch64.eap 04dca71df69368834ce03d6c485597b115a00146 target-aarch64/acap/reverse_proxy_0_0_0_aarch64.eap 7e453ac8f014f7c54e7cae8d025b6d34509cad7a target-aarch64/acap/send_event_1_0_0_aarch64.eap diff --git a/apps-aarch64.filesize b/apps-aarch64.filesize index e25f9f6..e5fb6c0 100644 --- a/apps-aarch64.filesize +++ b/apps-aarch64.filesize @@ -9,6 +9,7 @@ 1406 target-aarch64/acap/hello_world_0_0_0_aarch64.eap 1441 target-aarch64/acap/inspect_env_0_0_0_aarch64.eap 1430 target-aarch64/acap/licensekey_handler_0_0_0_aarch64.eap +1418 target-aarch64/acap/object_consumer_0_0_0_aarch64.eap 1408 target-aarch64/acap/object_detection_1_0_0_aarch64.eap 10315 target-aarch64/acap/reverse_proxy_0_0_0_aarch64.eap 3437 target-aarch64/acap/send_event_1_0_0_aarch64.eap diff --git a/apps/object_consumer/Cargo.toml b/apps/object_consumer/Cargo.toml new file mode 100644 index 0000000..d927ec2 --- /dev/null +++ b/apps/object_consumer/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "object_consumer" +version = "0.0.0" +edition.workspace = true +publish = false + +[dependencies] +libc = { workspace = true } +log = { workspace = true } + +acap-logging = { workspace = true } +datahub-sys = { workspace = true } + +[features] +default = ["acap-logging/default"] diff --git a/apps/object_consumer/manifest.json b/apps/object_consumer/manifest.json new file mode 100644 index 0000000..830cbd9 --- /dev/null +++ b/apps/object_consumer/manifest.json @@ -0,0 +1,22 @@ +{ + "schemaVersion": "2.2.0", + "acapPackageConf": { + "setup": { + "appName": "object_consumer", + "vendor": "Axis Communications", + "vendorId": "0123456789", + "runMode": "never", + "version": "0.0.0", + "compatibleOsVersions": [ + { + "max": "13" + } + ] + } + }, + "resources": { + "deviceDataHub": { + "enabled": true + } + } +} diff --git a/apps/object_consumer/src/main.rs b/apps/object_consumer/src/main.rs new file mode 100644 index 0000000..cbf4dba --- /dev/null +++ b/apps/object_consumer/src/main.rs @@ -0,0 +1,203 @@ +//! An example app that subscribes to object detection data using [`datahub_sys`]. +//! +//! Re-implements the [C example for consuming Device Data Hub data]. +//! Unlike the C example, which relies on globals and `atexit`, resources are owned by `main` and +//! released explicitly on every exit path. +//! +//! Note that this example does not do anything on its own, and +//! its counterpart (`object_detector`) has yet to be ported. +//! +//! [C example for consuming Device Data Hub data]: https://github.com/AxisCommunications/acap-native-sdk-examples/tree/main/device-data-hub/acap-communication/object-consumer + +use std::{ + ffi::{c_char, c_int, c_void, CStr}, + process::ExitCode, + ptr, + sync::atomic::{AtomicBool, Ordering}, +}; + +use datahub_sys::{ + dh_client_connect, dh_client_create, dh_client_create_subscriber, dh_client_destroy, + dh_client_disconnect, dh_client_set_logging, dh_error_destroy, dh_error_to_string, + dh_filter_add_topic_name, dh_filter_create, dh_filter_destroy, dh_subscribe_options_add_filter, + dh_subscribe_options_create, dh_subscribe_options_destroy, + dh_subscribe_options_set_enable_data_updates, dh_subscriber_destroy, + dh_subscriber_set_data_callback, dh_subscriber_subscribe, dh_topic_data_get_json_data, + dh_topic_sample_get_data, DHClient, DHError, DHSubscriber, DHTopicSample, DH_LOG_INFO, + DH_LOG_TARGET_CONSOLE, +}; +use libc::{SIGINT, SIGTERM}; +use log::{error, info}; + +const TOPIC_NAME: &CStr = c"com.example.objectdetector"; +const USER_DATA: &CStr = c"object_consumer_data"; + +static KEEP_RUNNING: AtomicBool = AtomicBool::new(true); + +/// Logs and destroys `error`, returning `true`, if it is set; returns `false` otherwise. +unsafe fn handle_client_error(error: *mut DHError, context: &str) -> bool { + if error.is_null() { + return false; + } + let message = CStr::from_ptr(dh_error_to_string(error)).to_string_lossy(); + error!("Error in {context}: {message}"); + let () = dh_error_destroy(error); + true +} + +/// Creates a client and connects it to the Device Data Hub. +unsafe fn initialize_client() -> Option<*mut DHClient> { + let mut error: *mut DHError = ptr::null_mut(); + let client = dh_client_create(c"Client for object_consumer".as_ptr(), &mut error); + if client.is_null() { + let _ = handle_client_error(error, "create client"); + return None; + } + + let mut error: *mut DHError = ptr::null_mut(); + if !dh_client_set_logging(client, DH_LOG_INFO, DH_LOG_TARGET_CONSOLE, &mut error) { + let _ = handle_client_error(error, "set logging"); + } + + let mut error: *mut DHError = ptr::null_mut(); + let _ = dh_client_connect(client, &mut error); + if handle_client_error(error, "client connect") { + let () = dh_client_destroy(client); + return None; + } + + Some(client) +} + +/// Logs the data of each received sample. +unsafe extern "C" fn on_data_received(sample: *const DHTopicSample, user_data: *mut c_void) { + debug_assert!(!sample.is_null()); + debug_assert!(!user_data.is_null()); + let user_data = CStr::from_ptr(user_data.cast::()).to_string_lossy(); + info!("User data: {user_data}"); + let topic_data = dh_topic_sample_get_data(sample); + let data = dh_topic_data_get_json_data(topic_data); + if !data.is_null() { + let data = CStr::from_ptr(data).to_string_lossy(); + info!("Received Object Detection data: {data}"); + } +} + +/// Creates a subscriber and subscribes to data updates for `topics`. +unsafe fn setup_subscription(client: *mut DHClient, topics: &[&CStr]) -> Option<*mut DHSubscriber> { + debug_assert!(!client.is_null()); + let mut error: *mut DHError = ptr::null_mut(); + let subscriber = dh_client_create_subscriber( + client, + c"Data subscriber for object-consumer".as_ptr(), + &mut error, + ); + if handle_client_error(error, "create subscriber") { + return None; + } + + let mut error: *mut DHError = ptr::null_mut(); + let _ = dh_subscriber_set_data_callback( + subscriber, + Some(on_data_received), + USER_DATA.as_ptr().cast_mut().cast(), + &mut error, + ); + if handle_client_error(error, "set data callback") { + let () = dh_subscriber_destroy(subscriber); + return None; + } + + let filter = dh_filter_create(); + if filter.is_null() { + error!("Failed to create filter"); + let () = dh_subscriber_destroy(subscriber); + return None; + } + + for topic in topics { + let mut error: *mut DHError = ptr::null_mut(); + let _ = dh_filter_add_topic_name(filter, topic.as_ptr(), &mut error); + if handle_client_error(error, "add topic name to filter") { + let () = dh_filter_destroy(filter); + let () = dh_subscriber_destroy(subscriber); + return None; + } + } + + let options = dh_subscribe_options_create(); + if options.is_null() { + error!("Failed to create subscription options"); + let () = dh_filter_destroy(filter); + let () = dh_subscriber_destroy(subscriber); + return None; + } + + let mut error: *mut DHError = ptr::null_mut(); + let _ = dh_subscribe_options_add_filter(options, filter, &mut error); + let () = dh_filter_destroy(filter); + if handle_client_error(error, "add filter to options") { + let () = dh_subscribe_options_destroy(options); + let () = dh_subscriber_destroy(subscriber); + return None; + } + + let () = dh_subscribe_options_set_enable_data_updates(options, true); + + let mut error: *mut DHError = ptr::null_mut(); + let _ = dh_subscriber_subscribe(subscriber, options, &mut error); + let () = dh_subscribe_options_destroy(options); + if handle_client_error(error, "subscribe to topic") { + let () = dh_subscriber_destroy(subscriber); + return None; + } + + Some(subscriber) +} + +/// Destroys `subscriber`, if any, then disconnects and destroys `client`. +unsafe fn cleanup_resources(client: *mut DHClient, subscriber: *mut DHSubscriber) { + debug_assert!(!client.is_null()); + if !subscriber.is_null() { + let () = dh_subscriber_destroy(subscriber); + } + + let mut error: *mut DHError = ptr::null_mut(); + let _ = dh_client_disconnect(client, &mut error); + let _ = handle_client_error(error, "client disconnect"); + let () = dh_client_destroy(client); +} + +extern "C" fn signal_handler(sig: c_int) { + if sig == SIGINT || sig == SIGTERM { + KEEP_RUNNING.store(false, Ordering::Relaxed); + } +} + +fn main() -> ExitCode { + acap_logging::init_logger(); + info!("Application started"); + + unsafe { + let _ = libc::signal(SIGINT, signal_handler as libc::sighandler_t); + let _ = libc::signal(SIGTERM, signal_handler as libc::sighandler_t); + } + + let topics = [TOPIC_NAME]; + + let Some(client) = (unsafe { initialize_client() }) else { + return ExitCode::FAILURE; + }; + let Some(subscriber) = (unsafe { setup_subscription(client, &topics) }) else { + let () = unsafe { cleanup_resources(client, ptr::null_mut()) }; + return ExitCode::FAILURE; + }; + + while KEEP_RUNNING.load(Ordering::Relaxed) { + let _ = unsafe { libc::pause() }; + } + + info!("Application terminated"); + let () = unsafe { cleanup_resources(client, subscriber) }; + ExitCode::SUCCESS +} diff --git a/crates/datahub-sys/Cargo.toml b/crates/datahub-sys/Cargo.toml new file mode 100644 index 0000000..14d4989 --- /dev/null +++ b/crates/datahub-sys/Cargo.toml @@ -0,0 +1,13 @@ +[package] +build = "build.rs" +name = "datahub-sys" +version = "0.0.0" +edition.workspace = true +license = "MIT" + +[build-dependencies] +bindgen = { workspace = true } +pkg-config = { workspace = true } + +[dependencies] +libc = { workspace = true } diff --git a/crates/datahub-sys/build.rs b/crates/datahub-sys/build.rs new file mode 100644 index 0000000..0a280d9 --- /dev/null +++ b/crates/datahub-sys/build.rs @@ -0,0 +1,29 @@ +use std::{env, path}; + +fn populated_bindings(dst: &path::PathBuf) { + let library = pkg_config::Config::new() + .probe("device-data-hub-client-c") + .unwrap(); + let mut bindings = bindgen::Builder::default() + .header("wrapper.h") + .generate_comments(false) + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .allowlist_function("^(dh_.*)$") + .allowlist_type("^(DH.*)$") + .allowlist_var("^(DH_.*)$") + .allowlist_recursively(false) + .layout_tests(false); + for path in library.include_paths { + bindings = bindings.clang_args(&["-I", path.to_str().unwrap()]); + } + bindings.generate().unwrap().write_to_file(dst).unwrap(); +} + +fn main() { + let dst = path::PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs"); + if env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default() != "x86_64" + && env::var("CARGO_CFG_TARGET_OS").unwrap_or_default() != "macos" + { + populated_bindings(&dst); + } +} diff --git a/crates/datahub-sys/src/bindings.rs b/crates/datahub-sys/src/bindings.rs new file mode 100644 index 0000000..dd9d63f --- /dev/null +++ b/crates/datahub-sys/src/bindings.rs @@ -0,0 +1,569 @@ +/* automatically generated by rust-bindgen 0.69.5 */ + +pub type DHErrorCode = u32; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DHError_t { + _unused: [u8; 0], +} +pub type DHError = DHError_t; +extern "C" { + pub fn dh_error_get_message(err: *const DHError) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn dh_error_get_code(err: *const DHError) -> DHErrorCode; +} +extern "C" { + pub fn dh_error_code_to_string(code: DHErrorCode) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn dh_error_to_string(err: *const DHError) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn dh_error_destroy(err: *mut DHError); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DHTimestamp_t { + _unused: [u8; 0], +} +pub type DHTimestamp = DHTimestamp_t; +extern "C" { + pub fn dh_timestamp_create() -> *mut DHTimestamp; +} +extern "C" { + pub fn dh_timestamp_set_with_sec(timestamp: *mut DHTimestamp, seconds: i64); +} +extern "C" { + pub fn dh_timestamp_set_with_ms(timestamp: *mut DHTimestamp, milliseconds: i64); +} +extern "C" { + pub fn dh_timestamp_set_current(timestamp: *mut DHTimestamp); +} +extern "C" { + pub fn dh_timestamp_get_as_sec(timestamp: *const DHTimestamp) -> i64; +} +extern "C" { + pub fn dh_timestamp_get_as_ms(timestamp: *const DHTimestamp) -> i64; +} +extern "C" { + pub fn dh_timestamp_destroy(timestamp: *mut DHTimestamp); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DHTopic_t { + _unused: [u8; 0], +} +pub type DHTopic = DHTopic_t; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DHTopicData_t { + _unused: [u8; 0], +} +pub type DHTopicData = DHTopicData_t; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DHInstanceKeys_t { + _unused: [u8; 0], +} +pub type DHInstanceKeys = DHInstanceKeys_t; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DHTopicInstance_t { + _unused: [u8; 0], +} +pub type DHTopicInstance = DHTopicInstance_t; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DHTopicSample_t { + _unused: [u8; 0], +} +pub type DHTopicSample = DHTopicSample_t; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DHTopicList_t { + _unused: [u8; 0], +} +pub type DHTopicList = DHTopicList_t; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DHTopicInstanceList_t { + _unused: [u8; 0], +} +pub type DHTopicInstanceList = DHTopicInstanceList_t; +extern "C" { + pub fn dh_topic_get_name(topic: *const DHTopic) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn dh_topic_get_json_definition( + topic: *const DHTopic, + error: *mut *mut DHError, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn dh_topic_destroy(topic: *mut DHTopic); +} +extern "C" { + pub fn dh_topic_data_create() -> *mut DHTopicData; +} +extern "C" { + pub fn dh_topic_data_destroy(topic_data: *mut DHTopicData); +} +extern "C" { + pub fn dh_topic_data_set_json_data( + topic_data: *mut DHTopicData, + json_str: *const ::std::os::raw::c_char, + error: *mut *mut DHError, + ) -> bool; +} +extern "C" { + pub fn dh_topic_data_get_json_data( + topic_data: *const DHTopicData, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn dh_topic_sample_get_topic_name( + topic_sample: *const DHTopicSample, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn dh_topic_sample_get_sample_id(topic_sample: *const DHTopicSample) -> u64; +} +extern "C" { + pub fn dh_topic_sample_is_historical(topic_sample: *const DHTopicSample) -> bool; +} +extern "C" { + pub fn dh_topic_sample_get_timestamp(topic_sample: *const DHTopicSample) -> *const DHTimestamp; +} +extern "C" { + pub fn dh_topic_sample_get_data(topic_sample: *const DHTopicSample) -> *const DHTopicData; +} +extern "C" { + pub fn dh_instance_keys_create() -> *mut DHInstanceKeys; +} +extern "C" { + pub fn dh_instance_keys_destroy(keys: *mut DHInstanceKeys); +} +extern "C" { + pub fn dh_instance_keys_add_string( + keys: *mut DHInstanceKeys, + name: *const ::std::os::raw::c_char, + value: *const ::std::os::raw::c_char, + error: *mut *mut DHError, + ) -> bool; +} +extern "C" { + pub fn dh_instance_keys_add_integer( + keys: *mut DHInstanceKeys, + name: *const ::std::os::raw::c_char, + value: i64, + error: *mut *mut DHError, + ) -> bool; +} +extern "C" { + pub fn dh_instance_keys_has_key( + keys: *const DHInstanceKeys, + name: *const ::std::os::raw::c_char, + ) -> bool; +} +extern "C" { + pub fn dh_instance_keys_get_string( + keys: *const DHInstanceKeys, + name: *const ::std::os::raw::c_char, + out_value: *mut *const ::std::os::raw::c_char, + error: *mut *mut DHError, + ) -> bool; +} +extern "C" { + pub fn dh_instance_keys_get_integer( + keys: *const DHInstanceKeys, + name: *const ::std::os::raw::c_char, + out_value: *mut i64, + error: *mut *mut DHError, + ) -> bool; +} +extern "C" { + pub fn dh_instance_keys_remove( + keys: *mut DHInstanceKeys, + name: *const ::std::os::raw::c_char, + error: *mut *mut DHError, + ) -> bool; +} +extern "C" { + pub fn dh_topic_instance_get_topic_name( + topic_instance: *const DHTopicInstance, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn dh_topic_instance_get_keys( + topic_instance: *const DHTopicInstance, + ) -> *const DHInstanceKeys; +} +extern "C" { + pub fn dh_topic_instance_get_info( + topic_instance: *const DHTopicInstance, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn dh_topic_instance_destroy(topic_instance: *mut DHTopicInstance); +} +extern "C" { + pub fn dh_topic_list_get_count(list: *const DHTopicList) -> u32; +} +extern "C" { + pub fn dh_topic_list_get_name( + list: *const DHTopicList, + index: u32, + ) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn dh_topic_list_destroy(topic_list: *mut DHTopicList); +} +extern "C" { + pub fn dh_topic_instance_list_get_count(list: *const DHTopicInstanceList) -> u32; +} +extern "C" { + pub fn dh_topic_instance_list_get( + list: *const DHTopicInstanceList, + index: u32, + ) -> *const DHTopicInstance; +} +extern "C" { + pub fn dh_topic_instance_list_destroy(instance_list: *mut DHTopicInstanceList); +} +pub type DHConsumerMatchStatus = u32; +pub type DHProductionId = u64; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DHWriter_t { + _unused: [u8; 0], +} +pub type DHWriter = DHWriter_t; +pub type DHOnConsumerMatchUpdateCallback = ::std::option::Option< + unsafe extern "C" fn( + production_id: DHProductionId, + status: DHConsumerMatchStatus, + user_data: *mut ::std::os::raw::c_void, + ), +>; +extern "C" { + pub fn dh_writer_destroy(writer: *mut DHWriter); +} +extern "C" { + pub fn dh_writer_set_consumer_match_update_callback( + writer: *mut DHWriter, + callback: DHOnConsumerMatchUpdateCallback, + user_data: *mut ::std::os::raw::c_void, + ); +} +extern "C" { + pub fn dh_writer_get_name(writer: *const DHWriter) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn dh_writer_get_topic_name(writer: *const DHWriter) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn dh_writer_write_data( + writer: *mut DHWriter, + instance_keys: *const DHInstanceKeys, + topic_data: *const DHTopicData, + timestamp: *const DHTimestamp, + error: *mut *mut DHError, + ) -> bool; +} +extern "C" { + pub fn dh_writer_create_instance( + writer: *mut DHWriter, + instance_keys: *const DHInstanceKeys, + instance_info: *const ::std::os::raw::c_char, + delete_on_disconnect: bool, + error: *mut *mut DHError, + ) -> bool; +} +extern "C" { + pub fn dh_writer_delete_instance( + writer: *mut DHWriter, + instance_keys: *const DHInstanceKeys, + error: *mut *mut DHError, + ) -> bool; +} +extern "C" { + pub fn dh_writer_delete_all_instances(writer: *mut DHWriter, error: *mut *mut DHError) -> bool; +} +extern "C" { + pub fn dh_writer_register_production( + writer: *mut DHWriter, + instance_keys: *const DHInstanceKeys, + production_id: *mut DHProductionId, + error: *mut *mut DHError, + ) -> bool; +} +extern "C" { + pub fn dh_writer_unregister_production( + writer: *mut DHWriter, + production_id: DHProductionId, + error: *mut *mut DHError, + ) -> bool; +} +extern "C" { + pub fn dh_writer_get_consumer_match_status( + writer: *mut DHWriter, + production_id: DHProductionId, + status: *mut DHConsumerMatchStatus, + error: *mut *mut DHError, + ) -> bool; +} +pub type DHTopicUpdateType = u32; +pub type DHTopicInstanceUpdateType = u32; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DHSubscriber_t { + _unused: [u8; 0], +} +pub type DHSubscriber = DHSubscriber_t; +pub type DHOnTopicUpdateCallback = ::std::option::Option< + unsafe extern "C" fn( + topic_name: *const ::std::os::raw::c_char, + update_type: DHTopicUpdateType, + user_data: *mut ::std::os::raw::c_void, + ), +>; +pub type DHOnInstanceUpdateCallback = ::std::option::Option< + unsafe extern "C" fn( + topic_instance: *const DHTopicInstance, + update_type: DHTopicInstanceUpdateType, + user_data: *mut ::std::os::raw::c_void, + ), +>; +pub type DHOnDataCallback = ::std::option::Option< + unsafe extern "C" fn( + topic_sample: *const DHTopicSample, + user_data: *mut ::std::os::raw::c_void, + ), +>; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DHFilter_t { + _unused: [u8; 0], +} +pub type DHFilter = DHFilter_t; +extern "C" { + pub fn dh_filter_create() -> *mut DHFilter; +} +extern "C" { + pub fn dh_filter_destroy(filter: *mut DHFilter); +} +extern "C" { + pub fn dh_filter_add_topic_name( + filter: *mut DHFilter, + topic_name: *const ::std::os::raw::c_char, + error: *mut *mut DHError, + ) -> bool; +} +extern "C" { + pub fn dh_filter_add_instance( + filter: *mut DHFilter, + instance_keys: *const DHInstanceKeys, + error: *mut *mut DHError, + ) -> bool; +} +extern "C" { + pub fn dh_filter_add_data_expression( + filter: *mut DHFilter, + data_expression: *const ::std::os::raw::c_char, + error: *mut *mut DHError, + ) -> bool; +} +pub type DHStartFrom = u32; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DHSubscribeOptions_t { + _unused: [u8; 0], +} +pub type DHSubscribeOptions = DHSubscribeOptions_t; +extern "C" { + pub fn dh_subscribe_options_create() -> *mut DHSubscribeOptions; +} +extern "C" { + pub fn dh_subscribe_options_destroy(options: *mut DHSubscribeOptions); +} +extern "C" { + pub fn dh_subscribe_options_add_filter( + options: *mut DHSubscribeOptions, + filter: *const DHFilter, + error: *mut *mut DHError, + ) -> bool; +} +extern "C" { + pub fn dh_subscribe_options_set_enable_topic_updates( + options: *mut DHSubscribeOptions, + enable: bool, + ); +} +extern "C" { + pub fn dh_subscribe_options_set_enable_instance_updates( + options: *mut DHSubscribeOptions, + enable: bool, + ); +} +extern "C" { + pub fn dh_subscribe_options_set_enable_data_updates( + options: *mut DHSubscribeOptions, + enable: bool, + ); +} +extern "C" { + pub fn dh_subscribe_options_set_start_from( + options: *mut DHSubscribeOptions, + start_from: DHStartFrom, + ); +} +extern "C" { + pub fn dh_subscriber_destroy(subscriber: *mut DHSubscriber); +} +extern "C" { + pub fn dh_subscriber_get_name(subscriber: *const DHSubscriber) + -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn dh_subscriber_set_topic_update_callback( + subscriber: *mut DHSubscriber, + on_topic_update: DHOnTopicUpdateCallback, + user_data: *mut ::std::os::raw::c_void, + error: *mut *mut DHError, + ) -> bool; +} +extern "C" { + pub fn dh_subscriber_set_instance_update_callback( + subscriber: *mut DHSubscriber, + on_instance_update: DHOnInstanceUpdateCallback, + user_data: *mut ::std::os::raw::c_void, + error: *mut *mut DHError, + ) -> bool; +} +extern "C" { + pub fn dh_subscriber_set_data_callback( + subscriber: *mut DHSubscriber, + on_data: DHOnDataCallback, + user_data: *mut ::std::os::raw::c_void, + error: *mut *mut DHError, + ) -> bool; +} +extern "C" { + pub fn dh_subscriber_subscribe( + subscriber: *mut DHSubscriber, + options: *const DHSubscribeOptions, + error: *mut *mut DHError, + ) -> bool; +} +extern "C" { + pub fn dh_subscriber_unsubscribe( + subscriber: *mut DHSubscriber, + error: *mut *mut DHError, + ) -> bool; +} +pub type DHConnectionState = u32; +pub type DHLogLevel = u32; +pub type DHLogTarget = u32; +pub type DHOnConnectionUpdateCallback = ::std::option::Option< + unsafe extern "C" fn(update: DHConnectionState, user_data: *mut ::std::os::raw::c_void), +>; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct DHClient_t { + _unused: [u8; 0], +} +pub type DHClient = DHClient_t; +extern "C" { + pub fn dh_client_create( + name: *const ::std::os::raw::c_char, + error: *mut *mut DHError, + ) -> *mut DHClient; +} +extern "C" { + pub fn dh_client_get_name(client: *const DHClient) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn dh_client_set_connection_update_callback( + client: *mut DHClient, + callback: DHOnConnectionUpdateCallback, + user_data: *mut ::std::os::raw::c_void, + error: *mut *mut DHError, + ) -> bool; +} +extern "C" { + pub fn dh_client_connect(client: *mut DHClient, error: *mut *mut DHError) -> bool; +} +extern "C" { + pub fn dh_client_disconnect(client: *mut DHClient, error: *mut *mut DHError) -> bool; +} +extern "C" { + pub fn dh_client_destroy(client: *mut DHClient); +} +extern "C" { + pub fn dh_client_get_connection_state(client: *const DHClient) -> DHConnectionState; +} +extern "C" { + pub fn dh_client_create_topic_from_file( + client: *mut DHClient, + file_path: *const ::std::os::raw::c_char, + error: *mut *mut DHError, + ) -> *mut DHTopic; +} +extern "C" { + pub fn dh_client_create_topic( + client: *mut DHClient, + topic_def: *const ::std::os::raw::c_char, + error: *mut *mut DHError, + ) -> *mut DHTopic; +} +extern "C" { + pub fn dh_client_delete_topic( + client: *mut DHClient, + topic_name: *const ::std::os::raw::c_char, + error: *mut *mut DHError, + ) -> bool; +} +extern "C" { + pub fn dh_client_get_topic_list( + client: *mut DHClient, + error: *mut *mut DHError, + ) -> *mut DHTopicList; +} +extern "C" { + pub fn dh_client_get_topic( + client: *mut DHClient, + topic_name: *const ::std::os::raw::c_char, + error: *mut *mut DHError, + ) -> *mut DHTopic; +} +extern "C" { + pub fn dh_client_get_topic_instances( + client: *mut DHClient, + topic_name: *const ::std::os::raw::c_char, + error: *mut *mut DHError, + ) -> *mut DHTopicInstanceList; +} +extern "C" { + pub fn dh_client_create_writer( + client: *mut DHClient, + writer_name: *const ::std::os::raw::c_char, + topic_name: *const ::std::os::raw::c_char, + error: *mut *mut DHError, + ) -> *mut DHWriter; +} +extern "C" { + pub fn dh_client_create_subscriber( + client: *mut DHClient, + subscriber_name: *const ::std::os::raw::c_char, + error: *mut *mut DHError, + ) -> *mut DHSubscriber; +} +extern "C" { + pub fn dh_client_set_logging( + client: *mut DHClient, + level: DHLogLevel, + target: DHLogTarget, + error: *mut *mut DHError, + ) -> bool; +} diff --git a/crates/datahub-sys/src/lib.rs b/crates/datahub-sys/src/lib.rs new file mode 100644 index 0000000..a23d859 --- /dev/null +++ b/crates/datahub-sys/src/lib.rs @@ -0,0 +1,69 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(improper_ctypes)] + +#[cfg(not(any(target_arch = "x86_64", target_os = "macos")))] +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +#[cfg(any(target_arch = "x86_64", target_os = "macos"))] +include!("./bindings.rs"); + +// The C headers define the constants below as object-like macros with casts, e.g. +// `#define DH_ERR_INVALID_PARAMS ((DHErrorCode)0)`, which bindgen cannot evaluate, +// so they are transcribed manually. + +pub const DH_ERR_INVALID_PARAMS: DHErrorCode = 0; +pub const DH_ERROR_DATA_TOO_BIG: DHErrorCode = 1; +pub const DH_ERR_NOT_CONNECTED: DHErrorCode = 2; +pub const DH_ERR_ALREADY_CONNECTED: DHErrorCode = 3; +pub const DH_ERR_UNKNOWN_ERROR: DHErrorCode = 4; +pub const DH_ERR_INTERNAL_ERROR: DHErrorCode = 5; +pub const DH_ERR_INVALID_ID: DHErrorCode = 6; +pub const DH_ERR_INVALID_TOPIC: DHErrorCode = 7; +pub const DH_ERR_NOT_INITIALIZED: DHErrorCode = 8; +pub const DH_ERR_MAX_CLIENTS: DHErrorCode = 9; +pub const DH_ERR_MAX_CONNECTIONS: DHErrorCode = 10; +pub const DH_ERR_AUTHENTICATION_FAILED: DHErrorCode = 11; +pub const DH_ERR_TOPIC_EXISTS: DHErrorCode = 12; +pub const DH_ERR_MAX_TOPIC: DHErrorCode = 13; +pub const DH_ERR_CONNECTION_ERROR: DHErrorCode = 14; +pub const DH_ERR_INVALID_DATA: DHErrorCode = 15; +pub const DH_ERR_INVALID_INSTANCE: DHErrorCode = 16; +pub const DH_ERR_INVALID_KEYS: DHErrorCode = 17; +pub const DH_ERR_INVALID_SUBSCRIPTION: DHErrorCode = 18; +pub const DH_ERR_INSTANCE_EXISTS: DHErrorCode = 19; +pub const DH_ERR_INSTANCES_NOT_SUPPORTED: DHErrorCode = 20; +pub const DH_ERR_MAX_INSTANCES: DHErrorCode = 21; +pub const DH_ERR_MAX_SUBSCRIPTIONS: DHErrorCode = 22; +pub const DH_ERR_AUTHORIZATION_FAILED: DHErrorCode = 23; +pub const DH_ERR_MAX_PRODUCTIONS: DHErrorCode = 24; +pub const DH_ERR_INVALID_PRODUCTION: DHErrorCode = 25; +pub const DH_ERR_INVALID_REQUEST: DHErrorCode = 26; +pub const DH_ERR_CREDENTIAL_ERROR: DHErrorCode = 27; + +pub const DH_CONN_DISCONNECTED: DHConnectionState = 0; +pub const DH_CONN_CONNECTED: DHConnectionState = 1; + +pub const DH_LOG_OFF: DHLogLevel = 0; +pub const DH_LOG_CRITICAL: DHLogLevel = 1; +pub const DH_LOG_ERROR: DHLogLevel = 2; +pub const DH_LOG_WARNING: DHLogLevel = 3; +pub const DH_LOG_INFO: DHLogLevel = 4; +pub const DH_LOG_DEBUG: DHLogLevel = 5; +pub const DH_LOG_TRACE: DHLogLevel = 6; + +pub const DH_LOG_TARGET_CONSOLE: DHLogTarget = 0; +pub const DH_LOG_TARGET_SYSLOG: DHLogTarget = 1; + +pub const DH_TOPIC_CREATED: DHTopicUpdateType = 0; +pub const DH_TOPIC_DELETED: DHTopicUpdateType = 1; + +pub const DH_TOPIC_INSTANCE_CREATED: DHTopicInstanceUpdateType = 0; +pub const DH_TOPIC_INSTANCE_DELETED: DHTopicInstanceUpdateType = 1; + +pub const DH_START_FROM_NOW: DHStartFrom = 0; +pub const DH_START_FROM_OLDEST: DHStartFrom = 1; + +pub const DH_CONSUMER_NO_MATCH: DHConsumerMatchStatus = 0; +pub const DH_CONSUMER_MATCH: DHConsumerMatchStatus = 1; diff --git a/crates/datahub-sys/wrapper.h b/crates/datahub-sys/wrapper.h new file mode 100644 index 0000000..90eb8c5 --- /dev/null +++ b/crates/datahub-sys/wrapper.h @@ -0,0 +1,4 @@ +#include +#include +#include +#include