1use crate::cfe::sb::msg::MsgId;
4use crate::cfe::time::SysTime;
5use crate::cstring;
6use crate::error::{CfsError, Result, TblError};
7use crate::status::check;
8use crate::{ffi, status};
9use core::ffi::c_void;
10use core::marker::PhantomData;
11use core::mem::{size_of, MaybeUninit};
12use core::ops::{Deref, Drop};
13
14pub type ValidationFn = ffi::CFE_TBL_CallbackFuncPtr_t;
16
17pub trait Validate {
22 fn validate(&self) -> bool {
24 true
25 }
26}
27
28extern "C" fn validate_trampoline<T: Validate>(ptr: *mut c_void) -> i32 {
29 let t = unsafe { &*(ptr as *const T) };
30 if t.validate() { 0 } else { -1 }
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35#[repr(transparent)]
36pub struct TableHandle(pub ffi::CFE_TBL_Handle_t);
37
38#[derive(Debug)]
43pub struct Table<T: Sized> {
44 handle: TableHandle,
45 is_owner: bool,
46 _phantom: PhantomData<T>,
47}
48
49use bitflags::bitflags;
50
51bitflags! {
52 pub struct TableOptions: u16 {
54 const DEFAULT = ffi::CFE_TBL_OPT_DEFAULT as u16;
56 const SINGLE_BUFFERED = ffi::CFE_TBL_OPT_SNGL_BUFFER as u16;
58 const DOUBLE_BUFFERED = ffi::CFE_TBL_OPT_DBL_BUFFER as u16;
60 const DUMP_ONLY = ffi::CFE_TBL_OPT_DUMP_ONLY as u16;
62 const CRITICAL = ffi::CFE_TBL_OPT_CRITICAL as u16;
64 }
65}
66
67pub struct TableInfo(pub(crate) ffi::CFE_TBL_Info_t);
69
70impl TableInfo {
71 pub fn size(&self) -> usize {
73 self.0.Size as usize
74 }
75
76 pub fn num_users(&self) -> u32 {
78 self.0.NumUsers
79 }
80
81 #[cfg(not(nos3_cfe))]
83 pub fn file_time(&self) -> SysTime {
84 SysTime(self.0.FileTime)
85 }
86
87 pub fn crc(&self) -> u32 {
89 self.0.Crc
90 }
91
92 pub fn time_of_last_update(&self) -> SysTime {
94 SysTime(self.0.TimeOfLastUpdate)
95 }
96
97 pub fn table_loaded_once(&self) -> bool {
99 self.0.TableLoadedOnce
100 }
101
102 pub fn dump_only(&self) -> bool {
104 self.0.DumpOnly
105 }
106
107 pub fn double_buffered(&self) -> bool {
109 self.0.DoubleBuffered
110 }
111
112 pub fn user_def_addr(&self) -> bool {
114 self.0.UserDefAddr
115 }
116
117 pub fn critical(&self) -> bool {
119 self.0.Critical
120 }
121
122 pub fn last_file_loaded(&self) -> &str {
124 let len = self
125 .0
126 .LastFileLoaded
127 .iter()
128 .position(|&c| c == 0)
129 .unwrap_or(self.0.LastFileLoaded.len());
130 let bytes = &self.0.LastFileLoaded[..len];
131 let u8slice = unsafe { core::slice::from_raw_parts(bytes.as_ptr() as *const u8, len) };
132 core::str::from_utf8(u8slice).unwrap_or("")
133 }
134}
135
136impl<T: Sized> Table<T> {
137 pub fn new(name: &str, options: TableOptions) -> Result<Self>
143 where
144 T: Default + Validate,
145 {
146 let mut handle = MaybeUninit::uninit();
147 let c_name = cstring::<{ ffi::CFE_MISSION_TBL_MAX_NAME_LENGTH as usize }>(name)
148 .map_err(|_| CfsError::Tbl(TblError::InvalidName))?;
149
150 let status = unsafe {
151 ffi::CFE_TBL_Register(
152 handle.as_mut_ptr(),
153 c_name.as_ptr(),
154 size_of::<T>(),
155 options.bits(),
156 Some(validate_trampoline::<T> as _),
157 )
158 };
159 check(status)?;
160
161 let table = Self {
162 handle: TableHandle(unsafe { handle.assume_init() }),
163 is_owner: true,
164 _phantom: PhantomData,
165 };
166
167 let default = T::default();
168 table.load_from_slice(core::slice::from_ref(&default))?;
169
170 Ok(table)
171 }
172
173 pub fn share(name: &str) -> Result<Self> {
181 let mut handle = MaybeUninit::uninit();
182 let c_name = cstring::<{ ffi::CFE_MISSION_TBL_MAX_FULL_NAME_LEN as usize }>(name)
183 .map_err(|_| CfsError::Tbl(TblError::InvalidName))?;
184
185 let status = unsafe { ffi::CFE_TBL_Share(handle.as_mut_ptr(), c_name.as_ptr()) };
186 check(status)?;
187
188 Ok(Self {
189 handle: TableHandle(unsafe { handle.assume_init() }),
190 is_owner: false,
191 _phantom: PhantomData,
192 })
193 }
194
195 pub fn load_from_file(&self, filename: &str) -> Result<()> {
199 let c_filename = cstring::<{ ffi::OS_MAX_PATH_LEN as usize }>(filename)
200 .map_err(|_| CfsError::Tbl(TblError::FilenameTooLong))?;
201 let status = unsafe {
202 ffi::CFE_TBL_Load(
203 self.handle.0,
204 ffi::CFE_TBL_SrcEnum_CFE_TBL_SRC_FILE,
205 c_filename.as_ptr() as *const _,
206 )
207 };
208 check(status)?;
209 Ok(())
210 }
211
212 pub fn load_from_slice(&self, data: &[T]) -> Result<()> {
216 let status = unsafe {
217 ffi::CFE_TBL_Load(
218 self.handle.0,
219 ffi::CFE_TBL_SrcEnum_CFE_TBL_SRC_ADDRESS,
220 data.as_ptr() as *const _,
221 )
222 };
223 check(status)?;
224 Ok(())
225 }
226
227 pub fn manage(&self) -> Result<()> {
230 check(unsafe { ffi::CFE_TBL_Manage(self.handle.0) })?;
231 Ok(())
232 }
233
234 pub fn modified(&self) -> Result<()> {
237 check(unsafe { ffi::CFE_TBL_Modified(self.handle.0) })?;
238 Ok(())
239 }
240
241 pub fn get(&self) -> Result<TableAccessor<'_, T>> {
244 TableAccessor::new(self.handle)
245 }
246
247 pub fn get_or_default(&self) -> T
250 where
251 T: Default + Copy,
252 {
253 self.get().map(|a| *a).unwrap_or_default()
254 }
255
256 pub fn handle(&self) -> TableHandle {
258 self.handle
259 }
260
261 pub fn get_info(name: &str) -> Result<TableInfo> {
266 let c_name = cstring::<{ ffi::CFE_MISSION_TBL_MAX_FULL_NAME_LEN as usize }>(name)
267 .map_err(|_| CfsError::Tbl(TblError::InvalidName))?;
268
269 let mut tbl_info_uninit = MaybeUninit::uninit();
270
271 check(unsafe { ffi::CFE_TBL_GetInfo(tbl_info_uninit.as_mut_ptr(), c_name.as_ptr()) })?;
272
273 Ok(TableInfo(unsafe { tbl_info_uninit.assume_init() }))
274 }
275
276 pub fn status(&self) -> Result<status::Status> {
278 check(unsafe { ffi::CFE_TBL_GetStatus(self.handle.0) })
279 }
280
281 pub fn update(&self) -> Result<()> {
283 check(unsafe { ffi::CFE_TBL_Update(self.handle.0) })?;
284 Ok(())
285 }
286
287 pub fn validate(&self) -> Result<()> {
289 check(unsafe { ffi::CFE_TBL_Validate(self.handle.0) })?;
290 Ok(())
291 }
292
293 pub fn dump_to_buffer(&self) -> Result<()> {
298 check(unsafe { ffi::CFE_TBL_DumpToBuffer(self.handle.0) })?;
299 Ok(())
300 }
301
302 pub fn notify_by_message(
312 &self,
313 msg_id: MsgId,
314 command_code: u16,
315 parameter: u32,
316 ) -> Result<()> {
317 check(unsafe {
318 ffi::CFE_TBL_NotifyByMessage(self.handle.0, msg_id.0, command_code, parameter)
319 })?;
320 Ok(())
321 }
322
323 pub unsafe fn get_accessors<const N: usize>(
329 handles: [TableHandle; N],
330 ) -> Result<[TableAccessor<'static, ()>; N]> {
331 let mut ptrs: [*mut c_void; N] = [core::ptr::null_mut(); N];
332 let mut ptr_ptrs: [*mut *mut c_void; N] = [core::ptr::null_mut(); N];
333 for i in 0..N {
334 ptr_ptrs[i] = &raw mut ptrs[i];
335 }
336 check(ffi::CFE_TBL_GetAddresses(
337 ptr_ptrs.as_mut_ptr(),
338 N as u16,
339 handles.as_ptr() as *const _,
340 ))?;
341
342 let mut accessors: [MaybeUninit<TableAccessor<'static, ()>>; N] =
343 MaybeUninit::uninit().assume_init();
344
345 for i in 0..N {
346 accessors[i].write(TableAccessor {
347 ptr: ptrs[i] as *const (),
348 handle: handles[i],
349 _phantom: PhantomData,
350 });
351 }
352
353 Ok(accessors.map(|a| a.assume_init()))
354 }
355}
356
357impl<T: Sized> Drop for Table<T> {
358 fn drop(&mut self) {
360 if self.is_owner {
363 let _ = unsafe { ffi::CFE_TBL_Unregister(self.handle.0) };
364 }
365 }
366}
367
368#[derive(Debug)]
372pub struct TableAccessor<'a, T: 'a> {
373 ptr: *const T,
374 handle: TableHandle,
375 _phantom: PhantomData<&'a T>,
376}
377
378impl<'a, T> TableAccessor<'a, T> {
379 pub fn new(handle: TableHandle) -> Result<Self> {
388 let mut ptr = core::ptr::null_mut();
389 let status = unsafe { ffi::CFE_TBL_GetAddress(&mut ptr, handle.0) };
390 if status != ffi::CFE_SUCCESS && status != ffi::CFE_TBL_INFO_UPDATED {
392 return Err(CfsError::from(status));
393 }
394
395 Ok(Self {
396 ptr: ptr as *const T,
397 handle,
398 _phantom: PhantomData,
399 })
400 }
401}
402
403impl<'a, T> Deref for TableAccessor<'a, T> {
404 type Target = T;
405 fn deref(&self) -> &Self::Target {
406 unsafe { &*self.ptr }
407 }
408}
409
410impl<'a, T> Drop for TableAccessor<'a, T> {
411 fn drop(&mut self) {
412 let _ = unsafe { ffi::CFE_TBL_ReleaseAddress(self.handle.0) };
414 }
415}