Skip to main content

viva_u3v/
discovery.rs

1//! USB3 Vision device enumeration.
2//!
3//! Scans all connected USB devices for USB3 Vision interfaces and returns
4//! descriptive information for each discovered camera.
5
6#[cfg(feature = "usb")]
7use crate::U3vError;
8use crate::descriptor::U3vInterfaceInfo;
9
10/// Information about a discovered USB3 Vision device.
11#[derive(Debug, Clone)]
12pub struct U3vDeviceInfo {
13    /// USB bus number.
14    pub bus: u8,
15    /// USB device address on the bus.
16    pub address: u8,
17    /// USB vendor ID.
18    pub vendor_id: u16,
19    /// USB product ID.
20    pub product_id: u16,
21    /// Device serial number (from USB string descriptor, if available).
22    pub serial: Option<String>,
23    /// Manufacturer name (from USB string descriptor, if available).
24    pub manufacturer: Option<String>,
25    /// Product/model name (from USB string descriptor, if available).
26    pub model: Option<String>,
27    /// Parsed U3V interface and endpoint information.
28    pub interface_info: U3vInterfaceInfo,
29}
30
31impl std::fmt::Display for U3vDeviceInfo {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        write!(
34            f,
35            "USB3V {:04x}:{:04x} bus={} addr={} {}",
36            self.vendor_id,
37            self.product_id,
38            self.bus,
39            self.address,
40            self.model.as_deref().unwrap_or("(unknown)"),
41        )
42    }
43}
44
45/// Enumerate all USB3 Vision devices currently connected to the system.
46///
47/// Returns an empty list if no U3V cameras are found. Devices that cannot
48/// be opened or whose descriptors fail to parse are silently skipped.
49#[cfg(feature = "usb")]
50pub fn discover() -> Result<Vec<U3vDeviceInfo>, U3vError> {
51    use crate::descriptor::{is_likely_u3v_device, parse_u3v_interfaces};
52    use rusb::UsbContext;
53
54    let context = rusb::Context::new().map_err(|e| U3vError::Usb(e.to_string()))?;
55    let devices = context
56        .devices()
57        .map_err(|e| U3vError::Usb(e.to_string()))?;
58
59    let mut found = Vec::new();
60
61    for device in devices.iter() {
62        let desc = match device.device_descriptor() {
63            Ok(d) => d,
64            Err(_) => continue,
65        };
66
67        if !is_likely_u3v_device(&desc) {
68            continue;
69        }
70
71        let config = match device.active_config_descriptor() {
72            Ok(c) => c,
73            Err(_) => continue,
74        };
75
76        let interface_info = match parse_u3v_interfaces(&config) {
77            Some(info) => info,
78            None => continue,
79        };
80
81        // Try to read string descriptors (may fail if device is busy).
82        let (serial, manufacturer, model) = match device.open() {
83            Ok(handle) => {
84                let timeout = std::time::Duration::from_millis(500);
85                let serial = desc
86                    .serial_number_string_index()
87                    .and_then(|i| handle.read_string_descriptor_ascii(i).ok());
88                let manufacturer = desc
89                    .manufacturer_string_index()
90                    .and_then(|i| handle.read_string_descriptor_ascii(i).ok());
91                let model = desc
92                    .product_string_index()
93                    .and_then(|i| handle.read_string_descriptor_ascii(i).ok());
94                let _ = timeout;
95                (serial, manufacturer, model)
96            }
97            Err(_) => (None, None, None),
98        };
99
100        found.push(U3vDeviceInfo {
101            bus: device.bus_number(),
102            address: device.address(),
103            vendor_id: desc.vendor_id(),
104            product_id: desc.product_id(),
105            serial,
106            manufacturer,
107            model,
108            interface_info,
109        });
110    }
111
112    Ok(found)
113}