feat: 初始化

This commit is contained in:
Horldsence
2026-09-22 20:05:16 +08:00
commit 7d87987e41
54 changed files with 6446 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "can-link"
description = "CAN 传输抽象:帧与链路通道(无协议知识)"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
[dependencies]
tokio = { workspace = true }
[lints]
workspace = true
+75
View File
@@ -0,0 +1,75 @@
//! CAN 传输抽象:帧与链路通道。
//!
//! 本 crate **不含任何协议知识**(无 DJI ID 常量):只定义经典 CAN 数据帧
//! 与一对 tokio 通道构成的链路端点。协议族(`dji-esc` / 未来 `dji-gm6020`)
//! 与总线后端(SocketCAN 桥、仿真器)共享此类型,互不依赖。
#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]
use tokio::sync::mpsc;
/// 经典 CAN 数据帧(数据长度固定 8 字节)。
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CanFrame {
/// 11 位标准 ID。
pub id: u32,
/// 数据域。
pub data: [u8; 8],
}
impl CanFrame {
/// 构造一帧。
#[must_use]
pub const fn new(id: u32, data: [u8; 8]) -> Self {
Self { id, data }
}
}
/// CAN 链路端点。
///
/// - `outgoing_tx`:本端发往总线的帧;
/// - `incoming_rx`:本端从总线收到的帧。
///
/// 一条链路由两个端点构成(见 [`CanLink::pair`])。持有本端、把对端交给
/// 对侧(SocketCAN 桥、仿真器)即可通信。
#[derive(Debug)]
pub struct CanLink {
/// 发往总线。
pub outgoing_tx: mpsc::Sender<CanFrame>,
/// 来自总线。
pub incoming_rx: mpsc::Receiver<CanFrame>,
}
impl CanLink {
/// 由已有通道组装端点。
#[must_use]
pub const fn new(
outgoing_tx: mpsc::Sender<CanFrame>,
incoming_rx: mpsc::Receiver<CanFrame>,
) -> Self {
Self { outgoing_tx, incoming_rx }
}
/// 创建一对互联端点:`a` 发出的帧会被 `b` 收到,反之亦然。
///
/// `bound` 为每方向缓冲帧数(建议 ≥ 64,1kHz 收发下留足余量)。
#[must_use]
pub fn pair(bound: usize) -> (Self, Self) {
let (tx_a_to_b, rx_at_b) = mpsc::channel(bound);
let (tx_b_to_a, rx_at_a) = mpsc::channel(bound);
(Self::new(tx_a_to_b, rx_at_a), Self::new(tx_b_to_a, rx_at_b))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn pair_loops_both_directions() {
let (mut a, mut b) = CanLink::pair(8);
a.outgoing_tx.send(CanFrame::new(0x200, [1, 0, 0, 0, 0, 0, 0, 0])).await.unwrap();
b.outgoing_tx.send(CanFrame::new(0x201, [2, 0, 0, 0, 0, 0, 0, 0])).await.unwrap();
assert_eq!(b.incoming_rx.recv().await.unwrap().id, 0x200);
assert_eq!(a.incoming_rx.recv().await.unwrap().id, 0x201);
}
}