use semtech_udp::pull_resp::Time;
use semtech_udp::{
pull_resp::{self, PhyData},
server_runtime::{Error, Event, UdpRuntime},
tx_ack, Bandwidth, CodingRate, DataRate, MacAddress, Modulation, SpreadingFactor,
};
use std::net::SocketAddr;
use structopt::StructOpt;
use tokio::sync::oneshot::{self, Receiver, Sender};
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let cli = Opt::from_args();
let addr = SocketAddr::from(([0, 0, 0, 0], cli.port));
println!("Starting server: {addr}");
let (mut client_rx, mut client_tx) = UdpRuntime::new(addr).await?.split();
let (tx, rx): (Sender<MacAddress>, Receiver<MacAddress>) = oneshot::channel();
let mut tx = Some(tx);
if cli.test {
tokio::spawn(async move {
let gateway_mac = rx.await.unwrap();
let mut first_shot = true;
while cli.delay != 0 || first_shot {
first_shot = false;
let data = vec![0; cli.length];
let txpk = pull_resp::TxPk {
time: Time::immediate(),
freq: cli.frequency,
rfch: 0,
powe: cli.power as u64,
modu: Modulation::LORA,
datr: DataRate::new(SpreadingFactor::_12, Bandwidth::_125KHz),
codr: Some(CodingRate::_4_5),
ipol: cli.polarization_inversion,
data: PhyData::new(data),
fdev: None,
prea: None,
ncrc: None,
};
println!("Sending: {txpk}");
let prepared_send = client_tx.prepare_downlink(Some(txpk), gateway_mac);
tokio::spawn(async move {
if let Err(e) = prepared_send.dispatch(Some(Duration::from_secs(5))).await {
if let Error::Ack(tx_ack::Error::AdjustedTransmitPower(
adjusted_power,
_tmst,
)) = e
{
println!("Packet sent at adjusted power: {adjusted_power:?}")
} else {
println!("Transmit Dispatch threw error: {e:?}")
}
} else {
println!("Send complete");
}
});
sleep(Duration::from_secs(cli.delay)).await;
}
});
}
println!("Ready for clients");
loop {
match client_rx.recv().await {
Event::UnableToParseUdpFrame(error, buf) => {
println!("Semtech UDP Parsing Error: {error}");
println!("UDP data: {buf:?}");
}
Event::NewClient((mac, addr)) => {
println!("New packet forwarder client: {mac}, {addr}");
if let Some(tx) = tx.take() {
tx.send(mac).unwrap();
}
}
Event::UpdateClient((mac, addr)) => {
println!("Mac existed, but IP updated: {mac}, {addr}");
}
Event::PacketReceived(rxpk, addr) => {
println!("Packet Receveived from {addr}: {rxpk:?}");
}
Event::StatReceived(stat, addr) => {
println!("Stat Receveived from {addr}: {stat:?}");
}
Event::NoClientWithMac(_packet, mac) => {
println!("Tried to send to client with unknown MAC: {mac:?}")
}
Event::ClientDisconnected((mac, addr)) => {
println!("Client disconnected: {mac}, {addr}");
}
}
}
}
#[derive(Debug, StructOpt)]
#[structopt(
name = "Semtech GWMP over UDP Host Example",
about = "Example application for semtech-udp library"
)]
pub struct Opt {
#[structopt(long, default_value = "1680")]
port: u16,
#[structopt(long)]
test: bool,
#[structopt(long, default_value = "27")]
power: u8,
#[structopt(long, default_value = "52")]
length: usize,
#[structopt(long, default_value = "0")]
delay: u64,
#[structopt(long, default_value = "868.1")]
frequency: f64,
#[structopt(long)]
polarization_inversion: bool,
}