1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use std::collections::HashSet;
use std::sync::Arc;
use std::time::Duration;
use tokio::time::sleep;
use xlink::channels::memory::MemoryChannel;
use xlink::core::types::{
ChannelState, ChannelType, DeviceCapabilities, DeviceId, DeviceType, MessagePayload,
};
use xlink::XLink;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
// 1. Setup Device A (Alice)
let alice_id = DeviceId::new();
let alice_caps = DeviceCapabilities {
device_id: alice_id,
device_type: DeviceType::Smartphone,
device_name: "Alice Phone".to_string(),
supported_channels: HashSet::from([ChannelType::Lan]),
battery_level: Some(80),
is_charging: false,
data_cost_sensitive: false,
};
// 2. Setup Device B (Bob)
let bob_id = DeviceId::new();
// Bob is just a target ID for this demo, we don't spin up a full SDK for him
// in this single process unless we mock the network fully.
// For simplicity, we assume the MemoryChannel "delivers" it.
// 3. Create SDK for Alice
// We need a channel. For the demo, we create a MemoryChannel.
// The handler is created inside SDK usually, but for circular dep in construction
// we often use a factory or lazy init. Here we do a slight workaround for the demo.
// Placeholder handler for construction
let (_tx, _rx) = tokio::sync::mpsc::channel::<()>(1);
// Construct Alice's SDK
// We need to construct the channel first, but the channel needs a handler.
// In a real app, the Channel impl might have an internal mpsc, and the SDK polls it.
// Here we use the SDK's handler logic.
// Let's create the SDK with an empty channel list first, then inject (if mutable)
// or just pass a channel that we wire up.
// To keep it simple and runnable:
// We will use a channel that just logs for now, as we can't easily wire the handler back
// into the constructor without more complex Arc<Mutex<Option<...>>> logic.
struct DemoHandler;
#[async_trait::async_trait]
impl xlink::core::traits::MessageHandler for DemoHandler {
async fn handle_message(
&self,
_msg: xlink::core::types::Message,
) -> xlink::core::error::Result<()> {
Ok(())
}
}
let mem_channel = Arc::new(MemoryChannel::new(Arc::new(DemoHandler), 50)); // 50ms latency
let sdk = XLink::new(alice_caps, vec![mem_channel.clone()]).await?;
sdk.start().await?;
// 4. Register Bob's capabilities in Alice's manager so routing works
// In real life, this happens via Discovery
let bob_caps = DeviceCapabilities {
device_id: bob_id,
device_type: DeviceType::Laptop,
device_name: "Bob Laptop".to_string(),
supported_channels: HashSet::from([ChannelType::Lan]),
battery_level: Some(100),
is_charging: true,
data_cost_sensitive: false,
};
sdk.capability_manager().register_remote_device(bob_caps);
// Update channel state for Bob (Simulate discovery found a good link)
sdk.capability_manager().update_channel_state(
bob_id,
ChannelType::Lan,
ChannelState {
available: true,
rtt_ms: 20, // Fast
jitter_ms: 5,
packet_loss_rate: 0.0,
bandwidth_bps: 1024 * 1024,
signal_strength: Some(-40),
network_type: xlink::core::types::NetworkType::WiFi,
failure_count: 0,
last_heartbeat: std::time::SystemTime::now()
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs(),
distance_meters: Some(10.0), // 10 meters away
},
);
log::info!("--- System Initialized ---");
log::info!("Alice: {}", alice_id);
log::info!("Bob: {}", bob_id);
// 5. Send Message
log::info!("Sending 'Hello Bob'...");
sdk.send(bob_id, MessagePayload::Text("Hello Bob!".to_string()))
.await?;
// Wait for async operations
sleep(Duration::from_millis(200)).await;
// 6. Simulate High Priority Message
log::info!("Sending Critical Alert...");
// Note: In a full impl, we'd set priority on the message builder.
// The current simple API defaults to Normal.
// Let's assume we added a builder or method for it.
sdk.send(bob_id, MessagePayload::Text("FIRE ALARM!".to_string()))
.await?;
sleep(Duration::from_millis(200)).await;
log::info!("Demo completed successfully.");
Ok(())
}