Skip to content

Commit

Permalink
Merge pull request #740 from andrewdavidmackenzie/pigg_738
Browse files Browse the repository at this point in the history
Merge devices in menu by serial, but allow multiple connection method…
  • Loading branch information
andrewdavidmackenzie authored Dec 16, 2024
2 parents 3dc7fe9 + 5c1ddca commit 3ca817e
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 126 deletions.
95 changes: 59 additions & 36 deletions src/discovery.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
#[cfg(feature = "iroh")]
use crate::discovery::DiscoveryMethod::IrohLocalSwarm;
#[cfg(feature = "tcp")]
use crate::discovery::DiscoveryMethod::Mdns;
#[cfg(feature = "iroh")]
use crate::host_net;
#[cfg(feature = "usb")]
use crate::discovery::DiscoveryMethod::USBRaw;
#[cfg(feature = "tcp")]
use crate::hw_definition::description::TCP_MDNS_SERVICE_TYPE;
use crate::hw_definition::description::{HardwareDetails, SsidSpec};
#[cfg(feature = "usb")]
use crate::usb;
use crate::views::hardware_view::HardwareConnection;
#[cfg(any(feature = "usb", feature = "iroh", feature = "tcp"))]
#[cfg(any(feature = "usb", feature = "tcp"))]
use async_std::prelude::Stream;
#[cfg(any(feature = "usb", feature = "iroh", feature = "tcp"))]
#[cfg(any(feature = "usb", feature = "tcp"))]
use futures::SinkExt;
#[cfg(any(feature = "usb", feature = "iroh", feature = "tcp"))]
#[cfg(any(feature = "usb", feature = "tcp"))]
use iced_futures::stream;
#[cfg(all(feature = "iroh", feature = "tcp"))]
use iroh_net::relay::RelayUrl;
#[cfg(all(feature = "iroh", feature = "tcp"))]
use iroh_net::NodeId;
#[cfg(feature = "tcp")]
use mdns_sd::{ServiceDaemon, ServiceEvent};
#[cfg(any(feature = "iroh", feature = "usb"))]
use std::collections::HashMap;
use std::fmt::{Display, Formatter};
#[cfg(feature = "tcp")]
use std::net::IpAddr;
#[cfg(any(feature = "iroh", feature = "usb"))]
#[cfg(all(feature = "iroh", feature = "tcp"))]
use std::str::FromStr;
#[cfg(feature = "usb")]
use std::time::Duration;
//#[cfg(not(any(feature = "usb", feature = "iroh")))]
//compile_error!("In order for discovery to work you must enable either \"usb\" or \"iroh\" feature");
Expand All @@ -45,11 +52,11 @@ impl Display for DiscoveryMethod {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
#[cfg(feature = "usb")]
DiscoveryMethod::USBRaw => f.write_str("USB"),
USBRaw => f.write_str("USB"),
#[cfg(feature = "iroh")]
DiscoveryMethod::IrohLocalSwarm => f.write_str("Iroh network"),
IrohLocalSwarm => f.write_str("Iroh"),
#[cfg(feature = "tcp")]
DiscoveryMethod::Mdns => f.write_str("TCP"),
Mdns => f.write_str("mDNS"),
#[cfg(not(any(feature = "usb", feature = "iroh", feature = "tcp")))]
DiscoveryMethod::NoDiscovery => f.write_str(""),
}
Expand All @@ -63,7 +70,7 @@ pub struct DiscoveredDevice {
pub discovery_method: DiscoveryMethod,
pub hardware_details: HardwareDetails,
pub ssid_spec: Option<SsidSpec>,
pub hardware_connection: HardwareConnection,
pub hardware_connections: HashMap<String, HardwareConnection>,
}

#[allow(clippy::large_enum_variant)]
Expand All @@ -75,48 +82,41 @@ pub enum DiscoveryEvent {
Error(SerialNumber),
}

