Skip to content

Commit 06d1829

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

3 files changed

Lines changed: 224 additions & 42 deletions

File tree

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

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use std::ffi::{c_char, c_void};
1111
use pyo3::prelude::*;
1212
use pyo3::ffi;
13+
use libc::{dlsym, RTLD_DEFAULT};
1314

1415
/// CSP DateTime type (nanoseconds since epoch)
1516
pub type CCspDateTime = i64;
@@ -158,11 +159,80 @@ pub struct CCspAdapterManagerVTable {
158159
// We use weak linking / dynamic_lookup on macOS.
159160
// ============================================================================
160161

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;
162+
type FnCcspEngineNow = unsafe extern "C" fn(engine: CCspEngineHandle) -> CCspDateTime;
163+
type FnCcspInputGetType = unsafe extern "C" fn(input: CCspInputHandle) -> CCspType;
164+
type FnCcspInputGetLastBool = unsafe extern "C" fn(input: CCspInputHandle, out: *mut i8) -> CCspErrorCode;
165+
type FnCcspInputGetLastInt64 = unsafe extern "C" fn(input: CCspInputHandle, out: *mut i64) -> CCspErrorCode;
166+
type FnCcspInputGetLastDouble = unsafe extern "C" fn(input: CCspInputHandle, out: *mut f64) -> CCspErrorCode;
167+
type FnCcspInputGetLastString = unsafe extern "C" fn(
168+
input: CCspInputHandle,
169+
out_data: *mut *const c_char,
170+
out_length: *mut usize,
171+
) -> CCspErrorCode;
172+
type FnCcspInputGetLastDatetime = unsafe extern "C" fn(
173+
input: CCspInputHandle,
174+
out: *mut CCspDateTime,
175+
) -> CCspErrorCode;
176+
type FnCcspPushInputAdapterPushInt64 = unsafe extern "C" fn(
177+
adapter: CCspPushInputAdapterHandle,
178+
value: i64,
179+
batch: *mut c_void,
180+
) -> CCspErrorCode;
181+
182+
unsafe fn resolve_symbol<T: Copy>(name: &'static [u8]) -> Option<T> {
183+
let symbol = dlsym(RTLD_DEFAULT, name.as_ptr() as *const c_char);
184+
if symbol.is_null() {
185+
None
186+
} else {
187+
Some(std::mem::transmute_copy(&symbol))
188+
}
189+
}
190+
191+
pub unsafe fn csp_engine_now(engine: CCspEngineHandle) -> Option<CCspDateTime> {
192+
resolve_symbol::<FnCcspEngineNow>(b"ccsp_engine_now\0").map(|f| f(engine))
193+
}
194+
195+
pub unsafe fn csp_input_get_type(input: CCspInputHandle) -> Option<CCspType> {
196+
resolve_symbol::<FnCcspInputGetType>(b"ccsp_input_get_type\0").map(|f| f(input))
197+
}
198+
199+
pub unsafe fn csp_input_get_last_bool(input: CCspInputHandle, out: *mut i8) -> Option<CCspErrorCode> {
200+
resolve_symbol::<FnCcspInputGetLastBool>(b"ccsp_input_get_last_bool\0").map(|f| f(input, out))
201+
}
202+
203+
pub unsafe fn csp_input_get_last_int64(input: CCspInputHandle, out: *mut i64) -> Option<CCspErrorCode> {
204+
resolve_symbol::<FnCcspInputGetLastInt64>(b"ccsp_input_get_last_int64\0").map(|f| f(input, out))
205+
}
206+
207+
pub unsafe fn csp_input_get_last_double(input: CCspInputHandle, out: *mut f64) -> Option<CCspErrorCode> {
208+
resolve_symbol::<FnCcspInputGetLastDouble>(b"ccsp_input_get_last_double\0").map(|f| f(input, out))
209+
}
210+
211+
pub unsafe fn csp_input_get_last_string(
212+
input: CCspInputHandle,
213+
out_data: *mut *const c_char,
214+
out_length: *mut usize,
215+
) -> Option<CCspErrorCode> {
216+
resolve_symbol::<FnCcspInputGetLastString>(b"ccsp_input_get_last_string\0")
217+
.map(|f| f(input, out_data, out_length))
218+
}
219+
220+
pub unsafe fn csp_input_get_last_datetime(
221+
input: CCspInputHandle,
222+
out: *mut CCspDateTime,
223+
) -> Option<CCspErrorCode> {
224+
resolve_symbol::<FnCcspInputGetLastDatetime>(b"ccsp_input_get_last_datetime\0")
225+
.map(|f| f(input, out))
226+
}
227+
228+
pub unsafe fn csp_push_input_adapter_push_int64(
229+
adapter: CCspPushInputAdapterHandle,
230+
value: i64,
231+
batch: *mut c_void,
232+
) -> Option<CCspErrorCode> {
233+
resolve_symbol::<FnCcspPushInputAdapterPushInt64>(b"ccsp_push_input_adapter_push_int64\0")
234+
.map(|f| f(adapter, value, batch))
235+
}
166236

167237
// ============================================================================
168238
// Capsule creation helpers

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

Lines changed: 73 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+
csp_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,86 @@ 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 = csp_push_input_adapter_push_int64(
85+
adapter_ptr,
86+
value,
87+
ptr::null_mut(),
88+
);
89+
if result.is_none() {
90+
eprintln!("[RustInputAdapter] CSP symbol missing: ccsp_push_input_adapter_push_int64");
91+
break;
92+
}
93+
if result != Some(crate::bindings::CCspErrorCode::Ok) {
94+
eprintln!(
95+
"[RustInputAdapter] Push failed with error: {:?}",
96+
result
97+
);
98+
}
99+
}
100+
}
101+
102+
thread::sleep(interval);
103+
}
104+
105+
eprintln!(
106+
"[RustInputAdapter] Thread exiting after {} values",
107+
counter.load(Ordering::SeqCst)
108+
);
109+
});
110+
111+
self.thread_handle = Some(handle);
64112
}
65113

