mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-28 16:41:52 +08:00
- 新增 RustDesk 模块,支持与 RustDesk 客户端连接 - 实现会合服务器协议和 P2P 连接 - 支持 NaCl 加密和密钥交换 - 添加视频帧和 HID 事件适配器 - 添加 Protobuf 协议定义 (message.proto, rendezvous.proto) - 新增完整项目文档 - 各功能模块文档 (video, hid, msd, otg, webrtc 等) - hwcodec 和 RustDesk 协议技术报告 - 系统架构和技术栈文档 - 更新 Web 前端 RustDesk 配置界面和 API
354 lines
8.8 KiB
Markdown
354 lines
8.8 KiB
Markdown
# Events 模块文档
|
|
|
|
## 1. 模块概述
|
|
|
|
Events 模块提供事件总线功能,用于模块间通信和状态广播。
|
|
|
|
### 1.1 主要功能
|
|
|
|
- 事件发布/订阅
|
|
- 多订阅者广播
|
|
- WebSocket 事件推送
|
|
- 状态变更通知
|
|
|
|
### 1.2 文件结构
|
|
|
|
```
|
|
src/events/
|
|
└── mod.rs # EventBus 实现
|
|
```
|
|
|
|
---
|
|
|
|
## 2. 架构设计
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
│ Event System │
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
┌────────────────┐ ┌────────────────┐ ┌────────────────┐
|
|
│ Video │ │ HID │ │ Audio │
|
|
│ Module │ │ Module │ │ Module │
|
|
└───────┬────────┘ └───────┬────────┘ └───────┬────────┘
|
|
│ │ │
|
|
│ publish() │ publish() │ publish()
|
|
└──────────────────┼──────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────┐
|
|
│ EventBus │
|
|
│ (broadcast channel) │
|
|
└──────────┬──────────┘
|
|
│
|
|
┌─────────────────┼─────────────────┐
|
|
│ │ │
|
|
│ subscribe() │ subscribe() │
|
|
▼ ▼ ▼
|
|
┌────────────────┐ ┌────────────────┐ ┌────────────────┐
|
|
│ WebSocket │ │ DeviceInfo │ │ Internal │
|
|
│ Handler │ │ Broadcaster │ │ Tasks │
|
|
└────────────────┘ └────────────────┘ └────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 3. 核心组件
|
|
|
|
### 3.1 EventBus
|
|
|
|
```rust
|
|
pub struct EventBus {
|
|
/// 广播发送器
|
|
tx: broadcast::Sender<SystemEvent>,
|
|
}
|
|
|
|
impl EventBus {
|
|
/// 创建事件总线
|
|
pub fn new() -> Self {
|
|
let (tx, _) = broadcast::channel(1024);
|
|
Self { tx }
|
|
}
|
|
|
|
/// 发布事件
|
|
pub fn publish(&self, event: SystemEvent) {
|
|
let _ = self.tx.send(event);
|
|
}
|
|
|
|
/// 订阅事件
|
|
pub fn subscribe(&self) -> broadcast::Receiver<SystemEvent> {
|
|
self.tx.subscribe()
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3.2 SystemEvent
|
|
|
|
```rust
|
|
#[derive(Clone, Debug, Serialize)]
|
|
pub enum SystemEvent {
|
|
// 视频事件
|
|
StreamStateChanged {
|
|
state: String,
|
|
device: Option<String>,
|
|
resolution: Option<Resolution>,
|
|
fps: Option<f32>,
|
|
},
|
|
|
|
VideoDeviceChanged {
|
|
added: Vec<String>,
|
|
removed: Vec<String>,
|
|
},
|
|
|
|
// HID 事件
|
|
HidStateChanged {
|
|
backend: String,
|
|
initialized: bool,
|
|
keyboard_connected: bool,
|
|
mouse_connected: bool,
|
|
mouse_mode: String,
|
|
error: Option<String>,
|
|
},
|
|
|
|
// MSD 事件
|
|
MsdStateChanged {
|
|
mode: String,
|
|
connected: bool,
|
|
image: Option<String>,
|
|
error: Option<String>,
|
|
},
|
|
|
|
MsdDownloadProgress {
|
|
download_id: String,
|
|
downloaded: u64,
|
|
total: u64,
|
|
speed: u64,
|
|
},
|
|
|
|
// ATX 事件
|
|
AtxStateChanged {
|
|
power_on: bool,
|
|
last_action: Option<String>,
|
|
error: Option<String>,
|
|
},
|
|
|
|
// 音频事件
|
|
AudioStateChanged {
|
|
enabled: bool,
|
|
streaming: bool,
|
|
device: Option<String>,
|
|
error: Option<String>,
|
|
},
|
|
|
|
// 配置事件
|
|
ConfigChanged {
|
|
section: String,
|
|
},
|
|
|
|
// 设备信息汇总
|
|
DeviceInfo {
|
|
video: VideoInfo,
|
|
hid: HidInfo,
|
|
msd: MsdInfo,
|
|
atx: AtxInfo,
|
|
audio: AudioInfo,
|
|
},
|
|
|
|
// 系统错误
|
|
SystemError {
|
|
module: String,
|
|
severity: String,
|
|
message: String,
|
|
},
|
|
|
|
// RustDesk 事件
|
|
RustDeskStatusChanged {
|
|
status: String,
|
|
device_id: Option<String>,
|
|
error: Option<String>,
|
|
},
|
|
|
|
RustDeskConnectionOpened {
|
|
connection_id: String,
|
|
peer_id: String,
|
|
},
|
|
|
|
RustDeskConnectionClosed {
|
|
connection_id: String,
|
|
peer_id: String,
|
|
reason: String,
|
|
},
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 4. 设备信息广播器
|
|
|
|
在 `main.rs` 中启动的后台任务:
|
|
|
|
```rust
|
|
pub fn spawn_device_info_broadcaster(
|
|
state: Arc<AppState>,
|
|
events: Arc<EventBus>,
|
|
) -> JoinHandle<()> {
|
|
tokio::spawn(async move {
|
|
let mut rx = events.subscribe();
|
|
let mut debounce = tokio::time::interval(Duration::from_millis(100));
|
|
let mut pending = false;
|
|
|
|
loop {
|
|
tokio::select! {
|
|
// 收到事件
|
|
result = rx.recv() => {
|
|
if result.is_ok() {
|
|
pending = true;
|
|
}
|
|
}
|
|
|
|
// 防抖定时器
|
|
_ = debounce.tick() => {
|
|
if pending {
|
|
pending = false;
|
|
// 收集设备信息
|
|
let device_info = state.get_device_info().await;
|
|
// 广播
|
|
events.publish(SystemEvent::DeviceInfo(device_info));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 5. WebSocket 事件推送
|
|
|
|
```rust
|
|
pub async fn ws_handler(
|
|
ws: WebSocketUpgrade,
|
|
State(state): State<Arc<AppState>>,
|
|
) -> impl IntoResponse {
|
|
ws.on_upgrade(|socket| handle_ws(socket, state))
|
|
}
|
|
|
|
async fn handle_ws(mut socket: WebSocket, state: Arc<AppState>) {
|
|
let mut rx = state.events.subscribe();
|
|
|
|
loop {
|
|
tokio::select! {
|
|
// 发送事件给客户端
|
|
result = rx.recv() => {
|
|
if let Ok(event) = result {
|
|
let json = serde_json::to_string(&event).unwrap();
|
|
if socket.send(Message::Text(json)).await.is_err() {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 接收客户端消息
|
|
msg = socket.recv() => {
|
|
match msg {
|
|
Some(Ok(Message::Close(_))) | None => break,
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 6. 使用示例
|
|
|
|
### 6.1 发布事件
|
|
|
|
```rust
|
|
// 视频模块发布状态变更
|
|
events.publish(SystemEvent::StreamStateChanged {
|
|
state: "streaming".to_string(),
|
|
device: Some("/dev/video0".to_string()),
|
|
resolution: Some(Resolution { width: 1920, height: 1080 }),
|
|
fps: Some(30.0),
|
|
});
|
|
|
|
// HID 模块发布状态变更
|
|
events.publish(SystemEvent::HidStateChanged {
|
|
backend: "otg".to_string(),
|
|
initialized: true,
|
|
keyboard_connected: true,
|
|
mouse_connected: true,
|
|
mouse_mode: "absolute".to_string(),
|
|
error: None,
|
|
});
|
|
```
|
|
|
|
### 6.2 订阅事件
|
|
|
|
```rust
|
|
let mut rx = events.subscribe();
|
|
|
|
loop {
|
|
match rx.recv().await {
|
|
Ok(SystemEvent::StreamStateChanged { state, .. }) => {
|
|
println!("Stream state: {}", state);
|
|
}
|
|
Ok(SystemEvent::HidStateChanged { backend, .. }) => {
|
|
println!("HID backend: {}", backend);
|
|
}
|
|
Err(_) => break,
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 7. 前端事件处理
|
|
|
|
```typescript
|
|
// 连接 WebSocket
|
|
const ws = new WebSocket('/api/ws');
|
|
|
|
ws.onmessage = (event) => {
|
|
const data = JSON.parse(event.data);
|
|
|
|
switch (data.type) {
|
|
case 'StreamStateChanged':
|
|
updateStreamStatus(data);
|
|
break;
|
|
case 'HidStateChanged':
|
|
updateHidStatus(data);
|
|
break;
|
|
case 'MsdStateChanged':
|
|
updateMsdStatus(data);
|
|
break;
|
|
case 'DeviceInfo':
|
|
updateAllDevices(data);
|
|
break;
|
|
}
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## 8. 最佳实践
|
|
|
|
### 8.1 事件粒度
|
|
|
|
- 使用细粒度事件便于精确更新
|
|
- DeviceInfo 用于初始化和定期同步
|
|
|
|
### 8.2 防抖
|
|
|
|
- 使用 100ms 防抖避免事件风暴
|
|
- 合并多个快速变更
|
|
|
|
### 8.3 错误处理
|
|
|
|
- 发布失败静默忽略 (fire-and-forget)
|
|
- 订阅者断开自动清理
|