#[cfg(any(feature = "iroh", feature = "usb"))]
/// A stream of [DiscoveryEvent] announcing the discovery or loss of devices via USB or Iroh
pub fn iroh_and_usb_discovery() -> impl Stream<Item = DiscoveryEvent> {
#[cfg(feature = "usb")]
/// A stream of [DiscoveryEvent] announcing the discovery or loss of devices via USB
pub fn usb_discovery() -> impl Stream<Item = DiscoveryEvent> {
stream::channel(100, move |mut gui_sender| async move {
#[cfg(feature = "iroh")]
let endpoint = host_net::iroh_host::iroh_endpoint().await.unwrap();

let mut previous_keys: Vec<String> = vec![];
let mut previous_serial_numbers: Vec<String> = vec![];

loop {
let mut current_keys = vec![];
#[allow(unused_mut)]
let mut current_devices = HashMap::new();

#[cfg(feature = "usb")]
current_devices.extend(usb::find_porkys().await);
#[cfg(feature = "iroh")]
current_devices.extend(host_net::iroh_host::find_piglets(&endpoint).await);
let mut current_serial_numbers = vec![];
let current_devices = usb::find_porkys().await;

// New devices
for (serial_number, discovered_device) in current_devices {
let key = format!("{serial_number}/{}", discovered_device.discovery_method);
if !previous_keys.contains(&key) {
if !previous_serial_numbers.contains(&serial_number) {
gui_sender
.send(DiscoveryEvent::DeviceFound(key, discovered_device))
.send(DiscoveryEvent::DeviceFound(
serial_number.clone(),
discovered_device,
))
.await
.unwrap_or_else(|e| eprintln!("Send error: {e}"));
}
current_keys.push(serial_number);
current_serial_numbers.push(serial_number);
}

// Lost devices
for key in previous_keys {
if !current_keys.contains(&key) {
for key in previous_serial_numbers {
if !current_serial_numbers.contains(&key) {
gui_sender
.send(DiscoveryEvent::DeviceLost(key.clone()))
.await
.unwrap_or_else(|e| eprintln!("Send error: {e}"));
}
}

previous_keys = current_keys;
previous_serial_numbers = current_serial_numbers;
tokio::time::sleep(Duration::from_secs(1)).await;
}
})
Expand All @@ -141,9 +141,30 @@ pub fn mdns_discovery() -> impl Stream<Item = DiscoveryEvent> {
let app_version = device_properties
.get_property_val_str("AppVersion")
.unwrap();
#[cfg(feature = "iroh")]
let iroh_nodeid = device_properties.get_property_val_str("IrohNodeID");
#[cfg(feature = "iroh")]
let iroh_relay_url_str = device_properties.get_property_val_str("IrohRelayURL");

if let Some(ip) = info.get_addresses_v4().drain().next() {
let port = info.get_port();
let mut hardware_connections = HashMap::new();
hardware_connections.insert(
"TCP".to_string(),
HardwareConnection::Tcp(IpAddr::V4(*ip), port),
);

#[cfg(feature = "iroh")]
if let Some(nodeid_str) = iroh_nodeid {
let nodeid = NodeId::from_str(nodeid_str).unwrap();
let relay_url =
iroh_relay_url_str.map(|s| RelayUrl::from_str(s).unwrap());
hardware_connections.insert(
"Iroh".to_string(),
HardwareConnection::Iroh(nodeid, relay_url),
);
}

let discovered_device = DiscoveredDevice {
discovery_method: Mdns,
hardware_details: HardwareDetails {
Expand All @@ -156,12 +177,14 @@ pub fn mdns_discovery() -> impl Stream<Item = DiscoveryEvent> {
app_version: app_version.to_string(),
},
ssid_spec: None,
hardware_connection: HardwareConnection::Tcp(IpAddr::V4(*ip), port),
hardware_connections,
};
let key = format!("{serial_number}/TCP");

gui_sender
.send(DiscoveryEvent::DeviceFound(key, discovered_device.clone()))
.send(DiscoveryEvent::DeviceFound(
serial_number.to_owned(),
discovered_device.clone(),
))
.await
.unwrap_or_else(|e| eprintln!("Send error: {e}"));
}
Expand Down
40 changes: 23 additions & 17 deletions src/host_net/iroh_host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,6 @@ use iroh_net::{
};
use std::io;

#[cfg(feature = "discovery")]
use crate::discovery::DiscoveredDevice;
#[cfg(feature = "discovery")]
use crate::discovery::DiscoveryMethod::IrohLocalSwarm;
#[cfg(feature = "discovery")]
use crate::hw;
#[cfg(feature = "discovery")]
use crate::views::hardware_view::HardwareConnection;
#[cfg(feature = "discovery")]
use iroh_net::discovery::local_swarm_discovery::LocalSwarmDiscovery;
#[cfg(feature = "discovery")]
use std::collections::HashMap;

/// Wait until we receive a message from remote hardware
pub async fn wait_for_remote_message(
connection: &mut Connection,
Expand Down Expand Up @@ -101,6 +88,22 @@ pub async fn connect(
Ok((reply.0, reply.1, connection))
}

/*
#[cfg(feature = "discovery")]
use crate::discovery::DiscoveredDevice;
#[cfg(feature = "discovery")]
use crate::discovery::DiscoveryMethod::IrohLocalSwarm;
#[cfg(feature = "discovery")]
use crate::hw;
#[cfg(feature = "discovery")]
use crate::views::hardware_view::HardwareConnection;
#[cfg(feature = "discovery")]
use iroh_net::discovery::local_swarm_discovery::LocalSwarmDiscovery;
#[cfg(feature = "discovery")]
use std::collections::HashMap;
use std::collections::HashSet;
#[cfg(feature = "discovery")]
/// Create an iroh-net [Endpoint] for use in discovery
pub async fn iroh_endpoint() -> anyhow::Result<Endpoint> {
Expand Down Expand Up @@ -128,19 +131,22 @@ pub async fn find_piglets(endpoint: &Endpoint) -> HashMap<String, DiscoveredDevi
.chars()
.take(16)
.collect::<String>();
let mut hardware_connections = HashSet::new();
hardware_connections.insert(HardwareConnection::Iroh(
remote.node_id,
remote.relay_url.map(|ri| ri.relay_url),
));
map.insert(
trunc, // TODO substitute for real serial_number when Iroh discovery supports it
DiscoveredDevice {
discovery_method: IrohLocalSwarm,
hardware_details: hw::driver::get().description().unwrap().details, // TODO show the real hardware description when Iroh discovery supports it
ssid_spec: None,
hardware_connection: HardwareConnection::Iroh(
remote.node_id,
remote.relay_url.map(|ri| ri.relay_url),
),
hardware_connections,
},
);
}
map
}
*/
27 changes: 16 additions & 11 deletions src/piggui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,8 +392,8 @@ impl Piggui {
self.hardware_view.subscription().map(Hardware),
];

#[cfg(all(feature = "discovery", any(feature = "iroh", feature = "usb")))]
subscriptions.push(Subscription::run(discovery::iroh_and_usb_discovery).map(Discovery));
#[cfg(all(feature = "discovery", feature = "usb"))]
subscriptions.push(Subscription::run(discovery::usb_discovery).map(Discovery));

#[cfg(all(feature = "discovery", feature = "tcp"))]
subscriptions.push(Subscription::run(discovery::mdns_discovery).map(Discovery));
Expand All @@ -412,16 +412,21 @@ impl Piggui {
/// Process [DiscoveryEvent] messages related to discovery/loss of devices
fn discovery_event(&mut self, event: DiscoveryEvent) {
match event {
DiscoveryEvent::DeviceFound(key, discovered_device) => {
DiscoveryEvent::DeviceFound(serial_number, discovered_device) => {
let method = discovered_device.discovery_method.clone();
if self
.discovered_devices
.insert(key, discovered_device)
.is_none()
{
println!("Device Found {method}");
self.info_row
.add_info_message(Info(format!("Device Found on {method}")));
println!("Device Found {method}");
self.info_row
.add_info_message(Info(format!("Device Found by {method}")));
// if the device is already in the list of discovered devices, make sure this method
// exists in the set of methods that can be used to connect to it
if let Some(known_device) = self.discovered_devices.get_mut(&serial_number) {
known_device
.hardware_connections
.extend(discovered_device.hardware_connections);
} else {
// new device, add to the map
self.discovered_devices
.insert(serial_number, discovered_device);
}
}
DiscoveryEvent::DeviceLost(key) => {
Expand Down
39 changes: 27 additions & 12 deletions src/piglet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,12 +145,21 @@ async fn run_service(
// Then listen for remote connections and "serve" them
#[cfg(all(feature = "tcp", not(feature = "iroh")))]
if let Some(mut listener) = listener_info.tcp_info.listener {
#[cfg(feature = "discovery")]
// The key string in TXT properties is case-insensitive.
let properties = [
("Serial", &desc.details.serial as &str),
("Model", &desc.details.model as &str),
("AppName", env!("CARGO_BIN_NAME")),
("AppVersion", env!("CARGO_PKG_VERSION")),
];

#[cfg(feature = "discovery")]
let (service_info, service_daemon) = register_mdns(
TCP_MDNS_SERVICE_TYPE,
listener_info.tcp_info.port,
&desc.details.serial,
&desc.details.model,
&properties,
)?;

loop {
Expand Down Expand Up @@ -191,12 +200,26 @@ async fn run_service(
listener_info.tcp_info.listener,
listener_info.iroh_info.endpoint,
) {
#[cfg(feature = "discovery")]
// The key string in TXT properties is case-insensitive.
let properties = [
("Serial", &desc.details.serial as &str),
("Model", &desc.details.model as &str),
("AppName", env!("CARGO_BIN_NAME")),
("AppVersion", env!("CARGO_PKG_VERSION")),
("IrohNodeID", &listener_info.iroh_info.nodeid.to_string()),
(
"IrohRelayURL",
&listener_info.iroh_info.relay_url.to_string(),
),
];

#[cfg(feature = "discovery")]
let (service_info, service_daemon) = register_mdns(
TCP_MDNS_SERVICE_TYPE,
listener_info.tcp_info.port,
&desc.details.serial,
&desc.details.model,
&properties,
)?;

loop {
Expand Down Expand Up @@ -410,29 +433,21 @@ fn register_mdns(
service_type: &str,
port: u16,
serial_number: &str,
model_name: &str,
properties: &[(&str, &str)],
) -> anyhow::Result<(ServiceInfo, ServiceDaemon)> {
let service_daemon = ServiceDaemon::new().context("Could not create service daemon")?;

let hostname = "host1".to_string(); // TODO what to put here?
let service_hostname = format!("{}.local.", hostname);

// The key string in TXT properties is case-insensitive.
let properties = [
("Serial", serial_number),
("Model", model_name),
("AppName", env!("CARGO_BIN_NAME")),
("AppVersion", env!("CARGO_PKG_VERSION")),
];

// Register a service.
let service_info = ServiceInfo::new(
service_type,
serial_number,
&service_hostname,
"",
port,
&properties[..],
properties,
)
.context("Could not create mDNS ServiceInfo")?
.enable_addr_auto();
Expand Down
4 changes: 3 additions & 1 deletion src/usb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,15 @@ pub async fn find_porkys() -> HashMap<String, DiscoveredDevice> {
Some(tcp) => HardwareConnection::Tcp(IpAddr::from(tcp.0), tcp.1),
_ => HardwareConnection::NoConnection,
};
let mut hardware_connections = HashMap::new();
hardware_connections.insert(connection.name(), connection);
map.insert(
hardware_details.serial.clone(),
DiscoveredDevice {
discovery_method: USBRaw,
hardware_details,
ssid_spec: ssid,
hardware_connection: connection,
hardware_connections,
},
);
}
Expand Down
Loading

0 comments on commit 3ca817e

Please sign in to comment.