pub struct AsyncRecordingHook { /* private fields */ }Expand description
异步录制钩子(Actor 模式 + Bounded Queue)
§内存安全(v1.2.1 关键修正)
使用 有界通道(Bounded Channel)防止 OOM:
- 容量: 100,000 帧(约 3.3 分钟 @ 500Hz)
- 队列满时丢帧,而不是无限增长导致 OOM
- 可通过
dropped_frames和frame_counter计数器监控
§设计理由
❌ v1.1 错误设计: unbounded() 可能导致 OOM
✅ v1.2.1 正确设计: bounded(10000) 优雅降级
✅ v1.3.0 最新设计: bounded(100000) 更长录制时长(约 3.3 分钟)
§示例
use piper_driver::recording::AsyncRecordingHook;
use piper_driver::hooks::FrameCallback;
use std::sync::Arc;
// 创建录制钩子
let (hook, rx) = AsyncRecordingHook::new();
// 直接持有计数器的 Arc 引用
let dropped_counter = hook.dropped_frames().clone();
let frame_counter = hook.frame_counter().clone();
// 注册为回调
let callback = Arc::new(hook) as Arc<dyn FrameCallback>;
// 监控丢帧和帧数
let dropped = dropped_counter.load(std::sync::atomic::Ordering::Relaxed);
let frames = frame_counter.load(std::sync::atomic::Ordering::Relaxed);
println!("已录制 {} 帧,丢了 {} 帧", frames, dropped);Implementations§
Source§impl AsyncRecordingHook
impl AsyncRecordingHook
Sourcepub fn new() -> (Self, Receiver<TimestampedFrame>)
pub fn new() -> (Self, Receiver<TimestampedFrame>)
创建新的录制钩子
§队列容量
- 容量: 100,000 帧(约 3.3 分钟 @ 500Hz)
- 500Hz CAN 总线: 约 3.3 分钟缓存
- 1kHz CAN 总线: 约 1.6 分钟缓存
- 内存占用: 约 2.4MB(100k × 24 bytes/frame)
设计理由:
- 足够吸收短暂的磁盘 I/O 延迟,同时防止 OOM
- 支持中等时长的录制(3 分钟左右)
- 超过此时长会导致丢帧(Channel 满)
§返回
(hook, rx): 钩子实例和接收端
§示例
use piper_driver::recording::AsyncRecordingHook;
let (hook, rx) = AsyncRecordingHook::new();Sourcepub fn with_stop_condition(
stop_on_id: Option<u32>,
) -> (Self, Receiver<TimestampedFrame>)
pub fn with_stop_condition( stop_on_id: Option<u32>, ) -> (Self, Receiver<TimestampedFrame>)
Sourcepub fn is_stop_requested(&self) -> bool
pub fn is_stop_requested(&self) -> bool
获取停止请求标志(新增:v1.4)
用于检查是否应该停止录制
Sourcepub fn stop_requested(&self) -> &Arc<AtomicBool>
pub fn stop_requested(&self) -> &Arc<AtomicBool>
获取停止请求标志的 Arc 引用(新增:v1.4)
用于跨线程共享停止标志
Sourcepub fn sender(&self) -> Sender<TimestampedFrame>
pub fn sender(&self) -> Sender<TimestampedFrame>
Sourcepub fn dropped_frames(&self) -> &Arc<AtomicU64>
pub fn dropped_frames(&self) -> &Arc<AtomicU64>
获取丢帧计数器
§使用建议(v1.2.1)
✅ 推荐: 在创建钩子时直接持有 Arc 引用
use piper_driver::recording::AsyncRecordingHook;
use std::sync::atomic::Ordering;
let (hook, _rx) = AsyncRecordingHook::new();
let dropped_counter = hook.dropped_frames().clone(); // 在此持有
// 直接读取,无需从 Context downcast
let count = dropped_counter.load(Ordering::Relaxed);❌ 不推荐: 试图从 Context 中 downcast(需要 Trait 继承 Any)
§返回
Arc<AtomicU64>: 丢帧计数器的引用
Sourcepub fn dropped_count(&self) -> u64
pub fn dropped_count(&self) -> u64
Sourcepub fn frame_counter(&self) -> &Arc<AtomicU64>
pub fn frame_counter(&self) -> &Arc<AtomicU64>
获取帧计数器(新增:v1.3.0)
§使用建议
✅ 推荐: 在创建钩子时直接持有 Arc 引用
use piper_driver::recording::AsyncRecordingHook;
use std::sync::atomic::Ordering;
let (hook, _rx) = AsyncRecordingHook::new();
let frame_counter = hook.frame_counter().clone(); // 在此持有
// 直接读取,无需从 Context downcast
let count = frame_counter.load(Ordering::Relaxed);§返回
Arc<AtomicU64>: 帧计数器的引用(不可变,只读)
Sourcepub fn frame_count(&self) -> u64
pub fn frame_count(&self) -> u64
Trait Implementations§
Source§impl FrameCallback for AsyncRecordingHook
impl FrameCallback for AsyncRecordingHook
Source§fn on_frame_received(&self, frame: &PiperFrame)
fn on_frame_received(&self, frame: &PiperFrame)
当接收到 CAN 帧时调用
§性能要求
- <1μs 开销(非阻塞)
- 队列满时丢帧,而非阻塞或无限增长
§时间戳精度(v1.2.1)
⏱️ 必须使用硬件时间戳:
use piper_driver::recording::TimestampedFrame;
use piper_protocol::PiperFrame;
let frame = PiperFrame::new_standard(0x251, &[1, 2, 3, 4]);
let ts_frame = TimestampedFrame::from(&frame);
assert_eq!(ts_frame.timestamp_us, frame.timestamp_us); // ✅ 硬件时间戳❌ 禁止软件生成时间戳:
// ❌ 错误:回调执行时间已晚于帧到达时间(仅说明概念) // let ts = SystemTime::now().duration_since(UNIX_EPOCH)?.as_micros() as u64;
Source§fn on_frame_sent(&self, frame: &PiperFrame)
fn on_frame_sent(&self, frame: &PiperFrame)
Auto Trait Implementations§
impl Freeze for AsyncRecordingHook
impl RefUnwindSafe for AsyncRecordingHook
impl Send for AsyncRecordingHook
impl Sync for AsyncRecordingHook
impl Unpin for AsyncRecordingHook
impl UnwindSafe for AsyncRecordingHook
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
Converts
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
Converts
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more