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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
extern crate aprs; extern crate fap; mod codec; use std::error::Error; use std::time::Duration; use futures::sink::SinkExt; use futures::StreamExt; use log::{info, trace, warn}; use tokio::net::TcpStream; use tokio::time; use tokio_util::codec::{FramedRead, FramedWrite}; pub struct APRSPacket { pub raw: Vec<u8>, } impl APRSPacket { pub fn parsed(&self) -> Result<Box<dyn aprs::Packet>, Box<dyn Error>> { let raw_packet = self.raw.clone(); let parsed = fap::Packet::new(raw_packet); match parsed { Ok(packet) => Ok(Box::new(packet)), Err(err) => Err(Box::new(err)), } } } pub struct ISSettings { pub host: String, pub port: u16, pub callsign: String, pub passcode: String, pub filter: String, } impl ISSettings { pub fn new( host: String, port: u16, callsign: String, passcode: String, filter: String, ) -> ISSettings { ISSettings { host, port, callsign, passcode, filter, } } } pub type PacketHandler = fn(APRSPacket); pub struct IS { settings: ISSettings, packet_handler: PacketHandler, } impl IS { pub fn new(settings: ISSettings, packet_handler: PacketHandler) -> IS { IS { settings, packet_handler, } } #[tokio::main] pub async fn connect(&self) -> Result<(), Box<dyn Error>> { let address = format!("{}:{}", self.settings.host, self.settings.port); let stream = TcpStream::connect(address).await?; let (r, w) = stream.into_split(); let mut writer = FramedWrite::new(w, codec::ByteLinesCodec::new()); let mut reader = FramedRead::new(r, codec::ByteLinesCodec::new()); let login_message = { let name = option_env!("CARGO_PKG_NAME").unwrap_or("unknown"); let version = option_env!("CARGO_PKG_VERSION").unwrap_or("0.0.0"); format!( "user {} pass {} vers {} {}{}", self.settings.callsign, self.settings.passcode, name, version, if self.settings.filter == "" { "".to_string() } else { format!(" filter {}", self.settings.filter) } ) }; info!("Logging on to APRS-IS server"); trace!("Login message: {}", login_message); writer.send(login_message.as_bytes()).await?; tokio::spawn(async move { let mut interval = time::interval(Duration::from_secs(3600)); loop { interval.tick().await; info!("Sending keep alive message to APRS-IS server"); writer.send("# keep alive".as_bytes()).await.unwrap(); } }); while let Some(packet) = reader.next().await { match packet { Ok(packet) => { if packet[0] == b'#' { match String::from_utf8(packet.to_vec()) { Ok(server_message) => { trace!("Received server response: {}", server_message); if server_message.contains("unverified") { info!("User not verified on APRS-IS server"); continue; } if server_message.contains(" verified") { info!("User verified on APRS-IS server"); } } Err(err) => warn!("Error processing server response: {}", err), } } else { trace!("{:?}", packet); (self.packet_handler)(APRSPacket { raw: packet.to_vec(), }); } } Err(err) => { warn!("Error processing packet from APRS-IS server: {}", err); } } } Ok(()) } }