feat: DevicePlugin Task2 — DevicePlugin骨架 + DeviceBackend trait + 模块注册
This commit is contained in:
@@ -2177,3 +2177,45 @@ ShowenV2 当前版本已达到 **生产就绪** 标准:
|
|||||||
Task 1 已完成,可以进入 Task 2(DevicePlugin 骨架与 Backend trait),建议由架构师王思远负责。
|
Task 1 已完成,可以进入 Task 2(DevicePlugin 骨架与 Backend trait),建议由架构师王思远负责。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2026-03-13 14:30 - 王思远(架构师)
|
||||||
|
|
||||||
|
### Task 2 完成汇报:DevicePlugin 骨架与 Backend trait
|
||||||
|
|
||||||
|
**任务状态**: ✅ 已完成
|
||||||
|
|
||||||
|
**完成内容**:
|
||||||
|
1. ✅ 创建 `src/plugins/device/backend.rs`
|
||||||
|
- 定义 `DeviceBackend` trait,包含 5 个核心方法
|
||||||
|
- 完整的中文文档注释,说明每个方法的职责
|
||||||
|
- trait 标记为 Send,支持多线程环境
|
||||||
|
|
||||||
|
2. ✅ 创建 `src/plugins/device/mod.rs`
|
||||||
|
- 实现 `DevicePlugin` 结构体,包含 ctx 和 backend 字段
|
||||||
|
- 实现 Plugin trait 的所有方法(id, info, capabilities, init, start, handle_message, stop)
|
||||||
|
- handle_message() 正确匹配 DeviceCommand,调用 backend.handle_command()
|
||||||
|
- 通过 ctx.tx 发送 DeviceResponse 响应
|
||||||
|
- 提供 new(backend) 构造函数
|
||||||
|
|
||||||
|
3. ✅ 更新 `src/plugins/mod.rs`
|
||||||
|
- 添加 `pub mod device;` 导出新模块
|
||||||
|
|
||||||
|
4. ✅ 验证通过
|
||||||
|
- `cargo check --workspace --all-targets`: 零 warning
|
||||||
|
- `cargo test --workspace`: 全部通过(66 个测试)
|
||||||
|
|
||||||
|
**架构设计亮点**:
|
||||||
|
- 采用策略模式,DeviceBackend trait 实现平台适配层
|
||||||
|
- 插件采用被动响应式设计,不主动启动任务
|
||||||
|
- 错误处理统一转换为 DeviceResponse::Error,保证消息协议完整性
|
||||||
|
- capabilities() 从 backend 动态获取,支持不同平台的能力声明
|
||||||
|
|
||||||
|
**文件清单**:
|
||||||
|
- `src/plugins/device/backend.rs` (107 行)
|
||||||
|
- `src/plugins/device/mod.rs` (125 行)
|
||||||
|
- `src/plugins/mod.rs` (已更新)
|
||||||
|
|
||||||
|
**下一步**: Task 3 由赵雨薇负责,实现 LinuxArm64Backend。
|
||||||
|
|
||||||
|
|||||||
@@ -142,3 +142,38 @@
|
|||||||
- 性能目标:60fps 渲染、3秒启动、7x24小时稳定
|
- 性能目标:60fps 渲染、3秒启动、7x24小时稳定
|
||||||
- 技术栈:Rust edition 2018(兼容 ARM)
|
- 技术栈:Rust edition 2018(兼容 ARM)
|
||||||
- 关键技术决策记录在 PROGRESS.md
|
- 关键技术决策记录在 PROGRESS.md
|
||||||
|
|
||||||
|
## 任务经验记录
|
||||||
|
|
||||||
|
### 2026-03-13: DevicePlugin Task 2 完成
|
||||||
|
**任务**: DevicePlugin 骨架与 Backend trait 设计
|
||||||
|
|
||||||
|
**完成内容**:
|
||||||
|
1. 创建 `src/plugins/device/backend.rs`,定义 `DeviceBackend` trait
|
||||||
|
- 包含 5 个核心方法:name(), init(), handle_command(), capabilities(), shutdown()
|
||||||
|
- 完整的中文文档注释,说明每个方法的职责和使用方式
|
||||||
|
- trait 要求 Send,支持多线程环境
|
||||||
|
2. 创建 `src/plugins/device/mod.rs`,实现 `DevicePlugin` 结构体
|
||||||
|
- 实现 Plugin trait 的所有方法
|
||||||
|
- handle_message() 正确处理 DeviceCommand,调用 backend.handle_command()
|
||||||
|
- 通过 ctx.tx 发送 DeviceResponse 回去
|
||||||
|
- 提供 new(backend) 构造函数
|
||||||
|
3. 更新 `src/plugins/mod.rs`,添加 `pub mod device;`
|
||||||
|
4. 验证通过:cargo check 零 warning,cargo test 全部通过(66 个测试)
|
||||||
|
|
||||||
|
**架构设计要点**:
|
||||||
|
- DevicePlugin 采用策略模式,通过 DeviceBackend trait 实现平台适配
|
||||||
|
- 插件本身是被动响应式的,不主动启动任务,只响应 DeviceCommand
|
||||||
|
- 错误处理统一转换为 DeviceResponse::Error,保证消息协议的完整性
|
||||||
|
- capabilities() 方法从 backend 获取能力列表,实现动态能力声明
|
||||||
|
|
||||||
|
**技术决策**:
|
||||||
|
- Backend trait 使用 Box<dyn DeviceBackend> 实现运行时多态
|
||||||
|
- init() 方法接收 JSON 配置,为未来扩展预留接口
|
||||||
|
- handle_command() 返回 Result<DeviceResponse>,支持细粒度错误处理
|
||||||
|
- 所有文档注释使用中文,符合团队规范
|
||||||
|
|
||||||
|
**下一步**:
|
||||||
|
- Task 3: 实现 LinuxArm64Backend,支持 Display 和 SleepInhibit 能力
|
||||||
|
- 需要参考 ScreenPlugin 的 systemd-inhibit 实现
|
||||||
|
- 需要读取 /sys/class/graphics/fb0 获取显示信息
|
||||||
|
|||||||
100
src/plugins/device/backend.rs
Normal file
100
src/plugins/device/backend.rs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
//! DeviceBackend trait — 设备后端抽象层
|
||||||
|
//!
|
||||||
|
//! 定义统一的设备操作接口,支持多平台实现。
|
||||||
|
|
||||||
|
use crate::core::message::{DeviceCapability, DeviceCommand, DeviceResponse};
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
/// 设备后端 trait
|
||||||
|
///
|
||||||
|
/// 所有平台的设备后端都需要实现此 trait,提供统一的设备操作接口。
|
||||||
|
/// DevicePlugin 通过此 trait 与具体平台的设备交互,实现跨平台支持。
|
||||||
|
///
|
||||||
|
/// # 实现要求
|
||||||
|
/// - 必须是 Send,以便在多线程环境中使用
|
||||||
|
/// - 所有方法都应该是非阻塞的或快速返回的
|
||||||
|
/// - 错误处理应该返回 Result,而不是 panic
|
||||||
|
///
|
||||||
|
/// # 示例
|
||||||
|
/// ```ignore
|
||||||
|
/// struct MyBackend;
|
||||||
|
///
|
||||||
|
/// impl DeviceBackend for MyBackend {
|
||||||
|
/// fn name(&self) -> &str {
|
||||||
|
/// "my-backend"
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn init(&mut self, config: &serde_json::Value) -> Result<()> {
|
||||||
|
/// // 初始化设备
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn handle_command(&mut self, cmd: DeviceCommand) -> Result<DeviceResponse> {
|
||||||
|
/// // 处理设备命令
|
||||||
|
/// Ok(DeviceResponse::Ok)
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn capabilities(&self) -> Vec<DeviceCapability> {
|
||||||
|
/// vec![DeviceCapability::Display]
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn shutdown(&mut self) -> Result<()> {
|
||||||
|
/// // 清理资源
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub trait DeviceBackend: Send {
|
||||||
|
/// 返回后端名称
|
||||||
|
///
|
||||||
|
/// 用于日志记录和调试,应该返回一个简短的标识符。
|
||||||
|
/// 例如:"linux-arm64", "macos", "windows"
|
||||||
|
fn name(&self) -> &str;
|
||||||
|
|
||||||
|
/// 初始化后端
|
||||||
|
///
|
||||||
|
/// 在插件启动时调用,用于初始化设备资源、读取配置等。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
/// - `config`: 后端配置(JSON 格式),可以为空对象
|
||||||
|
///
|
||||||
|
/// # 返回
|
||||||
|
/// - `Ok(())`: 初始化成功
|
||||||
|
/// - `Err(e)`: 初始化失败,包含错误信息
|
||||||
|
fn init(&mut self, config: &serde_json::Value) -> Result<()>;
|
||||||
|
|
||||||
|
/// 处理设备命令
|
||||||
|
///
|
||||||
|
/// 接收来自业务插件的设备命令,执行相应操作并返回响应。
|
||||||
|
/// 这是后端的核心方法,所有设备操作都通过此方法完成。
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
/// - `cmd`: 设备命令(如 GetDisplayInfo, SetBrightness 等)
|
||||||
|
///
|
||||||
|
/// # 返回
|
||||||
|
/// - `Ok(DeviceResponse)`: 命令执行成功,返回响应数据
|
||||||
|
/// - `Err(e)`: 命令执行失败,包含错误信息
|
||||||
|
///
|
||||||
|
/// # 注意
|
||||||
|
/// - 不支持的命令应该返回 `DeviceResponse::Error("Not implemented")`
|
||||||
|
/// - 方法应该快速返回,避免长时间阻塞
|
||||||
|
fn handle_command(&mut self, cmd: DeviceCommand) -> Result<DeviceResponse>;
|
||||||
|
|
||||||
|
/// 返回后端支持的设备能力列表
|
||||||
|
///
|
||||||
|
/// 用于声明后端支持哪些设备功能,业务插件可以根据此列表
|
||||||
|
/// 判断是否可以使用某个功能。
|
||||||
|
///
|
||||||
|
/// # 返回
|
||||||
|
/// 支持的设备能力列表(如 Display, Touch, Battery 等)
|
||||||
|
fn capabilities(&self) -> Vec<DeviceCapability>;
|
||||||
|
|
||||||
|
/// 关闭后端
|
||||||
|
///
|
||||||
|
/// 在插件停止时调用,用于清理资源、停止子进程等。
|
||||||
|
///
|
||||||
|
/// # 返回
|
||||||
|
/// - `Ok(())`: 关闭成功
|
||||||
|
/// - `Err(e)`: 关闭失败,包含错误信息
|
||||||
|
fn shutdown(&mut self) -> Result<()>;
|
||||||
|
}
|
||||||
135
src/plugins/device/mod.rs
Normal file
135
src/plugins/device/mod.rs
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
//! DevicePlugin — 统一设备管理插件
|
||||||
|
//!
|
||||||
|
//! 提供跨平台的设备操作接口,包括显示、触摸、音频、电池等功能。
|
||||||
|
//! 通过 Backend trait 实现平台适配,业务插件通过消息与 DevicePlugin 交互。
|
||||||
|
|
||||||
|
use crate::core::{
|
||||||
|
message::{Destination, Envelope, Message},
|
||||||
|
plugin::*,
|
||||||
|
};
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
pub mod backend;
|
||||||
|
pub use backend::DeviceBackend;
|
||||||
|
|
||||||
|
/// DevicePlugin 结构体
|
||||||
|
///
|
||||||
|
/// 统一设备管理插件,负责处理所有设备相关的操作。
|
||||||
|
/// 通过 DeviceBackend trait 实现平台适配,支持多平台。
|
||||||
|
///
|
||||||
|
/// # 架构
|
||||||
|
/// ```text
|
||||||
|
/// 业务插件 → Message::DeviceCommand → DevicePlugin → DeviceBackend → 硬件
|
||||||
|
/// ↓
|
||||||
|
/// DeviceResponse
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # 使用示例
|
||||||
|
/// ```ignore
|
||||||
|
/// // 创建 DevicePlugin
|
||||||
|
/// let backend = Box::new(LinuxArm64Backend::new());
|
||||||
|
/// let mut plugin = DevicePlugin::new(backend);
|
||||||
|
///
|
||||||
|
/// // 初始化
|
||||||
|
/// plugin.init(ctx)?;
|
||||||
|
/// plugin.start()?;
|
||||||
|
///
|
||||||
|
/// // 发送命令
|
||||||
|
/// let cmd = Message::DeviceCommand(DeviceCommand::GetDisplayInfo);
|
||||||
|
/// plugin.handle_message(cmd)?;
|
||||||
|
/// ```
|
||||||
|
pub struct DevicePlugin {
|
||||||
|
/// 插件上下文(用于发送消息)
|
||||||
|
ctx: Option<PluginContext>,
|
||||||
|
/// 设备后端实现
|
||||||
|
backend: Box<dyn DeviceBackend>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DevicePlugin {
|
||||||
|
/// 创建新的 DevicePlugin 实例
|
||||||
|
///
|
||||||
|
/// # 参数
|
||||||
|
/// - `backend`: 设备后端实现(如 LinuxArm64Backend)
|
||||||
|
///
|
||||||
|
/// # 示例
|
||||||
|
/// ```ignore
|
||||||
|
/// let backend = Box::new(LinuxArm64Backend::new());
|
||||||
|
/// let plugin = DevicePlugin::new(backend);
|
||||||
|
/// ```
|
||||||
|
pub fn new(backend: Box<dyn DeviceBackend>) -> Self {
|
||||||
|
Self { ctx: None, backend }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Plugin for DevicePlugin {
|
||||||
|
fn id(&self) -> &str {
|
||||||
|
"device"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn info(&self) -> PluginInfo {
|
||||||
|
PluginInfo {
|
||||||
|
name: "Device Manager".to_string(),
|
||||||
|
version: "0.1.0".to_string(),
|
||||||
|
description: format!("统一设备管理插件 ({})", self.backend.name()),
|
||||||
|
platform: Platform::Any,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn capabilities(&self) -> Vec<String> {
|
||||||
|
self.backend
|
||||||
|
.capabilities()
|
||||||
|
.iter()
|
||||||
|
.map(|cap| format!("{:?}", cap))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self, ctx: PluginContext) -> Result<()> {
|
||||||
|
// 初始化后端(传入空配置,未来可从 ctx.config 读取)
|
||||||
|
let config = serde_json::json!({});
|
||||||
|
self.backend.init(&config)?;
|
||||||
|
self.ctx = Some(ctx);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start(&mut self) -> Result<()> {
|
||||||
|
// DevicePlugin 是被动响应式插件,不需要主动启动任务
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_message(&mut self, msg: Message) -> Result<()> {
|
||||||
|
match msg {
|
||||||
|
Message::DeviceCommand(cmd) => {
|
||||||
|
// 处理设备命令
|
||||||
|
let response = match self.backend.handle_command(cmd) {
|
||||||
|
Ok(resp) => resp,
|
||||||
|
Err(e) => {
|
||||||
|
// 将错误转换为 DeviceResponse::Error
|
||||||
|
crate::core::message::DeviceResponse::Error(e.to_string())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 发送响应消息
|
||||||
|
if let Some(ctx) = &self.ctx {
|
||||||
|
let envelope = Envelope {
|
||||||
|
from: self.id().to_string(),
|
||||||
|
to: Destination::Broadcast,
|
||||||
|
message: Message::DeviceResponse(response),
|
||||||
|
};
|
||||||
|
ctx.tx.send(envelope)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::Shutdown => {
|
||||||
|
self.stop()?;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// 忽略其他消息
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop(&mut self) -> Result<()> {
|
||||||
|
self.backend.shutdown()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
pub mod ble;
|
pub mod ble;
|
||||||
|
pub mod device;
|
||||||
pub mod http;
|
pub mod http;
|
||||||
pub mod screen;
|
pub mod screen;
|
||||||
pub mod video;
|
pub mod video;
|
||||||
|
|||||||
Reference in New Issue
Block a user