Skip to main content

AsyncRecordingHook

Struct AsyncRecordingHook 

Source
pub struct AsyncRecordingHook { /* private fields */ }
Expand description

异步录制钩子(Actor 模式 + Bounded Queue)

§内存安全(v1.2.1 关键修正)

使用 有界通道(Bounded Channel)防止 OOM:

  • 容量: 100,000 帧(约 3.3 分钟 @ 500Hz)
  • 队列满时丢帧,而不是无限增长导致 OOM
  • 可通过 dropped_framesframe_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

Source

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();
Source

pub fn with_stop_condition( stop_on_id: Option<u32>, ) -> (Self, Receiver<TimestampedFrame>)

创建新的录制钩子(带停止条件)

§参数
  • stop_on_id: 当收到此 CAN ID 时停止录制(None 表示不启用)
§返回
  • (hook, rx): 钩子实例和接收端
§示例
use piper_driver::recording::AsyncRecordingHook;

// 当收到 0x2A4 时停止录制(末端位姿帧)
let (hook, rx) = AsyncRecordingHook::with_stop_condition(Some(0x2A4));
Source

pub fn is_stop_requested(&self) -> bool

获取停止请求标志(新增:v1.4)

用于检查是否应该停止录制

Source

pub fn stop_requested(&self) -> &Arc<AtomicBool>

获取停止请求标志的 Arc 引用(新增:v1.4)

用于跨线程共享停止标志

Source

pub fn sender(&self) -> Sender<TimestampedFrame>

获取发送端(用于自定义场景)

§注意

大多数情况下不需要直接使用此方法,只需将 AsyncRecordingHook 注册为 FrameCallback 即可。

Source

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);

不推荐: 试图从 Contextdowncast(需要 Trait 继承 Any

§返回

Arc<AtomicU64>: 丢帧计数器的引用

Source

pub fn dropped_count(&self) -> u64

获取当前丢帧数量

§返回

当前丢失的帧数

Source

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>: 帧计数器的引用(不可变,只读)

Source

pub fn frame_count(&self) -> u64

获取当前已录制的帧数(新增:v1.3.0)

§返回

当前已成功录制的帧数

Trait Implementations§

Source§

impl FrameCallback for AsyncRecordingHook

Source§

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)

当发送 CAN 帧成功后调用(可选)

§时机

仅在 tx.send() 成功后调用,确保录制的是实际发送的帧。

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

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
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more