#![cfg(feature = "shared_radio")]
#![cfg_attr(docsrs, doc(cfg(feature = "shared_radio")))]
use crate::Result;
use crate::{Ack, Channel, Crazyradio};
use flume::{bounded, unbounded, Receiver, Sender, WeakSender};
pub struct SharedCrazyradio {
radio_command: Sender<RadioCommand>,
send_packet_res_send: Sender<Result<SendPacketResult>>,
send_packet_res: Receiver<Result<SendPacketResult>>,
send_packet_no_ack_res_send: Sender<Result<()>>,
send_packet_no_ack_res: Receiver<Result<()>>,
scan_res_send: Sender<Result<ScanResult>>,
scan_res: Receiver<Result<ScanResult>>,
}
impl SharedCrazyradio {
pub fn new(radio: Crazyradio) -> Self {
let (radio_command, radio_command_recv) = unbounded();
std::thread::spawn(move || {
radio_loop(radio, radio_command_recv);
});
let (send_packet_res_send, send_packet_res) = bounded(1);
let (send_packet_no_ack_res_send, send_packet_no_ack_res) = bounded(1);
let (scan_res_send, scan_res) = bounded(1);
SharedCrazyradio {
radio_command,
send_packet_res_send,
send_packet_res,
send_packet_no_ack_res_send,
send_packet_no_ack_res,
scan_res_send,
scan_res,
}
}
pub fn scan(
&self,
start: Channel,
stop: Channel,
address: [u8; 5],
payload: Vec<u8>,
) -> Result<Vec<Channel>> {
self.radio_command
.send(RadioCommand::Scan {
client: self.scan_res_send.clone(),
start,
stop,
address,
payload,
})
.unwrap();
let result = self.scan_res.recv().unwrap()?;
Ok(result.found)
}
pub fn send_packet(
&mut self,
channel: Channel,
address: [u8; 5],
payload: Vec<u8>,
) -> Result<(Ack, Vec<u8>)> {
self.radio_command
.send(RadioCommand::SendPacket {
client: self.send_packet_res_send.clone(),
channel,
address,
payload,
})
.unwrap();
let result = self.send_packet_res.recv().unwrap()?;
Ok((
Ack {
received: result.acked,
length: result.payload.len(),
power_detector: result.power_detector,
retry: result.retry,
rssi_dbm: result.rssi_dbm,
},
result.payload,
))
}
pub fn send_packet_no_ack(
&mut self,
channel: Channel,
address: [u8; 5],
payload: Vec<u8>,
) -> Result<()> {
self.radio_command
.send(RadioCommand::SendPacketNoAck {
client: self.send_packet_no_ack_res_send.clone(),
channel,
address,
payload,
})
.unwrap();
Ok(())
}
pub fn downgrade(&self) -> WeakSharedCrazyradio {
WeakSharedCrazyradio {
radio_command: Some(self.radio_command.downgrade()),
}
}
}
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
impl SharedCrazyradio {
pub async fn scan_async(
&mut self,
start: Channel,
stop: Channel,
address: [u8; 5],
payload: Vec<u8>,
) -> Result<Vec<Channel>> {
self.radio_command
.send_async(RadioCommand::Scan {
client: self.scan_res_send.clone(),
start,
stop,
address,
payload,
})
.await
.unwrap();
let result = self.scan_res.recv_async().await.unwrap()?;
Ok(result.found)
}
pub async fn send_packet_async(
&mut self,
channel: Channel,
address: [u8; 5],
payload: Vec<u8>,
) -> Result<(Ack, Vec<u8>)> {
self.radio_command
.send_async(RadioCommand::SendPacket {
client: self.send_packet_res_send.clone(),
channel,
address,
payload,
})
.await
.unwrap();
let result = self.send_packet_res.recv_async().await.unwrap()?;
Ok((
Ack {
received: result.acked,
length: result.payload.len(),
power_detector: result.power_detector,
retry: result.retry,
rssi_dbm: result.rssi_dbm,
},
result.payload,
))
}
pub async fn send_packet_no_ack_async(
&mut self,
channel: Channel,
address: [u8; 5],
payload: Vec<u8>,
) -> Result<()> {
self.radio_command
.send_async(RadioCommand::SendPacketNoAck {
client: self.send_packet_no_ack_res_send.clone(),
channel,
address,
payload,
})
.await
.unwrap();
self.send_packet_no_ack_res.recv_async().await.unwrap()?;
Ok(())
}
}
impl Clone for SharedCrazyradio {
fn clone(&self) -> Self {
let (send_packet_res_send, send_packet_res) = bounded(1);
let (send_packet_no_ack_res_send, send_packet_no_ack_res) = bounded(1);
let (scan_res_send, scan_res) = bounded(1);
let radio_command = self.radio_command.clone();
SharedCrazyradio {
radio_command,
send_packet_res_send,
send_packet_res,
send_packet_no_ack_res_send,
send_packet_no_ack_res,
scan_res_send,
scan_res,
}
}
}
pub struct WeakSharedCrazyradio {
radio_command: Option<WeakSender<RadioCommand>>,
}
impl Default for WeakSharedCrazyradio {
fn default() -> Self {
WeakSharedCrazyradio {
radio_command: None,
}
}
}
impl WeakSharedCrazyradio {
pub fn upgrade(&self) -> Option<SharedCrazyradio> {
let radio_command = self.radio_command.as_ref()?.upgrade()?;
let (send_packet_res_send, send_packet_res) = bounded(1);
let (send_packet_no_ack_res_send, send_packet_no_ack_res) = bounded(1);
let (scan_res_send, scan_res) = bounded(1);
Some(SharedCrazyradio {
radio_command,
send_packet_res_send,
send_packet_res,
send_packet_no_ack_res_send,
send_packet_no_ack_res,
scan_res_send,
scan_res,
})
}
}
enum RadioCommand {
SendPacket {
client: Sender<Result<SendPacketResult>>,
channel: Channel,
address: [u8; 5],
payload: Vec<u8>,
},
SendPacketNoAck {
client: Sender<Result<()>>,
channel: Channel,
address: [u8; 5],
payload: Vec<u8>,
},
Scan {
client: Sender<Result<ScanResult>>,
start: Channel,
stop: Channel,
address: [u8; 5],
payload: Vec<u8>,
},
}
struct SendPacketResult {
acked: bool,
payload: Vec<u8>,
retry: usize,
power_detector: bool,
rssi_dbm: Option<i16>,
}
struct ScanResult {
found: Vec<Channel>,
}
fn scan(
crazyradio: &mut Crazyradio,
start: Channel,
stop: Channel,
address: [u8; 5],
payload: Vec<u8>,
) -> Result<ScanResult> {
crazyradio.set_address(&address)?;
let found = crazyradio.scan_channels(start, stop, &payload)?;
Ok(ScanResult { found })
}
fn send_packet(
crazyradio: &mut Crazyradio,
channel: Channel,
address: [u8; 5],
payload: Vec<u8>,
) -> Result<SendPacketResult> {
let mut ack_data = Vec::new();
ack_data.resize(32, 0);
crazyradio.set_channel(channel)?;
crazyradio.set_address(&address)?;
crazyradio.set_ack_enable(true)?;
let ack = crazyradio.send_packet(&payload, &mut ack_data)?;
ack_data.resize(ack.length, 0);
Ok(SendPacketResult {
acked: ack.received,
payload: ack_data,
retry: ack.retry,
power_detector: ack.power_detector,
rssi_dbm: ack.rssi_dbm,
})
}
fn send_packet_no_ack(
crazyradio: &mut Crazyradio,
channel: Channel,
address: [u8; 5],
payload: Vec<u8>,
) -> Result<()> {
crazyradio.set_channel(channel)?;
crazyradio.set_address(&address)?;
crazyradio.set_ack_enable(false)?;
crazyradio.send_packet_no_ack(&payload)
}
fn radio_loop(crazyradio: Crazyradio, radio_cmd: Receiver<RadioCommand>) {
let mut crazyradio = crazyradio;
for command in radio_cmd {
match command {
RadioCommand::Scan {
client,
start,
stop,
address,
payload,
} => {
let res = scan(&mut crazyradio, start, stop, address, payload);
let _ = client.send(res);
}
RadioCommand::SendPacket {
client,
channel,
address,
payload,
} => {
let res = send_packet(&mut crazyradio, channel, address, payload);
let _ = client.send(res);
}
RadioCommand::SendPacketNoAck {
client,
channel,
address,
payload,
} => {
let res = send_packet_no_ack(&mut crazyradio, channel, address, payload);
let _ = client.send(res);
}
}
}
}