pub struct DeviceDiscovery { /* private fields */ }Expand description
Discovers Enpose tracker devices on the local network.
On every call to Self::discover the API sends discovery
requests to every directed-broadcast address of every up,
non-loopback IPv4 interface on the host (e.g. 192.168.10.255 on
a host with enp1s0 192.168.10.10/24), plus the limited-broadcast
address 255.255.255.255. This reaches the cluster network on
multi-NIC hosts where the kernel’s default route would otherwise
send the limited broadcast out the wrong interface (typically the
wifi default route, leaving the cluster ethernet unreachable).
A single instance can be reused for many discovery rounds; the API holds no persistent network state between calls.
Implementations§
Source§impl DeviceDiscovery
impl DeviceDiscovery
Sourcepub fn new() -> Self
pub fn new() -> Self
Create a new DeviceDiscovery that, on each Self::discover
call, broadcasts to every directed-broadcast on the host plus
255.255.255.255 — all on BROADCAST_PORT.
Examples found in repository?
20fn main() -> std::io::Result<()> {
21 let devices = DeviceDiscovery::new().discover()?;
22
23 if devices.is_empty() {
24 println!("No Enpose devices found on the local network.");
25 return Ok(());
26 }
27
28 println!("Found {} device(s):", devices.len());
29 for device in &devices {
30 let status = if device.compatible {
31 "compatible"
32 } else {
33 "INCOMPATIBLE (protocol version mismatch)"
34 };
35 println!(" - {} (serial {}): {}", device.ip, device.serial, status);
36 }
37
38 // Pick the first compatible device to stream from.
39 let Some(device) = devices.iter().find(|d| d.compatible) else {
40 println!("No compatible device to stream poses from.");
41 return Ok(());
42 };
43
44 println!("\nStreaming poses from {} for {:?}...", device.ip, STREAM_DURATION);
45
46 // Threaded mode: a background thread receives and buffers poses, so none
47 // are missed regardless of how often we poll below.
48 let mut stream = PoseStream::from_device(device, true)?;
49
50 let start = Instant::now();
51 while start.elapsed() < STREAM_DURATION {
52 // Blocking receive: waits up to 3 s for a pose update, so no manual
53 // polling delay is needed.
54 for pose in stream.receive_pose_updates(true)? {
55 println!(
56 " t={:>10}us marker {:>3}: pos=({:+.4}, {:+.4}, {:+.4}) rmse={:.4} sensors={}",
57 pose.timestamp,
58 pose.marker_id,
59 pose.x,
60 pose.y,
61 pose.z,
62 pose.position_rmse,
63 pose.sensors,
64 );
65 }
66 }
67
68 // `stream` is dropped here, which disconnects from the device.
69 Ok(())
70}Sourcepub fn discover(&self) -> Result<Vec<DeviceInfo>>
pub fn discover(&self) -> Result<Vec<DeviceInfo>>
Broadcast discovery requests and collect replies.
Sends the discovery request to BROADCAST_PORT up to three times,
150 ms apart, and listens on the same socket for
PKT_TYPE_PEER_INFO replies. Resending guards against a dropped
request or reply on a lossy segment. Only the elected primary of a
cluster replies, so a cluster contributes one entry; multiple clusters
on the same L2 segment each contribute one. Replies are de-duplicated
by serial.
Timing: returns 50 ms after the last reply (whichever cluster replies last), or — if nothing replies — only at the hard 500 ms cap, having retransmitted the request in the meantime.
§Errors
Returns an io::Error only for unrecoverable socket failures
(bind, send, or unexpected recv errors). A normal “no devices
found” outcome returns Ok(vec![]).
Examples found in repository?
20fn main() -> std::io::Result<()> {
21 let devices = DeviceDiscovery::new().discover()?;
22
23 if devices.is_empty() {
24 println!("No Enpose devices found on the local network.");
25 return Ok(());
26 }
27
28 println!("Found {} device(s):", devices.len());
29 for device in &devices {
30 let status = if device.compatible {
31 "compatible"
32 } else {
33 "INCOMPATIBLE (protocol version mismatch)"
34 };
35 println!(" - {} (serial {}): {}", device.ip, device.serial, status);
36 }
37
38 // Pick the first compatible device to stream from.
39 let Some(device) = devices.iter().find(|d| d.compatible) else {
40 println!("No compatible device to stream poses from.");
41 return Ok(());
42 };
43
44 println!("\nStreaming poses from {} for {:?}...", device.ip, STREAM_DURATION);
45
46 // Threaded mode: a background thread receives and buffers poses, so none
47 // are missed regardless of how often we poll below.
48 let mut stream = PoseStream::from_device(device, true)?;
49
50 let start = Instant::now();
51 while start.elapsed() < STREAM_DURATION {
52 // Blocking receive: waits up to 3 s for a pose update, so no manual
53 // polling delay is needed.
54 for pose in stream.receive_pose_updates(true)? {
55 println!(
56 " t={:>10}us marker {:>3}: pos=({:+.4}, {:+.4}, {:+.4}) rmse={:.4} sensors={}",
57 pose.timestamp,
58 pose.marker_id,
59 pose.x,
60 pose.y,
61 pose.z,
62 pose.position_rmse,
63 pose.sensors,
64 );
65 }
66 }
67
68 // `stream` is dropped here, which disconnects from the device.
69 Ok(())
70}