flashdb_rs/tsdb/
mod.rs

1mod types;
2pub use types::*;
3
4mod reader;
5pub use reader::*;
6
7use crate::{
8    fdb_blob, fdb_blob_make_write, fdb_blob_read, fdb_db_t, fdb_tsdb, fdb_tsdb_control_read,
9    fdb_tsdb_control_write, fdb_tsdb_deinit, fdb_tsdb_init, fdb_tsdb_t, fdb_tsl_append_with_ts,
10    fdb_tsl_clean, fdb_tsl_iter, fdb_tsl_iter_by_time, fdb_tsl_iter_reverse, fdb_tsl_query_count,
11    fdb_tsl_set_status, Error, FlashDispatch, RawHandle, FDB_KV_NAME_MAX,
12    FDB_TSDB_CTRL_GET_LAST_TIME, FDB_TSDB_CTRL_GET_ROLLOVER, FDB_TSDB_CTRL_GET_SEC_SIZE,
13    FDB_TSDB_CTRL_SET_MAX_SIZE, FDB_TSDB_CTRL_SET_NOT_FORMAT, FDB_TSDB_CTRL_SET_ROLLOVER,
14    FDB_TSDB_CTRL_SET_SEC_SIZE,
15};
16
17use core::{
18    ffi::{c_char, c_void},
19    marker::PhantomData,
20};
21
22use embedded_storage::nor_flash::NorFlash;
23
24pub struct TSDB<S: NorFlash> {
25    inner: fdb_tsdb,
26    storage: S,
27    user_data: FlashDispatch,
28    #[cfg(feature = "log")]
29    name_buf: [u8; FDB_KV_NAME_MAX as usize + 1],
30    initialized: bool,
31    // 由于 fdb_kvdb 内部引用了 storage 和 name_buf,结构体无法安全地在线程间移动,
32    // 因此标记为 !Send 和 !Sync。
33    _marker: PhantomData<*const ()>,
34}
35
36#[cfg(feature = "std")]
37impl TSDB<crate::storage::StdStorage> {
38    /// 在 `std` 环境下,创建一个基于文件的 TSDB 实例。
39    ///
40    /// 此函数返回一个 `Box<TSDB<StdStorage>>`,以确保数据库实例在内存中的地址是稳定的,
41    /// 防止因栈帧移动导致传递给 C 库的内部指针失效。
42    ///
43    /// # 参数
44    /// - `name`: 数据库名称
45    /// - `path`: 数据库文件存储的目录
46    /// - `sec_size`: 扇区大小
47    /// - `max_size`: 数据库最大容量
48    /// - `entry_max`: 单个日志条目的最大长度
49    pub fn new_file(
50        name: &str,
51        path: &str,
52        sec_size: u32,
53        max_size: u32,
54        entry_max: usize,
55    ) -> Result<Box<Self>, Error> {
56        let storage = crate::storage::StdStorage::new(
57            path,
58            name,
59            sec_size,
60            max_size,
61            crate::storage::FileStrategy::Multi,
62        )?;
63
64        let mut db = Box::new(TSDB::new(storage));
65        db.set_name(name)?;
66        db.init(entry_max)?;
67        Ok(db)
68    }
69}
70
71impl<S: NorFlash> TSDB<S> {
72    /// 创建一个未初始化的 KVDB 实例。
73    ///
74    /// # Arguments
75    /// * `storage` - 一个实现了 `NorFlash` trait 的存储实例。
76    pub fn new(storage: S) -> Self {
77        Self {
78            inner: Default::default(),
79            storage,
80            user_data: FlashDispatch::new::<S>(),
81            #[cfg(feature = "log")]
82            name_buf: [0; FDB_KV_NAME_MAX as usize + 1],
83            initialized: false,
84            _marker: PhantomData,
85        }
86    }
87
88    /// 设置数据库名称,仅用于日志输出。
89    ///
90    /// **注意**: 此方法必须在 `init()` 之前调用。
91    pub fn set_name(&mut self, name: &str) -> Result<(), Error> {
92        if name.len() > FDB_KV_NAME_MAX as usize {
93            return Err(Error::KvNameError);
94        }
95        #[cfg(feature = "log")]
96        {
97            self.name_buf[..name.len()].copy_from_slice(name.as_bytes());
98            self.name_buf[name.len()] = b'\0';
99        }
100        Ok(())
101    }
102
103    /// 设置数据库为不可格式化模式。
104    ///
105    /// 在此模式下,如果数据库初始化时发现头部信息损坏,将返回错误而不是自动格式化。
106    /// **注意**: 此方法必须在 `init()` 之前调用。
107    pub fn set_not_formatable(&mut self, enable: bool) {
108        self.fdb_tsdb_control_write(FDB_TSDB_CTRL_SET_NOT_FORMAT, enable);
109    }
110
111    /// 检查数据库是否处于不可格式化模式。
112    pub fn not_formatable(&mut self) -> bool {
113        let mut enable = false;
114        self.fdb_tsdb_control_read(FDB_TSDB_CTRL_SET_NOT_FORMAT, &mut enable);
115        return enable;
116    }
117
118    /// 启用或禁用翻转写入 (Rollover)。
119    ///
120    /// 启用后,当数据库写满时,最旧的数据将被新数据覆盖。
121    /// 禁用后,数据库写满时 `append` 操作将返回 `SavedFull` 错误。
122    /// 默认启用。
123    pub fn set_rollover(&mut self, enable: bool) {
124        self.fdb_tsdb_control_write(FDB_TSDB_CTRL_SET_ROLLOVER, enable);
125    }
126
127    /// 检查翻转写入 (Rollover) 是否已启用。
128    pub fn rollover(&self) -> bool {
129        let mut flag = false;
130        self.fdb_tsdb_control_read(FDB_TSDB_CTRL_GET_ROLLOVER, &mut flag);
131        flag
132    }
133
134    /// 获取当前扇区大小(字节)
135    pub fn sec_size(&self) -> u32 {
136        let mut size: u32 = 0;
137        self.fdb_tsdb_control_read(FDB_TSDB_CTRL_GET_SEC_SIZE, &mut size);
138        size
139    }
140
141    /// 获取上次追加 TSL 时的时间戳
142    pub fn last_time(&self) -> i64 {
143        let mut size: i64 = 0;
144        self.fdb_tsdb_control_read(FDB_TSDB_CTRL_GET_LAST_TIME, &mut size);
145        size
146    }
147
148    /// 初始化数据库。
149    ///
150    /// 此方法会加载现有数据库或根据 `storage` 的容量创建一个新的数据库。
151    /// 它是实际与底层 C 库交互的入口。
152    ///
153    /// # 参数
154    /// - `default_kvs`: (可选) 提供一组默认键值对。如果数据库是首次创建,
155    ///   这些键值对将被写入数据库。
156    pub fn init(&mut self, entry_max: usize) -> Result<(), Error> {
157        if self.initialized {
158            return Ok(());
159        }
160        // 从 NorFlash trait 获取扇区大小和总容量
161        let sec_size = S::ERASE_SIZE as u32;
162        let max_size = self.storage.capacity() as u32;
163
164        unsafe {
165            let db_ptr = self.handle() as fdb_db_t;
166            (*db_ptr).mode = crate::fdb_storage_type_FDB_STORAGE_CUSTOM;
167
168            // 设置 flashdb 的配置
169            self.fdb_tsdb_control_write(FDB_TSDB_CTRL_SET_SEC_SIZE, sec_size);
170            self.fdb_tsdb_control_write(FDB_TSDB_CTRL_SET_MAX_SIZE, max_size);
171
172            // 只有这里获取才不会导致悬空指针
173            self.user_data.instance = &mut self.storage as *mut _ as *mut c_void;
174
175            #[cfg(feature = "log")]
176            let name = self.name_buf.as_ptr() as *const c_char;
177            #[cfg(not(feature = "log"))]
178            let name = b"\0".as_ptr() as *const c_char;
179
180            let result = fdb_tsdb_init(
181                db_ptr as *mut fdb_tsdb,
182                name,
183                core::ptr::null(),
184                None,
185                entry_max,
186                &mut self.user_data as *mut _ as *mut c_void,
187            );
188
189            if result == crate::fdb_err_t_FDB_NO_ERR {
190                self.initialized = true;
191                Ok(())
192            } else {
193                Err(result.into())
194            }
195        }
196    }
197}
198
199impl<S: NorFlash> TSDB<S> {
200    #[inline]
201    fn fdb_blob_read(&mut self, blob: &mut fdb_blob) -> usize {
202        unsafe { fdb_blob_read(self.handle() as *mut _, blob) }
203    }
204
205    #[inline]
206    fn fdb_tsdb_control_write<T>(&mut self, cmd: u32, arg: T) {
207        fdb_tsdb_control_write(self.handle(), cmd, arg)
208    }
209
210    #[inline]
211    fn fdb_tsdb_control_read<T>(&self, cmd: u32, arg: &mut T) {
212        fdb_tsdb_control_read(self.handle(), cmd, arg)
213    }
214}
215
216impl<S: NorFlash> TSDB<S> {
217    /// 追加带时间戳的日志条目
218    ///
219    /// # 参数
220    /// - `timestamp`: 时间戳(毫秒级UNIX时间)
221    /// - `data`: 要存储的字节数据
222    ///
223    /// # 返回
224    /// - `Ok(())`: 追加成功
225    /// - `Err(Error)`: 存储失败(如空间不足)
226    pub fn append_with_timestamp(&mut self, timestamp: i64, data: &[u8]) -> Result<(), Error> {
227        // 创建可写Blob结构(封装数据缓冲区)
228        let mut blob = fdb_blob_make_write(data);
229        // 调用底层C函数追加带时间戳的TSL
230        Error::convert(unsafe { fdb_tsl_append_with_ts(self.handle(), &mut blob, timestamp as _) })
231    }
232
233    /// 设置日志条目的状态(逻辑标记)
234    ///
235    /// # 参数
236    /// - `timestamp`: 目标日志的时间戳
237    /// - `status`: 要设置的状态(如已同步、已删除)
238    ///
239    /// # 应用场景
240    /// - 标记数据已上传至云端
241    /// - 逻辑删除旧数据(非物理删除)
242    pub fn set_status(&mut self, tsl: &mut TSLEntry, status: TSLStatus) -> Result<(), Error> {
243        // 调用底层函数设置TSL状态
244        Error::convert(unsafe { fdb_tsl_set_status(self.handle(), tsl.handle(), status as _) })
245    }
246
247    /// 查询指定时间范围内特定状态的日志数量
248    ///
249    /// # 参数
250    /// - `from`: 起始时间戳
251    /// - `to`: 结束时间戳
252    /// - `status`: 要筛选的状态
253    ///
254    /// # 注意
255    /// - 结果通过底层API直接输出,未返回Rust值
256    pub fn count(&mut self, from: i64, to: i64, status: TSLStatus) -> usize {
257        unsafe { fdb_tsl_query_count(self.handle(), from as _, to as _, status as _) }
258    }
259
260    /// 迭代所有日志条目(支持正向/反向)
261    ///
262    /// # 参数
263    /// - `callback`: 迭代回调函数,返回`false`可提前终止
264    /// - `reverse`: 是否反向迭代(最新条目优先)
265    ///
266    pub fn tsdb_iter<F: FnMut(&mut TSDB<S>, &mut TSLEntry) -> bool + Send>(
267        &mut self,
268        callback: F,
269        reverse: bool,
270    ) {
271        let db = self.handle();
272        let mut callback_data = CallbackData { db: self, callback };
273        unsafe {
274            // 根据标志选择正向/反向迭代器
275            if reverse {
276                fdb_tsl_iter_reverse(
277                    db,
278                    Some(iter_callback::<S, F>),
279                    &mut callback_data as *mut _ as *mut _,
280                )
281            } else {
282                fdb_tsl_iter(
283                    db,
284                    Some(iter_callback::<S, F>),
285                    &mut callback_data as *mut _ as *mut _,
286                )
287            }
288        }
289    }
290
291    /// 按时间范围迭代日志条目
292    ///
293    /// # 参数
294    /// - `from`: 起始时间戳
295    /// - `to`: 结束时间戳 (包含)
296    /// - `callback`: 迭代回调函数 (包含)
297    /// -
298    //
299    pub fn tsdb_iter_by_time<F: FnMut(&mut TSDB<S>, &mut TSLEntry) -> bool + Send>(
300        &mut self,
301        from: i64,
302        to: i64,
303        callback: F,
304    ) {
305        let db = self.handle();
306        let mut callback_data = CallbackData { db: self, callback };
307        unsafe {
308            fdb_tsl_iter_by_time(
309                db,
310                from as _,
311                to as _,
312                Some(iter_callback::<S, F>),
313                &mut callback_data as *mut _ as *mut _,
314            )
315        };
316    }
317
318    /// 重置数据库(清除所有日志条目)
319    ///
320    /// # 警告
321    /// - 此操作会删除所有数据,不可恢复
322    /// - 建议在初始化或测试时使用
323    pub fn reset(&mut self) -> Result<(), Error> {
324        unsafe { fdb_tsl_clean(self.handle()) };
325        Ok(())
326    }
327
328    /// 获取指定TSL条目的数据
329    ///
330    /// # 参数
331    /// - `tsl_obj`: TSL对象(包含状态和长度信息)
332    ///
333    /// # 返回
334    /// - `Ok(Some(data))`: 状态有效时返回数据
335    /// - `Ok(None)`: 状态为UNUSED/DELETED时返回None
336    /// - `Err(Error)`: 读取失败(如数据损坏)
337    #[cfg(feature = "alloc")]
338    pub fn get_value(&mut self, tsl_obj: &TSLEntry) -> Result<Option<alloc::vec::Vec<u8>>, Error> {
339        // 转换TSL状态(unsafe操作,确保枚举值匹配)
340        let status = unsafe { core::mem::transmute(tsl_obj.status()) };
341        match status {
342            // 可读取状态(PRE_WRITE/Write/UserStatus1)
343            TSLStatus::PRE_WRITE | TSLStatus::Write | TSLStatus::UserStatus1 => {
344                // 创建指定长度的缓冲区
345                let mut data: alloc::vec::Vec<u8> = alloc::vec::Vec::with_capacity(tsl_obj.value_len());
346                unsafe { data.set_len(tsl_obj.value_len()) };
347                // 根据TSL创建Blob读取结构
348                let mut blob = fdb_blob_make_by_tsl(&mut data, tsl_obj, 0);
349
350                // 执行底层读取
351                let read_len = self.fdb_blob_read(&mut blob);
352                if read_len != data.len() {
353                    return Err(Error::ReadError);
354                }
355                Ok(Some(data))
356            }
357            // 不可读取状态(UNUSED/Deleted/UserStatus2)
358            TSLStatus::UNUSED | TSLStatus::Deleted | TSLStatus::UserStatus2 => Ok(None),
359        }
360    }
361
362    /// 打开TSL数据读取器
363    ///
364    /// # 参数
365    /// - `tsl_obj`: 目标TSL对象(通过迭代获取)
366    ///
367    /// # 返回
368    /// - `TSDBReader`: 实现了`Read`和`Seek`的读取器
369    pub fn open_read(&mut self, entry: TSLEntry) -> TSDBReader<'_, S> {
370        TSDBReader::new(self, entry)
371    }
372}
373
374impl<S: NorFlash> RawHandle for TSDB<S> {
375    type Handle = fdb_tsdb_t;
376
377    fn handle(&self) -> Self::Handle {
378        &self.inner as *const _ as *mut _
379    }
380}
381
382impl<S: NorFlash> Drop for TSDB<S> {
383    fn drop(&mut self) {
384        if self.initialized {
385            unsafe {
386                fdb_tsdb_deinit(self.handle());
387            };
388        }
389    }
390}