1use 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
14pub struct PiperBuilder {
48 interface: Option<String>,
49 baud_rate: Option<u32>,
50 timeout: Option<Duration>,
51 daemon_addr: Option<String>,
52}
53
54fn parse_firmware_version(version_str: &str) -> Option<Version> {
66 let version_str = version_str.trim();
68
69 let version_part = version_str.strip_prefix("S-V")?;
71
72 let normalized = version_part.replace('-', ".");
74
75 Version::parse(&normalized).ok()
77}
78
79impl PiperBuilder {
80 pub fn new() -> Self {
82 Self::default()
83 }
84
85 pub fn interface(mut self, interface: impl Into<String>) -> Self {
91 self.interface = Some(interface.into());
92 self
93 }
94
95 pub fn baud_rate(mut self, baud_rate: u32) -> Self {
97 self.baud_rate = Some(baud_rate);
98 self
99 }
100
101 pub fn timeout(mut self, timeout: Duration) -> Self {
103 self.timeout = Some(timeout);
104 self
105 }
106
107 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 pub fn build(self) -> Result<Piper<Standby>> {
132 debug!("Building Piper client connection");
133
134 let mut driver_builder = DriverBuilder::new();
136
137 if let Some(ref interface) = self.interface {
139 debug!("Configuring interface: {}", interface);
140 driver_builder = driver_builder.interface(interface);
141 } else {
142 #[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 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 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 let driver = Arc::new(driver_builder.build()?);
169 debug!("Driver connection established");
170
171 let timeout = self.timeout.unwrap_or(Duration::from_secs(5));
173 debug!("Waiting for robot feedback (timeout: {:?})", timeout);
174 driver.wait_for_feedback(timeout)?;
176 info!("Robot ready - Standby mode");
177
178 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 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)) },
202 }
203 } else {
204 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 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 }
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}