//! cam-core —— 桌面端的非界面部分。 //! //! 这一层不认识 Tauri,也不认识任何界面:它只负责 //! //! - [`receiver`]:收 UDP、发现摄像头、按发送方重组 JPEG 帧; //! - [`armor`]:收装甲板主板广播的血量,并列出场上有哪几块板子; //! - [`stats`]:帧率统计(按帧数除以墙钟,不是对帧间隔取平均); //! - [`config`]:磁盘上的配置(身份、绑定、HUD、密码哈希); //! - [`password`]:密码的哈希与校验。 //! //! # 为什么单独一层 //! //! 旧的 Slint 客户端把这几件事和界面耦合在一个 crate 里,于是**它们只能通过跑起 //! 整个界面来验证**。抽出来之后,重组、发现、帧率窗口、血量新鲜度都能用单元测试 //! 直接盯住——而它们恰好是最容易错、错了又最难在现场看出来的部分。 //! //! # 界面怎么拿数据 //! //! 两条路,都是**推**而不是轮询: //! //! - 视频帧走 [`receiver::Receiver::set_frame_sink`]:接收线程重组好一张 JPEG 就 //! 交给 sink,由上层转成 WebSocket 的二进制消息。sink 必须是非阻塞的。 //! - 血量走 [`armor::ArmorHealthStore::set_sink`]:每收到一条上报就交给 sink。 //! //! 同时两边的**最新值**都留在内存里([`receiver::LatestFrames`] 与 //! [`armor::ArmorHealthStore::boards`]),给"截图"和"设置界面里的下拉列表"用—— //! 那两处要的是当前状态,不是每一帧。 pub mod armor; pub mod config; pub mod password; pub mod receiver; pub mod slots; pub mod snapshot; pub mod stats; pub use config::{Config, HudConfig, Role}; pub use receiver::{ ArmorSource, CameraInfo, FrameSink, LatestFrames, NoArmorYet, Receiver, SourceStats, ARMOR_BOARDS, }; pub use slots::SlotAllocator; pub use stats::FpsWindow;