66114
/// Stop the adapter.
67115
pub fn stop(&mut self) {
68116
eprintln!(
69-
"[RustInputAdapter] Stopped after {} values",
117+
"[RustInputAdapter] Stopping after {} values",
70118
self.counter.load(Ordering::SeqCst)
71119
);
72120

73121
self.running.store(false, Ordering::SeqCst);
122+
123+
// Wait for the thread to finish
124+
if let Some(handle) = self.thread_handle.take() {
125+
if let Err(e) = handle.join() {
126+
eprintln!("[RustInputAdapter] Thread join error: {:?}", e);
127+
}
128+
}
74129
}
75130
}
76131

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

Lines changed: 76 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+
csp_engine_now, csp_input_get_last_bool, csp_input_get_last_datetime,
15+
csp_input_get_last_double, csp_input_get_last_int64, csp_input_get_last_string,
16+
csp_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,77 @@ 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 = match csp_engine_now(engine) {
68+
Some(value) => value,
69+
None => {
70+
eprintln!("{}[RustOutputAdapter] CSP symbol missing: ccsp_engine_now", prefix);
71+
return;
72+
}
73+
};
74+
75+
// Get the type of the input
76+
let input_type = match csp_input_get_type(input) {
77+
Some(value) => value,
78+
None => {
79+
eprintln!("{}[RustOutputAdapter] CSP symbol missing: ccsp_input_get_type", prefix);
80+
return;
81+
}
82+
};
83+
84+
// Retrieve and print the value based on type
85+
match input_type {
86+
CCspType::Bool => {
87+
let mut value: i8 = 0;
88+
if csp_input_get_last_bool(input, &mut value) == Some(CCspErrorCode::Ok) {
89+
eprintln!(
90+
"{}[{}] bool: {}",
91+
prefix,
92+
now,
93+
if value != 0 { "true" } else { "false" }
94+
);
95+
}
96+
}
97+
CCspType::Int64 => {
98+
let mut value: i64 = 0;
99+
if csp_input_get_last_int64(input, &mut value) == Some(CCspErrorCode::Ok) {
100+
eprintln!("{}[{}] int64: {}", prefix, now, value);
101+
}
102+
}
103+
CCspType::Double => {
104+
let mut value: f64 = 0.0;
105+
if csp_input_get_last_double(input, &mut value) == Some(CCspErrorCode::Ok) {
106+
eprintln!("{}[{}] double: {}", prefix, now, value);
107+
}
108+
}
109+
CCspType::String => {
110+
let mut data: *const c_char = std::ptr::null();
111+
let mut len: usize = 0;
112+
if csp_input_get_last_string(input, &mut data, &mut len) == Some(CCspErrorCode::Ok)
113+
&& !data.is_null() && len > 0 {
114+
let bytes = slice::from_raw_parts(data as *const u8, len);
115+
if let Ok(s) = str::from_utf8(bytes) {
116+
eprintln!("{}[{}] string: {}", prefix, now, s);
117+
} else {
118+
eprintln!("{}[{}] string: <invalid utf8>", prefix, now);
119+
}
120+
}
121+
}
122+
CCspType::DateTime => {
123+
let mut value: CCspDateTime = 0;
124+
if csp_input_get_last_datetime(input, &mut value) == Some(CCspErrorCode::Ok) {
125+
eprintln!("{}[{}] datetime: {} ns", prefix, now, value);
126+
}
127+
}
128+
_ => {
129+
eprintln!("{}[{}] <type {:?}>", prefix, now, input_type);
130+
}
131+
}
75132
}
76133
}
77134

0 commit comments

Comments
 (0)