Skip to content

Commit 61ead66

Browse files
committed
finish rust integration
Signed-off-by: Tim Paine <3105306+timkpaine@users.noreply.github.com>
1 parent c845c31 commit 61ead66

3 files changed

Lines changed: 181 additions & 42 deletions

File tree

examples/05_cpp/5_c_api_adapter_rust/src/bindings.rs

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,53 @@ pub struct CCspAdapterManagerVTable {
158158
// We use weak linking / dynamic_lookup on macOS.
159159
// ============================================================================
160160

161-
// Function pointer types for CSP C API
162-
pub type FnCcspEngineNow = unsafe extern "C" fn(engine: CCspEngineHandle) -> CCspDateTime;
163-
pub type FnCcspInputGetType = unsafe extern "C" fn(input: CCspInputHandle) -> CCspType;
164-
pub type FnCcspInputGetLastInt64 = unsafe extern "C" fn(input: CCspInputHandle, out: *mut i64) -> CCspErrorCode;
165-
pub type FnCcspPushInt64 = unsafe extern "C" fn(adapter: CCspPushInputAdapterHandle, value: i64, batch: *mut c_void) -> CCspErrorCode;
161+
// ============================================================================
162+
// CSP C API function declarations
163+
//
164+
// These are resolved at runtime via dynamic lookup (see build.rs).
165+
// The symbols are provided by CSP's shared library when the Python module
166+
// is loaded alongside CSP.
167+
// ============================================================================
168+
169+
extern "C" {
170+
// Engine functions
171+
pub fn ccsp_engine_now(engine: CCspEngineHandle) -> CCspDateTime;
172+
173+
// Input functions (for output adapters)
174+
pub fn ccsp_input_get_type(input: CCspInputHandle) -> CCspType;
175+
pub fn ccsp_input_get_last_bool(input: CCspInputHandle, out: *mut i8) -> CCspErrorCode;
176+
pub fn ccsp_input_get_last_int64(input: CCspInputHandle, out: *mut i64) -> CCspErrorCode;
177+
pub fn ccsp_input_get_last_double(input: CCspInputHandle, out: *mut f64) -> CCspErrorCode;
178+
pub fn ccsp_input_get_last_string(
179+
input: CCspInputHandle,
180+
out_data: *mut *const c_char,
181+
out_length: *mut usize,
182+
) -> CCspErrorCode;
183+
pub fn ccsp_input_get_last_datetime(input: CCspInputHandle, out: *mut CCspDateTime) -> CCspErrorCode;
184+
185+
// Push functions (for input adapters)
186+
pub fn ccsp_push_input_adapter_push_bool(
187+
adapter: CCspPushInputAdapterHandle,
188+
value: i8,
189+
batch: *mut c_void,
190+
) -> CCspErrorCode;
191+
pub fn ccsp_push_input_adapter_push_int64(
192+
adapter: CCspPushInputAdapterHandle,
193+
value: i64,
194+
batch: *mut c_void,
195+
) -> CCspErrorCode;
196+
pub fn ccsp_push_input_adapter_push_double(
197+
adapter: CCspPushInputAdapterHandle,
198+
value: f64,
199+
batch: *mut c_void,
200+
) -> CCspErrorCode;
201+
pub fn ccsp_push_input_adapter_push_string(
202+
adapter: CCspPushInputAdapterHandle,
203+
data: *const c_char,
204+
length: usize,
205+
batch: *mut c_void,
206+
) -> CCspErrorCode;
207+
}
166208

167209
// ============================================================================
168210
// Capsule creation helpers

examples/05_cpp/5_c_api_adapter_rust/src/input_adapter.rs

Lines changed: 70 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,24 @@
33
//! This module demonstrates how to implement a CSP push input adapter in Rust
44
//! using the C API FFI bindings.
55
//!
6-
//! Note: The actual data pushing requires the CSP C API symbols to be available
7-
//! at runtime. When built standalone, the callbacks are implemented but the
8-
//! push functionality is stubbed.
6+
//! The adapter spawns a background thread that periodically pushes integer
7+
//! values to the CSP graph using the C API.
98
109
use std::ffi::c_void;
11-
use std::sync::atomic::{AtomicBool, AtomicI64, Ordering};
10+
use std::ptr;
11+
use std::sync::atomic::{AtomicBool, AtomicI64, AtomicPtr, Ordering};
1212
use std::sync::Arc;
13+
use std::thread::{self, JoinHandle};
14+
use std::time::Duration;
1315

1416
use crate::bindings::{
15-
CCspDateTime, CCspEngineHandle, CCspPushInputAdapterHandle,
17+
ccsp_push_input_adapter_push_int64, CCspDateTime, CCspEngineHandle,
18+
CCspPushInputAdapterHandle,
1619
};
1720

1821
/// Push input adapter that generates incrementing counter values.
1922
///
20-
/// This adapter would spawn a background thread when started that periodically
23+
/// This adapter spawns a background thread when started that periodically
2124
/// pushes integer values to the CSP graph using the C API.
2225
pub struct RustInputAdapter {
2326
/// Interval between pushes in milliseconds
@@ -29,9 +32,11 @@ pub struct RustInputAdapter {
2932
/// Flag to signal thread to stop
3033
running: Arc<AtomicBool>,
3134

32-
/// Push adapter handle (set by start callback)
33-
#[allow(dead_code)]
34-
adapter_handle: Option<CCspPushInputAdapterHandle>,
35+
/// Push adapter handle (stored as AtomicPtr for thread-safe access)
36+
adapter_handle: Arc<AtomicPtr<c_void>>,
37+
38+
/// Thread handle
39+
thread_handle: Option<JoinHandle<()>>,
3540
}
3641

3742
impl RustInputAdapter {
@@ -41,36 +46,83 @@ impl RustInputAdapter {
4146
interval_ms: if interval_ms > 0 { interval_ms } else { 100 },
4247
counter: Arc::new(AtomicI64::new(0)),
4348
running: Arc::new(AtomicBool::new(false)),
44-
adapter_handle: None,
49+
adapter_handle: Arc::new(AtomicPtr::new(ptr::null_mut())),
50+
thread_handle: None,
4551
}
4652
}
4753

4854
/// Start the adapter.
4955
///
50-
/// In a full implementation with CSP C API symbols linked, this would
51-
/// spawn a background thread that calls ccsp_push_input_adapter_push_int64.
56+
/// Spawns a background thread that pushes integer values at the configured
57+
/// interval using the CSP C API.
5258
pub fn start(&mut self, adapter: CCspPushInputAdapterHandle) {
53-
self.adapter_handle = Some(adapter);
59+
// Store the adapter handle (AtomicPtr is Send + Sync)
60+
self.adapter_handle.store(adapter, Ordering::SeqCst);
5461
self.running.store(true, Ordering::SeqCst);
5562

63+
let running = Arc::clone(&self.running);
64+
let counter = Arc::clone(&self.counter);
65+
let adapter_handle = Arc::clone(&self.adapter_handle);
66+
let interval_ms = self.interval_ms;
67+
5668
eprintln!(
57-
"[RustInputAdapter] Started with interval {} ms (adapter handle: {:?})",
69+
"[RustInputAdapter] Starting with interval {} ms (adapter handle: {:?})",
5870
self.interval_ms, adapter
5971
);
6072

61-
// Note: In a full implementation, we would spawn a thread here that
62-
// calls ccsp_push_input_adapter_push_int64 to push values.
63-
// This requires the CSP C API symbols to be available at runtime.
73+
// Spawn background thread that pushes values
74+
let handle = thread::spawn(move || {
75+
let interval = Duration::from_millis(interval_ms);
76+
77+
while running.load(Ordering::SeqCst) {
78+
let value = counter.fetch_add(1, Ordering::SeqCst);
79+
let adapter_ptr = adapter_handle.load(Ordering::SeqCst);
80+
81+
// Push the counter value to CSP using the C API
82+
if !adapter_ptr.is_null() {
83+
unsafe {
84+
let result = ccsp_push_input_adapter_push_int64(
85+
adapter_ptr,
86+
value,
87+
ptr::null_mut(),
88+
);
89+
// Log any errors (in production, handle more gracefully)
90+
if result != crate::bindings::CCspErrorCode::Ok {
91+
eprintln!(
92+
"[RustInputAdapter] Push failed with error: {:?}",
93+
result
94+
);
95+
}
96+
}
97+
}
98+
99+
thread::sleep(interval);
100+
}
101+
102+
eprintln!(
103+
"[RustInputAdapter] Thread exiting after {} values",
104+
counter.load(Ordering::SeqCst)
105+
);
106+
});
107+
108+
self.thread_handle = Some(handle);
64109
}
65110

66111
/// Stop the adapter.
67112
pub fn stop(&mut self) {
68113
eprintln!(
69-
"[RustInputAdapter] Stopped after {} values",
114+
"[RustInputAdapter] Stopping after {} values",
70115
self.counter.load(Ordering::SeqCst)
71116
);
72117

73118
self.running.store(false, Ordering::SeqCst);
119+
120+
// Wait for the thread to finish
121+
if let Some(handle) = self.thread_handle.take() {
122+
if let Err(e) = handle.join() {
123+
eprintln!("[RustInputAdapter] Thread join error: {:?}", e);
124+
}
125+
}
74126
}
75127
}
76128

examples/05_cpp/5_c_api_adapter_rust/src/output_adapter.rs

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,21 @@
33
//! This module demonstrates how to implement a CSP output adapter in Rust
44
//! using the C API FFI bindings.
55
//!
6-
//! Note: The actual value retrieval requires the CSP C API symbols to be available
7-
//! at runtime. When built standalone, the execute callback logs that it was invoked
8-
//! but cannot access the actual values.
6+
//! The adapter receives values from the CSP graph and prints them to stderr,
7+
//! demonstrating how to use the C API to retrieve typed values.
98
10-
use std::ffi::c_void;
9+
use std::ffi::{c_char, c_void};
10+
use std::slice;
11+
use std::str;
1112

1213
use crate::bindings::{
13-
CCspDateTime, CCspEngineHandle, CCspInputHandle,
14+
ccsp_engine_now, ccsp_input_get_last_bool, ccsp_input_get_last_datetime,
15+
ccsp_input_get_last_double, ccsp_input_get_last_int64, ccsp_input_get_last_string,
16+
ccsp_input_get_type, CCspDateTime, CCspEngineHandle, CCspErrorCode, CCspInputHandle,
17+
CCspType,
1418
};
1519

16-
/// Output adapter that prints values to stdout.
20+
/// Output adapter that prints values to stderr.
1721
///
1822
/// This adapter is called by CSP whenever the input time series ticks.
1923
/// It retrieves the latest value and prints it with an optional prefix.
@@ -54,24 +58,65 @@ impl RustOutputAdapter {
5458
/// # Safety
5559
///
5660
/// The engine and input handles must be valid.
57-
///
58-
/// Note: In a full implementation with CSP C API symbols linked, this would
59-
/// call ccsp_engine_now, ccsp_input_get_type, and ccsp_input_get_last_* to
60-
/// retrieve and print the actual values.
6161
pub unsafe fn execute(&mut self, engine: CCspEngineHandle, input: CCspInputHandle) {
6262
let prefix = self.prefix.as_deref().unwrap_or("");
6363

6464
self.count += 1;
6565

66-
// Note: Without CSP C API symbols, we can only log that execute was called.
67-
// In a full implementation, we would call:
68-
// let now = ccsp_engine_now(engine);
69-
// let input_type = ccsp_input_get_type(input);
70-
// ccsp_input_get_last_int64/double/bool/string(input, &mut value)
71-
eprintln!(
72-
"{}[RustOutputAdapter] execute called (count={}, engine={:?}, input={:?})",
73-
prefix, self.count, engine, input
74-
);
66+
// Get current engine time
67+
let now = ccsp_engine_now(engine);
68+
69+
// Get the type of the input
70+
let input_type = ccsp_input_get_type(input);
71+
72+
// Retrieve and print the value based on type
73+
match input_type {
74+
CCspType::Bool => {
75+
let mut value: i8 = 0;
76+
if ccsp_input_get_last_bool(input, &mut value) == CCspErrorCode::Ok {
77+
eprintln!(
78+
"{}[{}] bool: {}",
79+
prefix,
80+
now,
81+
if value != 0 { "true" } else { "false" }
82+
);
83+
}
84+
}
85+
CCspType::Int64 => {
86+
let mut value: i64 = 0;
87+
if ccsp_input_get_last_int64(input, &mut value) == CCspErrorCode::Ok {
88+
eprintln!("{}[{}] int64: {}", prefix, now, value);
89+
}
90+
}
91+
CCspType::Double => {
92+
let mut value: f64 = 0.0;
93+
if ccsp_input_get_last_double(input, &mut value) == CCspErrorCode::Ok {
94+
eprintln!("{}[{}] double: {}", prefix, now, value);
95+
}
96+
}
97+
CCspType::String => {
98+
let mut data: *const c_char = std::ptr::null();
99+
let mut len: usize = 0;
100+
if ccsp_input_get_last_string(input, &mut data, &mut len) == CCspErrorCode::Ok
101+
&& !data.is_null() && len > 0 {
102+
let bytes = slice::from_raw_parts(data as *const u8, len);
103+
if let Ok(s) = str::from_utf8(bytes) {
104+
eprintln!("{}[{}] string: {}", prefix, now, s);
105+
} else {
106+
eprintln!("{}[{}] string: <invalid utf8>", prefix, now);
107+
}
108+
}
109+
}
110+
CCspType::DateTime => {
111+
let mut value: CCspDateTime = 0;
112+
if ccsp_input_get_last_datetime(input, &mut value) == CCspErrorCode::Ok {
113+
eprintln!("{}[{}] datetime: {} ns", prefix, now, value);
114+
}
115+
}
116+
_ => {
117+
eprintln!("{}[{}] <type {:?}>", prefix, now, input_type);
118+
}
119+
}
75120
}
76121
}
77122

0 commit comments

Comments
 (0)