meshtnc/
lib.rs

1use color_eyre::eyre::eyre;
2use futures::{SinkExt, StreamExt, lock::Mutex};
3use log::{debug, error, info, trace, warn};
4use tokio_serial::{self, SerialPort, SerialPortBuilderExt, SerialStream};
5use tokio_util::{bytes::{Bytes, BytesMut}, codec::{Decoder, Encoder}};
6use std::{fmt::Display, io::{self, stdout}, sync::{Arc, atomic::Ordering}, time::{Duration, SystemTime, UNIX_EPOCH}};
7use meshcore::{identity::Keystore, packet::Packet};
8use pcap_file::pcap::{PcapPacket, PcapWriter};
9use std::io::Write;
10
11use atomic_enum::atomic_enum;
12
13#[atomic_enum]
14#[derive(PartialEq)]
15enum MeshTncState {
16    Unknown  = 0,
17    Starting = 1,
18    RadioSet = 2,
19    Idle     = 3,
20    KISS     = 4,
21}
22
23impl Display for MeshTncState {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        match self {
26            MeshTncState::Unknown =>  write!(f, "Unknown"),
27            MeshTncState::Starting => write!(f, "Starting"),
28            MeshTncState::RadioSet => write!(f, "RadioSet"),
29            MeshTncState::Idle     => write!(f, "Idle"),
30            MeshTncState::KISS     => write!(f, "KISS"),
31        }
32    }
33}
34
35struct KissCodec {
36    state: Arc<AtomicMeshTncState>
37}
38
39impl Default for KissCodec {
40    fn default() -> Self {
41        Self { state:
42            Arc::new(AtomicMeshTncState::new(MeshTncState::Starting))
43        }
44    }
45}
46
47impl KissCodec {
48    fn decode_ascii(&mut self, src: &mut BytesMut) -> Result<Option<Vec<u8>>, io::Error> {
49        // trace!("In decode_ascii: {}", str::from_utf8(src.as_ref()).unwrap());
50        let newline = src.as_ref().iter().position(|b| *b == b'\n');
51        if let Some(n) = newline {
52            let line = src.split_to(n + 1);
53            return match str::from_utf8(line.as_ref()) {
54                Ok(s) => {
55                    trace!("Got ascii line: {}", s.trim_ascii());
56                    Ok(Some(s.into()))
57                },
58                Err(_) => Err(
59                    io::Error::new(io::ErrorKind::Other, "Invalid String")),
60            };
61        } else {
62            Ok(None)
63        }
64    }
65
66    fn decode_kiss(&mut self, _src: &mut BytesMut) -> Result<Option<Vec<u8>>, io::Error> {
67        Ok(None)
68    }
69
70    fn encode_ascii(&self, src: Vec<u8>, dst: &mut BytesMut) -> Result<(), io::Error> {
71        // Append the new string with a newline into the BytesMut
72        let string = String::from_utf8(src);
73        if let Err(e) = string {
74            warn!("Tried to send illegal string over port: {}", e);
75            return Err(io::Error::new(io::ErrorKind::Other, "Invalid string"));
76        } else if let Ok(string) = string {
77            let newline_string = format!("{}\n\r", string);
78            let string_length = newline_string.as_bytes().len();
79            dst.reserve(string_length);
80            dst.extend_from_slice(newline_string.as_bytes());
81            Ok(())
82        } else {
83            // INCONCEVABLE!
84            Ok(())
85        }
86    }
87
88    fn encode_kiss(&self, _src: Vec<u8>, _dst: &mut BytesMut) -> Result<(), io::Error> {
89        Ok(())
90    }
91}
92
93impl Decoder for KissCodec {
94    type Item = Vec<u8>;
95    type Error = io::Error;
96
97    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
98        // This function gets called periodically, and if there's a frame it's able
99        // to discover within the byte stream then it returns Ok(Self::Item) with the
100        // contents after splitting that data off the head of the buffer.
101        // 
102        // If no frames are discovered within the stream, then it returns Ok(None).
103        //
104        // Because the KISS system can either be ASCII when in non-KISS mode, and KISS
105        // doesn't use regular line endings, we have to keep track of what state we think
106        // we're in.
107        let state = self.state.load(Ordering::Relaxed);
108        match state {
109            MeshTncState::Starting => return self.decode_ascii(src),
110            MeshTncState::RadioSet => return self.decode_ascii(src),
111            MeshTncState::Idle     => return self.decode_ascii(src),
112            MeshTncState::KISS     => return self.decode_kiss(src),
113
114            // If we're in an unknown state, it's really best to just start discarding
115            // bytes.  We should know somewhere "above" this code that we need help,
116            // and that place should either attempt to reboot the device or send an
117            // escape sequence that lets us get into either Starting or Idle.
118            MeshTncState::Unknown => {
119                src.clear();
120                return Ok(None);
121            }
122        }
123    }
124}
125
126impl Encoder<Vec<u8>> for KissCodec {
127    type Error = io::Error;
128
129    fn encode(&mut self, mut item: Vec<u8>, dst: &mut BytesMut) -> Result<(), Self::Error> {
130        // Depending on what state we're in, we need to make choices about how to
131        // frame the message.
132        let state = self.state.load(Ordering::Relaxed);
133        match state {
134            MeshTncState::Starting => return self.encode_ascii(item, dst),
135            MeshTncState::RadioSet => return self.encode_ascii(item, dst),
136            MeshTncState::Idle     => return self.encode_ascii(item, dst),
137            MeshTncState::KISS     => return self.encode_kiss(item, dst),
138
139            // If we're in an unknown state, it's really best to just start discarding
140            // bytes.  We should know somewhere "above" this code that we need help,
141            // and that place should either attempt to reboot the device or send an
142            // escape sequence that lets us get into either Starting or Idle.
143            MeshTncState::Unknown => {
144                item.clear();
145                return Ok(());
146            }
147        }
148    }
149}
150pub struct MeshTNC {
151    pub port: Mutex<SerialStream>,
152    pub freq: f32,
153    pub bw: f32,
154    pub sf: u8,
155    pub cr: u8,
156    pub sb: String,
157    pub keystore: Keystore
158}
159
160impl MeshTNC {
161    pub fn new(port: String, freq: f32, bw: f32, sf: u8, cr: u8, sb: String, keystore: Keystore) -> color_eyre::eyre::Result<MeshTNC> {
162
163        // Attempt to open the port
164        let mut port = tokio_serial::new(port, 115_200)
165            .flow_control(tokio_serial::FlowControl::None)
166            .stop_bits(tokio_serial::StopBits::One)
167            .data_bits(tokio_serial::DataBits::Eight)
168            .open_native_async()?;
169
170        // We really don't need exclusive access to this port.
171        // This will allow us to see what's happening with other
172        // users of this port are running.
173        #[cfg(unix)]
174        if let Err(e) = port.set_exclusive(false) {
175            warn!("Unable to set non-exslusive use of this port. ¯\\_(ツ)_/¯: {}", e.description);
176        }
177
178        Ok(MeshTNC {
179            port: Mutex::new(port),
180            freq,
181            bw,
182            sf,
183            cr,
184            sb,
185            keystore,
186        })
187    }
188
189    pub async fn start<W>(&mut self, mut pcap_writer: Option<PcapWriter<W>>) -> color_eyre::eyre::Result<()>
190        where W: Write {
191        // get protected access to the port
192        let port = self.port.get_mut();
193
194        // This doesn't work on the Seeed Studio LoRa-E5 Dev Board
195        // because the rts pin is capactively-coupled to reset, and the
196        // capacitor is way too small.  So, the reset pulse is too small
197        // and too short to actually work.
198        info!("Attempting to reboot TNC");
199        port.write_request_to_send(false)?;
200        tokio::time::sleep(Duration::from_millis(10)).await;
201        port.write_request_to_send(true)?;
202
203        // Create the framed reader
204        let codec = KissCodec::default();
205        // Get a copy of the state from the reader (needs to be Arc<Atomic> because
206        // we need to share ownershil with the codec system.)
207        let local_state = Arc::clone(&codec.state);
208        // Create the reader codec framing system
209        let framed = codec.framed(port);
210        let (mut writer, mut reader) = framed.split();
211
212        // Handle incoming bytes from the interface
213        while let Some(line_result) = reader.next().await {
214            let line = line_result.expect("Failed to read line");
215
216            // Depending on the mode we're in, we want to handle them differently
217            // In the "starting" mode, we're waiting for the "Starting!" message
218            // from the device.
219
220            // First match just KISS because the rest are all ASCII
221            let state = local_state.load(Ordering::Relaxed);
222
223            if state == MeshTncState::KISS {
224                // Just handle the packet
225            }
226            
227            else {
228                // Convert it to ascii and trim any whitespace
229                let line = String::from_utf8(line)?;
230
231                match state {
232                // This is the origin state where we assume the device has just
233                // booted.  We look for "Starting!" and move into idle.
234                MeshTncState::Starting => {
235                    if line.trim_ascii() == "Starting!" {
236                        // Send the command to set the radio data.
237                        let radio_set = format!(
238                            "set radio {},{},{},{},{}",
239                            self.freq,
240                            self.bw,
241                            self.sf,
242                            self.cr,
243                            self.sb
244                        );
245                        // Send the radio parameters
246                        writer.send(radio_set.as_bytes().to_vec()).await?;
247                        local_state.store(MeshTncState::RadioSet, Ordering::Relaxed);
248                        debug!("Moved into the Radio Set state")
249                    }
250                }
251                MeshTncState::RadioSet => {
252                    // Ensure that the radio parameters were accepted.  We'll
253                    // scan until we get either something that contains "OK"
254                    // or an "Error".  In the case of "Error", the radio didn't
255                    if line.contains("OK") {
256                        // Move onto the "idle" state.
257                        local_state.store(MeshTncState::Idle, Ordering::Relaxed);
258                        debug!("Moved into idle state");
259                        info!("Radio ready!");
260
261                        if pcap_writer.is_none() {
262                            println!(" ROUTE T | v1 | Transp Fl. |     Route Summary    | I | Pkt. Type | Summary....");
263                        }
264                    } else if line.contains("Error") {
265                        // This error can't be helped by us, so kick it back to the user
266                        // with a hopefully helpful error
267                        error!("Radio parameters weren't accepted by the TNC.  Please check them.");
268                        return Err(eyre!("Unable to set radio parameters; they were rejected by the TNC."));
269                    }
270                }
271                MeshTncState::Idle => {
272                    // In the idle state, the TNC will send us radio log packets.
273                    
274                    // If the user didn't request KISS mode (which allows transmit, but no RSSI/SNR)
275                    // then we're kinda done, and we can start sending packets into the channel
276                    let components: Vec<&str> = line.trim_ascii().split(",").collect();
277                    if components[1] == "RXLOG" {
278                        if components.len() == 5 {
279                            let _timestamp: Result<u64, _> = components[0].parse();
280                            // Skipping "RXLOG"
281                            // and the RSSI and SNR for now
282                            let _: Option<f32> = components[2].parse().ok();
283                            let _: Option<f32> = components[3].parse().ok();
284                            let data: Result<Vec<u8>, _> = hex::decode(components[4]);
285
286                            if let Ok(data) = data {
287                                debug!("Got packet: {}", hex::encode_upper(&data));
288
289                                let bytes = Bytes::copy_from_slice(&data);
290
291                                if let Some(ref mut pcap_writer) = pcap_writer {
292                                    Self::write_packet_pcap(&bytes, pcap_writer).await?;
293                                }
294
295                                let mut packet = Packet::from(bytes);
296                                packet.try_decrypt(&self.keystore);
297                                println!("{}", packet);
298
299                            } else if let Err(e) = data {
300                                debug!("Got RXLOG packet with invalid HEX: \"{}\": {}", components[4], e);
301                            }
302
303                            continue;
304                        }
305                        debug!("Got a potentially mal-formed RXLOG packet: \n{}", line);
306                    }
307                },
308                // We'll never get here, because we handle it up there ^
309                MeshTncState::KISS => break,
310                // We'll never get here, because bytes in `unknown`
311                // are all discarded.
312                MeshTncState::Unknown => break,
313            }
314
315            }
316        }
317
318        Ok(())
319   }
320
321    pub async fn write_packet_pcap<W>(packet: &Bytes, pcap_writer: &mut PcapWriter<W>) -> color_eyre::eyre::Result<()>
322        where W: Write {
323        pcap_writer.write_packet(&PcapPacket::new(
324            SystemTime::now().duration_since(UNIX_EPOCH)?,
325            (packet.len() + 14 + 20) as u32,
326            packet,
327        ))?;
328
329        // Is this actually necessary?
330        stdout().flush()?;
331
332        Ok(())
333    }
334
335}