Skip to main content

piper_client/
builder.rs

1//! Client 层 Piper Builder
2//!
3//! 提供链式 API 创建 `Piper<Standby>` 实例,自动处理平台差异和适配器选择。
4
5use crate::observer::Observer;
6use crate::state::*;
7use crate::types::{DeviceQuirks, Result};
8use piper_driver::PiperBuilder as DriverBuilder;
9use semver::Version;
10use std::sync::Arc;
11use std::time::Duration;
12use tracing::{debug, info, warn};
13
14/// Client 层 Piper Builder
15///
16/// 提供链式 API 创建 `Piper<Standby>` 实例,自动处理平台差异和适配器选择。
17///
18/// # 示例
19///
20/// ```rust,no_run
21/// use piper_client::PiperBuilder;
22/// use std::time::Duration;
23///
24/// # fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
25/// // 使用默认配置(推荐)
26/// let robot = PiperBuilder::new().build()?;
27///
28/// // 指定接口
29/// let robot = PiperBuilder::new()
30///     .interface("can0")
31///     .build()?;
32///
33/// // 完整配置
34/// let robot = PiperBuilder::new()
35///     .interface("can0")
36///     .baud_rate(1_000_000)
37///     .timeout(Duration::from_secs(5))
38///     .build()?;
39///
40/// // 使用守护进程
41/// let robot = PiperBuilder::new()
42///     .with_daemon("/tmp/gs_usb_daemon.sock")
43///     .build()?;
44/// # Ok(())
45/// # }
46/// ```
47pub struct PiperBuilder {
48    interface: Option<String>,
49    baud_rate: Option<u32>,
50    timeout: Option<Duration>,
51    daemon_addr: Option<String>,
52}
53
54/// 解析固件版本字符串
55///
56/// 将 "S-V1.6-3" 格式的字符串解析为 semver::Version
57///
58/// # 参数
59///
60/// - `version_str`: 固件版本字符串(例如 "S-V1.6-3")
61///
62/// # 返回
63///
64/// 解析后的 semver::Version(例如 "1.6.3")
65fn parse_firmware_version(version_str: &str) -> Option<Version> {
66    // 格式: "S-V1.6-3" -> "1.6.3"
67    let version_str = version_str.trim();
68
69    // 移除 "S-V" 前缀
70    let version_part = version_str.strip_prefix("S-V")?;
71
72    // 替换连字符为点号: "1.6-3" -> "1.6.3"
73    let normalized = version_part.replace('-', ".");
74
75    // 解析为 semver::Version
76    Version::parse(&normalized).ok()
77}
78
79impl PiperBuilder {
80    /// 创建新的 Builder
81    pub fn new() -> Self {
82        Self::default()
83    }
84
85    /// 设置 CAN 接口名称或设备序列号
86    ///
87    /// - Linux: "can0"/"can1" 等 SocketCAN 接口名,或设备序列号(使用 GS-USB)
88    /// - macOS/Windows: GS-USB 设备序列号
89    /// - 如果为 `None`,使用平台默认值(Linux: "can0", 其他: 自动选择)
90    pub fn interface(mut self, interface: impl Into<String>) -> Self {
91        self.interface = Some(interface.into());
92        self
93    }
94
95    /// 设置 CAN 波特率(默认: 1_000_000)
96    pub fn baud_rate(mut self, baud_rate: u32) -> Self {
97        self.baud_rate = Some(baud_rate);
98        self
99    }
100
101    /// 设置连接超时(默认: 5 秒)
102    pub fn timeout(mut self, timeout: Duration) -> Self {
103        self.timeout = Some(timeout);
104        self
105    }
106
107    /// 使用守护进程模式
108    ///
109    /// 当启用守护进程模式时,`interface` 参数会被忽略(守护进程模式优先级最高)。
110    ///
111    /// # 参数
112    ///
113    /// - `daemon_addr`: 守护进程地址
114    ///   - UDS 路径(如 "/tmp/gs_usb_daemon.sock")
115    ///   - UDP 地址(如 "127.0.0.1:8888")
116    ///
117    /// # 注意
118    ///
119    /// 守护进程模式会忽略 `interface` 参数,因为设备选择由守护进程管理。
120    pub fn with_daemon(mut self, daemon_addr: impl Into<String>) -> Self {
121        self.daemon_addr = Some(daemon_addr.into());
122        self
123    }
124
125    /// 构建 `Piper<Standby>` 实例
126    ///
127    /// # 注意
128    ///
129    /// - 当启用 Daemon 模式时,`interface` 参数会被忽略(Daemon 模式优先级最高)
130    /// - Interface 为 `None` 时,Linux 平台默认使用 "can0",其他平台自动选择第一个 GS-USB 设备
131    pub fn build(self) -> Result<Piper<Standby>> {
132        debug!("Building Piper client connection");
133
134        // 构造 Driver Builder
135        let mut driver_builder = DriverBuilder::new();
136
137        // 处理 interface:保持 Option<String> 语义
138        if let Some(ref interface) = self.interface {
139            debug!("Configuring interface: {}", interface);
140            driver_builder = driver_builder.interface(interface);
141        } else {
142            // 使用平台默认值
143            #[cfg(target_os = "linux")]
144            {
145                debug!("Using default Linux interface: can0");
146                driver_builder = driver_builder.interface("can0");
147            }
148            #[cfg(not(target_os = "linux"))]
149            {
150                debug!("Auto-selecting first available GS-USB device");
151            }
152        }
153
154        // 设置波特率(如果有)
155        if let Some(baud) = self.baud_rate {
156            debug!("Configuring baud rate: {} bps", baud);
157            driver_builder = driver_builder.baud_rate(baud);
158        }
159
160        // 设置守护进程(如果有,优先级最高)
161        if let Some(ref daemon) = self.daemon_addr {
162            info!("Using daemon mode: {}", daemon);
163            driver_builder = driver_builder.with_daemon(daemon);
164        }
165
166        // 构建 Driver 实例
167        // 注意:DriverError 通过 #[from] 自动转换为 RobotError::Infrastructure
168        let driver = Arc::new(driver_builder.build()?);
169        debug!("Driver connection established");
170
171        // 等待反馈
172        let timeout = self.timeout.unwrap_or(Duration::from_secs(5));
173        debug!("Waiting for robot feedback (timeout: {:?})", timeout);
174        // 注意:DriverError 通过 #[from] 自动转换为 RobotError::Infrastructure
175        driver.wait_for_feedback(timeout)?;
176        info!("Robot ready - Standby mode");
177
178        // 查询固件版本(用于 DeviceQuirks)
179        info!("Querying firmware version for DeviceQuirks initialization");
180        if let Err(e) = driver.query_firmware_version() {
181            warn!(
182                "Failed to query firmware version: {:?}. Using default quirks (latest firmware behavior).",
183                e
184            );
185        }
186
187        // 获取固件版本并创建 DeviceQuirks
188        let quirks = if let Some(version_str) = driver.get_firmware_version() {
189            debug!("Raw firmware version string: {}", version_str);
190            match parse_firmware_version(&version_str) {
191                Some(version) => {
192                    info!("Parsed firmware version: {}", version);
193                    DeviceQuirks::from_firmware_version(version)
194                },
195                None => {
196                    warn!(
197                        "Failed to parse firmware version '{}'. Using default quirks (latest firmware behavior).",
198                        version_str
199                    );
200                    DeviceQuirks::from_firmware_version(Version::new(1, 9, 0)) // 默认使用最新版本行为
201                },
202            }
203        } else {
204            // 如果查询失败,使用最新版本行为(所有 quirks 都为 false/1.0)
205            info!(
206                "No firmware version available. Using default quirks (latest firmware behavior)."
207            );
208            DeviceQuirks::from_firmware_version(Version::new(1, 9, 0))
209        };
210
211        // 创建 Observer
212        let observer = Observer::new(driver.clone());
213
214        Ok(Piper {
215            driver,
216            observer,
217            quirks,
218            _state: machine::Standby,
219        })
220    }
221}
222
223impl Default for PiperBuilder {
224    fn default() -> Self {
225        Self {
226            interface: {
227                #[cfg(target_os = "linux")]
228                {
229                    Some("can0".to_string())
230                }
231                #[cfg(not(target_os = "linux"))]
232                {
233                    None // macOS/Windows: 自动选择第一个 GS-USB 设备
234                }
235            },
236            baud_rate: Some(1_000_000),
237            timeout: Some(Duration::from_secs(5)),
238            daemon_addr: None,
239        }
240    }
241}
242
243#[cfg(test)]
244mod tests {
245    use super::*;
246
247    #[test]
248    fn test_piper_builder_new() {
249        let builder = PiperBuilder::new();
250        assert!(builder.interface.is_some() || builder.interface.is_none());
251        assert_eq!(builder.baud_rate, Some(1_000_000));
252        assert_eq!(builder.timeout, Some(Duration::from_secs(5)));
253        assert_eq!(builder.daemon_addr, None);
254    }
255
256    #[test]
257    fn test_piper_builder_chain() {
258        let builder = PiperBuilder::new().interface("can0").baud_rate(500_000);
259
260        assert_eq!(builder.interface, Some("can0".to_string()));
261        assert_eq!(builder.baud_rate, Some(500_000));
262    }
263
264    #[test]
265    fn test_piper_builder_default() {
266        let builder = PiperBuilder::default();
267        assert_eq!(builder.baud_rate, Some(1_000_000));
268        assert_eq!(builder.timeout, Some(Duration::from_secs(5)));
269    }
270
271    #[test]
272    fn test_piper_builder_with_daemon() {
273        let builder = PiperBuilder::new().with_daemon("/tmp/gs_usb_daemon.sock");
274        assert_eq!(
275            builder.daemon_addr,
276            Some("/tmp/gs_usb_daemon.sock".to_string())
277        );
278    }
279}