refactor: enforce layered crates and Rust audit gates

This commit is contained in:
Horldsence
2026-09-24 13:00:03 +08:00
parent af151525d1
commit 8a582fc102
67 changed files with 3918 additions and 581 deletions
+25
View File
@@ -0,0 +1,25 @@
[package]
name = "socketcan"
description = "SocketCAN transport bridge"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
[lints]
workspace = true
[dependencies]
can = { workspace = true }
tokio = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
[dev-dependencies]
tokio = { workspace = true }
[target.'cfg(target_os = "linux")'.dependencies]
socketcan-rs = { workspace = true }
[package.metadata.sentry-chassis]
layer = "transport/can"
+122
View File
@@ -0,0 +1,122 @@
#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]
//! 真实总线桥接实现(Linux)。
#[cfg(target_os = "linux")]
mod imp {
use std::io;
use std::sync::Arc;
use can::{CanFrame as LinkFrame, CanLink};
use socketcan_rs::{
CanDataFrame, CanFilter, CanFrame as ScFrame, EmbeddedFrame as _, Frame as _,
SocketOptions as _, StandardId, tokio::CanSocket,
};
use tokio::sync::mpsc;
use tracing::{debug, error};
/// 单个链路的通道缓冲(1kHz × 8 电调反馈余量)。
const CHANNEL_BOUND: usize = 512;
/// 打开 CAN 接口并桥接为 [`CanLink`],返回面向总线驱动的端点。
///
/// 内部:
/// - 硬件过滤只放行电调反馈 ID 段(`0x200..=0x20F`,覆盖 0x201–0x208,
/// 非反馈帧再由协议层解码拒绝,双保险);
/// - 两个桥接任务:socket → 通道(反馈)、通道 → socket(命令)。
///
/// # Errors
///
/// 接口不存在 / 权限不足等 `io::Error`。
pub fn open_link(interface: &str) -> io::Result<CanLink> {
let socket = CanSocket::open(interface)?;
socket.set_filters(&[CanFilter::new(0x200, 0x7F0)])?;
let socket = Arc::new(socket);
let (bus_side, bridge_side) = CanLink::pair(CHANNEL_BOUND);
let CanLink { outgoing_tx, incoming_rx } = bridge_side;
tokio::spawn(rx_bridge(Arc::clone(&socket), outgoing_tx));
tokio::spawn(tx_bridge(socket, incoming_rx));
Ok(bus_side)
}
/// socket → 通道:真实总线反馈进入协议 bus。
async fn rx_bridge(socket: Arc<CanSocket>, tx: mpsc::Sender<LinkFrame>) {
loop {
match socket.read_frame().await {
Ok(frame) => {
if let Some(frame) = to_link_frame(&frame) {
if tx.send(frame).await.is_err() {
debug!("bus side closed, rx bridge exiting");
return;
}
}
}
Err(e) => {
// 接口脱落(如 USB 转 CAN 拔出)等致命错误:退出守护进程,
// 由 systemd 拉起重连。
error!(error = %e, "CAN read failed, rx bridge exiting");
return;
}
}
}
}
/// 通道 → socket:协议 bus 命令写入真实总线。
async fn tx_bridge(socket: Arc<CanSocket>, mut rx: mpsc::Receiver<LinkFrame>) {
while let Some(frame) = rx.recv().await {
match to_socketcan_frame(&frame) {
Some(sc_frame) => {
if let Err(e) = socket.write_frame(sc_frame).await {
error!(error = %e, "CAN write failed, tx bridge exiting");
return;
}
}
None => {
debug!(id = frame.id, "drop frame with non-standard CAN id");
}
}
}
debug!("bus side closed, tx bridge exiting");
}
/// socketcan 帧 → 链路帧(仅 8 字节数据帧)。
fn to_link_frame(frame: &ScFrame) -> Option<LinkFrame> {
match frame {
ScFrame::Data(df) if df.data().len() == 8 => {
Some(LinkFrame::new(df.raw_id(), df.data().try_into().ok()?))
}
_ => None,
}
}
/// 链路帧 → socketcan 帧(ID 必须落在 11 位标准 ID 范围内)。
fn to_socketcan_frame(frame: &LinkFrame) -> Option<ScFrame> {
let id = StandardId::new(frame.id as u16)?;
let df = CanDataFrame::new(id, &frame.data)?;
Some(ScFrame::Data(df))
}
}
#[cfg(target_os = "linux")]
pub use imp::*;
#[cfg(not(target_os = "linux"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Unsupported;
#[cfg(not(target_os = "linux"))]
impl std::fmt::Display for Unsupported {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("SocketCAN is only available on Linux")
}
}
#[cfg(not(target_os = "linux"))]
impl std::error::Error for Unsupported {}
#[cfg(not(target_os = "linux"))]
pub fn open_link(_interface: &str) -> Result<can::CanLink, Unsupported> {
Err(Unsupported)
}