@@ -7,24 +7,47 @@ use crate::utils::{Notifier, str_from_ptr};
77use crate :: { impl_has_impl_ptr, rclz_try} ;
88use ros_z:: Builder ;
99use ros_z:: context:: ZContext ;
10+ use std:: ffi:: c_void;
1011use std:: str:: FromStr ;
12+ use std:: sync:: atomic:: { AtomicU64 , Ordering } ;
1113use std:: sync:: Arc ;
1214use std:: {
1315 ffi:: { CString , c_char, c_int} ,
1416 ops:: Deref ,
1517} ;
1618use zenoh:: Result ;
1719
20+ // Global instance ID counter
21+ static INSTANCE_ID_COUNTER : AtomicU64 = AtomicU64 :: new ( 1 ) ;
22+
1823pub struct ContextImpl {
1924 inner : ZContext ,
2025 notifier : Arc < Notifier > ,
26+ /// Instance ID for this context (increments globally)
27+ instance_id : u64 ,
28+ /// Copy of init options given during init
29+ init_options : rcl_init_options_t ,
30+ /// RMW context (zero-initialized for now)
31+ rmw_context : rmw_context_t ,
32+ /// Whether the context is valid (active/not shutdown)
33+ is_valid : bool ,
34+ /// Domain ID for this context
35+ domain_id : usize ,
2136}
2237
2338impl ContextImpl {
24- pub ( crate ) fn new ( zcontext : ZContext ) -> Self {
39+ pub ( crate ) fn new ( zcontext : ZContext , init_options : rcl_init_options_t , domain_id : usize ) -> Self {
40+ // Fetch and increment the global instance ID
41+ let instance_id = INSTANCE_ID_COUNTER . fetch_add ( 1 , Ordering :: SeqCst ) ;
42+
2543 Self {
2644 inner : zcontext,
2745 notifier : Arc :: new ( Notifier :: default ( ) ) ,
46+ instance_id,
47+ init_options,
48+ rmw_context : rmw_context_t:: default ( ) ,
49+ is_valid : true ,
50+ domain_id,
2851 }
2952 }
3053
@@ -33,21 +56,76 @@ impl ContextImpl {
3356 name : * const :: std:: os:: raw:: c_char ,
3457 namespace_ : * const :: std:: os:: raw:: c_char ,
3558 context : * mut rcl_context_t ,
36- _options : * const rcl_node_options_t ,
59+ options : * const rcl_node_options_t ,
3760 ) -> Result < NodeImpl > {
61+ // Normalize namespace: zenoh expects namespace WITHOUT leading slash
62+ // but ROS2 provides it WITH leading slash, so we need to strip it for zenoh
63+ let input_namespace = str_from_ptr ( namespace_) ?;
64+ let normalized_namespace = if input_namespace. is_empty ( ) || input_namespace == "/" {
65+ ""
66+ } else {
67+ input_namespace. strip_prefix ( '/' ) . unwrap_or ( input_namespace)
68+ } ;
69+
3870 let znode = self
3971 . inner
4072 . create_node ( str_from_ptr ( name) ?)
41- . with_namespace ( str_from_ptr ( namespace_ ) ? )
73+ . with_namespace ( normalized_namespace )
4274 . build ( ) ?;
4375
44- let namespace = CString :: from_str ( & znode. entity . namespace ) ?;
45- let name = CString :: from_str ( & znode. entity . name ) ?;
76+ let namespace_str = & znode. entity . namespace ;
77+ let name_str = & znode. entity . name ;
78+
79+ // Ensure namespace has leading / to match C++ RCL behavior
80+ let namespace_with_slash = if namespace_str. is_empty ( ) {
81+ "/" . to_string ( )
82+ } else {
83+ format ! ( "/{}" , namespace_str)
84+ } ;
85+
86+ // Fully qualified name should always start with /
87+ let fq_name_str = if namespace_with_slash == "/" {
88+ format ! ( "/{}" , name_str)
89+ } else {
90+ format ! ( "{}/{}" , namespace_with_slash, name_str)
91+ } ;
92+
93+ // Logger name: remove leading / and replace / with .
94+ let logger_name_str = if namespace_with_slash == "/" {
95+ name_str. to_string ( )
96+ } else {
97+ namespace_with_slash[ 1 ..] . replace ( '/' , "." ) + "." + name_str
98+ } ;
99+
100+ let namespace = CString :: new ( namespace_with_slash) ?;
101+ let name = CString :: from_str ( name_str) ?;
102+ let fq_name = CString :: new ( fq_name_str) . unwrap ( ) ;
103+ let logger_name = CString :: new ( logger_name_str) . unwrap ( ) ;
104+
105+ // Copy options if provided
106+ let node_options = if options. is_null ( ) {
107+ rcl_node_options_t:: default ( )
108+ } else {
109+ unsafe { std:: ptr:: read ( options) }
110+ } ;
111+
112+ // Create a dummy rmw_handle (non-null pointer for compatibility)
113+ let rmw_handle = Box :: into_raw ( Box :: new ( rmw_node_t:: default ( ) ) ) ;
114+
115+ // Create a zero-initialized guard condition (for now)
116+ let graph_guard_condition = rcl_guard_condition_t:: default ( ) ;
117+
46118 Ok ( NodeImpl {
47119 inner : znode,
48120 name,
49121 namespace,
122+ fq_name,
123+ logger_name,
50124 notifier : self . notifier . clone ( ) ,
125+ instance_id : self . instance_id ,
126+ options : node_options,
127+ rmw_handle,
128+ graph_guard_condition,
51129 } )
52130 }
53131
@@ -93,7 +171,14 @@ pub extern "C" fn rcl_publisher_event_init(
93171 publisher : * const rcl_publisher_t ,
94172 event_type : rcl_publisher_event_type_t ,
95173) -> rcl_ret_t {
96- RCL_RET_OK as _
174+ if event. is_null ( ) || publisher. is_null ( ) {
175+ return RCL_RET_INVALID_ARGUMENT as _ ;
176+ }
177+ // Assume valid types are 0-10, invalid >10
178+ if event_type as i32 > 10 {
179+ return RCL_RET_INVALID_ARGUMENT as _ ;
180+ }
181+ RCL_RET_UNSUPPORTED as _
97182}
98183
99184#[ unsafe( no_mangle) ]
@@ -102,30 +187,223 @@ pub extern "C" fn rcl_subscription_event_init(
102187 subscription : * const rcl_subscription_t ,
103188 event_type : rcl_subscription_event_type_t ,
104189) -> rcl_ret_t {
190+ // FIXME: Follow the event implementation in rmw_zenoh_cpp: /home/circle/Workings/ZettaScale/project/nix-ros/ws/src/rmw_zenoh/rmw_zenoh_cpp/src/detail/event.cpp
191+ if event. is_null ( ) || subscription. is_null ( ) {
192+ return RCL_RET_INVALID_ARGUMENT as _ ;
193+ }
194+ // Assume valid types are 0-10, invalid >10
195+ if event_type as i32 > 10 {
196+ return RCL_RET_INVALID_ARGUMENT as _ ;
197+ }
198+ RCL_RET_UNSUPPORTED as _
199+ }
200+
201+ #[ unsafe( no_mangle) ]
202+ pub extern "C" fn rcl_event_fini ( event : * mut rcl_event_t ) -> rcl_ret_t {
203+ // FIXME: Follow the event implementation in rmw_zenoh_cpp: /home/circle/Workings/ZettaScale/project/nix-ros/ws/src/rmw_zenoh/rmw_zenoh_cpp/src/detail/event.cpp
105204 RCL_RET_OK as _
106205}
107206
207+ #[ unsafe( no_mangle) ]
208+ pub extern "C" fn rcl_event_is_valid ( event : * const rcl_event_t ) -> bool {
209+ // FIXME: Follow the event implementation in rmw_zenoh_cpp: /home/circle/Workings/ZettaScale/project/nix-ros/ws/src/rmw_zenoh/rmw_zenoh_cpp/src/detail/event.cpp
210+ false
211+ }
212+
213+ #[ unsafe( no_mangle) ]
214+ pub extern "C" fn rcl_take_event (
215+ event : * const rcl_event_t ,
216+ event_status : * mut c_void ,
217+ ) -> rcl_ret_t {
218+ // FIXME: Follow the event implementation in rmw_zenoh_cpp: /home/circle/Workings/ZettaScale/project/nix-ros/ws/src/rmw_zenoh/rmw_zenoh_cpp/src/detail/event.cpp
219+ if event. is_null ( ) {
220+ return RCL_RET_EVENT_INVALID as _ ;
221+ }
222+ // Assume the event is invalid since we don't support events
223+ RCL_RET_EVENT_INVALID as _
224+ }
225+
226+ #[ unsafe( no_mangle) ]
227+ pub extern "C" fn rcl_event_get_rmw_handle ( event : * const rcl_event_t ) -> * mut rmw_event_t {
228+ // FIXME: print a non-support warnings
229+ std:: ptr:: null_mut ( )
230+ }
231+
232+ #[ unsafe( no_mangle) ]
233+ pub extern "C" fn rcl_get_zero_initialized_event ( ) -> rcl_event_t {
234+ rcl_event_t:: default ( )
235+ }
236+
108237#[ unsafe( no_mangle) ]
109238pub unsafe extern "C" fn rcl_context_is_valid ( context : * const rcl_context_t ) -> bool {
110- unsafe { !( * context) . impl_ . is_null ( ) }
239+ if context. is_null ( ) {
240+ return false ;
241+ }
242+
243+ unsafe {
244+ if ( * context) . impl_ . is_null ( ) {
245+ return false ;
246+ }
247+ }
248+
249+ // Check if the impl is valid (not shutdown)
250+ match context. borrow_impl ( ) {
251+ Ok ( impl_) => impl_. is_valid ,
252+ Err ( _) => false ,
253+ }
111254}
112255
113256#[ unsafe( no_mangle) ]
114257pub extern "C" fn rcl_shutdown ( context : * mut rcl_context_t ) -> rcl_ret_t {
115258 tracing:: trace!( "rcl_shutdown" ) ;
116- rclz_try ! {
117- context. own_impl( ) ?. inner. shutdown( ) ?;
259+
260+ // Check for null pointer
261+ if context. is_null ( ) {
262+ return RCL_RET_INVALID_ARGUMENT as _ ;
263+ }
264+
265+ // Check if context is zero-initialized (never init'd)
266+ unsafe {
267+ if ( * context) . impl_ . is_null ( ) {
268+ return RCL_RET_INVALID_ARGUMENT as _ ;
269+ }
270+ }
271+
272+ // Check if context is valid (if not valid, it's already shutdown)
273+ if !unsafe { rcl_context_is_valid ( context) } {
274+ return RCL_RET_ALREADY_SHUTDOWN as _ ;
275+ }
276+
277+ // Shutdown the context
278+ match context. borrow_mut_impl ( ) {
279+ Ok ( impl_) => {
280+ match impl_. inner . shutdown ( ) {
281+ Ok ( _) => {
282+ // Mark context as invalid
283+ impl_. is_valid = false ;
284+ RCL_RET_OK as _
285+ }
286+ Err ( e) => {
287+ tracing:: error!( "rcl_shutdown failed: {e}" ) ;
288+ RCL_RET_ERROR as _
289+ }
290+ }
291+ }
292+ Err ( _) => RCL_RET_INVALID_ARGUMENT as _ ,
118293 }
119294}
120295
121296#[ unsafe( no_mangle) ]
122- pub extern "C" fn rcl_context_fini ( _context : * mut rcl_context_t ) -> rcl_ret_t {
123- // FIXME : tracing is not usable at the exit stage
297+ pub extern "C" fn rcl_context_fini ( context : * mut rcl_context_t ) -> rcl_ret_t {
298+ // TODO : tracing is not usable at the exit stage
124299 // tracing::trace!("rcl_context_fini");
300+
301+ // Check for null argument
302+ if context. is_null ( ) {
303+ return RCL_RET_INVALID_ARGUMENT as _ ;
304+ }
305+
306+ unsafe {
307+ // If context is zero-initialized (impl is null), return OK
308+ if ( * context) . impl_ . is_null ( ) {
309+ return RCL_RET_OK as _ ;
310+ }
311+
312+ // If context is still valid (not shutdown), return error
313+ if rcl_context_is_valid ( context) {
314+ // In C, this sets an error: "rcl_shutdown() not called on the given context"
315+ return RCL_RET_INVALID_ARGUMENT as _ ;
316+ }
317+ }
318+
319+ // TODO: Implement proper cleanup of context resources
320+ // For now, just return OK after checks
125321 RCL_RET_OK as _
126322}
127323
128324#[ unsafe( no_mangle) ]
129325pub extern "C" fn rcl_get_zero_initialized_context ( ) -> rcl_context_t {
130326 rcl_context_t:: default ( )
131327}
328+
329+ #[ unsafe( no_mangle) ]
330+ pub extern "C" fn rcl_context_get_instance_id ( context : * const rcl_context_t ) -> u64 {
331+ match context. borrow_impl ( ) {
332+ Ok ( impl_) => {
333+ // Return 0 if context is not valid (shutdown)
334+ if impl_. is_valid {
335+ impl_. instance_id
336+ } else {
337+ 0
338+ }
339+ }
340+ Err ( _) => 0 ,
341+ }
342+ }
343+
344+ #[ unsafe( no_mangle) ]
345+ pub extern "C" fn rcl_context_get_domain_id (
346+ context : * const rcl_context_t ,
347+ domain_id : * mut usize ,
348+ ) -> rcl_ret_t {
349+ if context. is_null ( ) || domain_id. is_null ( ) {
350+ return RCL_RET_INVALID_ARGUMENT as _ ;
351+ }
352+ match context. borrow_impl ( ) {
353+ Ok ( impl_) => {
354+ unsafe {
355+ * domain_id = impl_. domain_id ;
356+ }
357+ RCL_RET_OK as _
358+ }
359+ Err ( _) => RCL_RET_INVALID_ARGUMENT as _ ,
360+ }
361+ }
362+
363+ #[ unsafe( no_mangle) ]
364+ pub extern "C" fn rcl_context_get_init_options (
365+ context : * const rcl_context_t ,
366+ ) -> * const rcl_init_options_t {
367+ // Check for null argument
368+ if context. is_null ( ) {
369+ return std:: ptr:: null ( ) ;
370+ }
371+
372+ // Check if context is zero-initialized
373+ unsafe {
374+ if ( * context) . impl_ . is_null ( ) {
375+ // In C, this sets an error: "context is zero-initialized"
376+ return std:: ptr:: null ( ) ;
377+ }
378+ }
379+
380+ // Return pointer to init_options stored in context impl
381+ match context. borrow_impl ( ) {
382+ Ok ( impl_) => & impl_. init_options ,
383+ Err ( _) => std:: ptr:: null ( ) ,
384+ }
385+ }
386+
387+ #[ unsafe( no_mangle) ]
388+ pub extern "C" fn rcl_context_get_rmw_context (
389+ context : * mut rcl_context_t ,
390+ ) -> * mut rmw_context_t {
391+ // Check for null argument
392+ if context. is_null ( ) {
393+ return std:: ptr:: null_mut ( ) ;
394+ }
395+
396+ // Check if context is zero-initialized
397+ unsafe {
398+ if ( * context) . impl_ . is_null ( ) {
399+ // In C, this sets an error: "context is zero-initialized"
400+ return std:: ptr:: null_mut ( ) ;
401+ }
402+ }
403+
404+ // Return pointer to rmw_context stored in context impl
405+ match context. borrow_mut_impl ( ) {
406+ Ok ( impl_) => & mut impl_. rmw_context ,
407+ Err ( _) => std:: ptr:: null_mut ( ) ,
408+ }
409+ }
0 commit comments