liblora 0.1.1

A library to handle LoRa UART functionality
Documentation
use serial2::SerialPort;
use std::str;
use std::str::FromStr;
use std::thread;
use std::time::Duration;

pub mod error;
use error::LoRaError as Lerr;

pub mod rcv;
use rcv::Rcv;

pub mod transfer;
use transfer::{Context, Transfer};

const LORA_MSG_BUF: usize = 512;

#[derive(Debug)]
pub struct LoRa {
    pub serial: SerialPort,
    pub config: Config,
    pub buf: [u8; LORA_MSG_BUF],
    pub verbose: bool,
    pub msg_queue: Vec<Rcv>,
}

#[derive(Debug, Default)]
pub struct Config {
    pub addr: u16,
    pub dest: u16,
    pub net: u8,
    pub param: Param,
}

#[derive(Debug)]
pub struct Param {
    pub spread: u8,
    pub bandwidth: u8,
    pub coderate: u8,
    pub preamble: u8,
}

impl Default for Param {
    fn default() -> Self {
        Self {
            spread: 5,
            bandwidth: 9,
            coderate: 1,
            preamble: 4,
        }
    }
}

impl LoRa {
    pub fn open(port: &str, baud: u32, millis: u64) -> Result<Self, Lerr> {
        let mut serial = SerialPort::open(port, baud)?;
        serial.set_read_timeout(Duration::from_millis(millis))?;
        Ok(Self::new(serial))
    }

    pub fn new(serial: SerialPort) -> Self {
        let config = Config::default();
        let msg_queue = Vec::default();
        Self {
            serial,
            config,
            buf: [0u8; LORA_MSG_BUF],
            verbose: true,
            msg_queue,
        }
    }

    pub fn configure(&mut self) -> Result<(), Lerr> {
        self.query(&format!("AT+ADDRESS={}", self.config.addr))?;
        self.query(&format!("AT+NETWORKID={}", self.config.net))?;
        self.query(&format!(
            "AT+PARAMETER={},{},{},{}",
            self.config.param.spread,
            self.config.param.bandwidth,
            self.config.param.coderate,
            self.config.param.preamble,
        ))?;
        Ok(())
    }

    fn str_chunk(mut input: &str) -> Vec<String> {
        let mut out = Vec::new();

        while input.len() > 240 {
            out.push(input[..240].to_string());
            input = &input[..240];
        }
        out.push(input.to_string());

        out
    }

    pub fn send_msg(&mut self, msg: &str) -> Result<(), Lerr> {
        for chunk in Self::str_chunk(msg) {
            let command = format!("AT+SEND={},{},{chunk}", self.config.dest, chunk.len());
            self.query(&command)?;
        }
        Ok(())
    }

    pub fn send_msg_to(&mut self, dest: u16, msg: &str) -> Result<(), Lerr> {
        for chunk in Self::str_chunk(msg) {
            let command = format!("AT+SEND={},{},{chunk}", dest, chunk.len());
            self.query(&command)?;
        }
        Ok(())
    }

    pub fn send_data(&mut self, ctx: &mut Context) -> Result<(), Lerr> {
        let dts = ctx.begin_transfer()?;
        'begin: loop {
            self.send_msg(&String::from(dts.clone()))?;
            let msg = self.get_messages()?;
            for m in msg {
                if m.data == "DTR" {
                    break 'begin;
                }
            }
        }

        // send data
        'end: loop {
            if let Some(next) = ctx.get_next()? {
                println!(
                    "sending {} chunk: {} / {}",
                    ctx.name, ctx.next_chunk, ctx.total_chunks
                );
                self.send_msg(&String::from(next))?;
            } else {
                let dte = ctx.end_transfer()?;
                self.send_msg(&String::from(dte))?;
            }
            for m in self.get_messages()? {
                let data = m.data.clone();
                let recv = Transfer::try_from(data)?;
                ctx.process(&recv)?;
                if m.data == "DTD" {
                    break 'end;
                }
            }
        }

        println!("{} transfer complete", ctx.name);
        Ok(())
    }

    pub fn recv_data(&mut self, msg: &Transfer) -> Result<Context, Lerr> {
        let mut ctx = Context::new_recv(msg)?;
        loop {
            if ctx.data.is_empty() {
                let dtr = ctx.begin_transfer_ack()?;
                self.send_msg(&String::from(dtr))?;
            }
            if let Err(Lerr::ResponseError(e)) = self.check_read() {
                println!("{e} - retrying");
                continue;
            }
            for m in self.get_messages()? {
                if let Ok(transfer) = Transfer::try_from(m.data) {
                    ctx.process(&transfer)?;
                    println!(
                        "received {} chunk: {} / {}",
                        ctx.name,
                        ctx.next_chunk + 1,
                        ctx.total_chunks
                    );
                }
            }
            if ctx.is_transfer_complete() {
                break;
            }
            if let Ok(Some(req)) = ctx.req_data_chunks() {
                println!("requesting {:?} chunks", req);
                self.send_msg(&String::from(req))?;
            }
        }

        loop {
            let dtd = ctx.end_transfer_ack()?;
            self.send_msg(&String::from(dtd))?;
            self.check_read()?;
            let messages = self.get_messages()?;
            if messages.is_empty() {
                break;
            }
            for m in messages {
                let data = m.data.clone();
                let recv = Transfer::try_from(data)?;
                ctx.process(&recv)?;
            }
        }

        println!("{} transfer complete", ctx.name);
        Ok(ctx)
    }
    pub fn query(&mut self, query: &str) -> Result<(), Lerr> {
        let data = format!("{query}\r\n");
        self.write(data.as_bytes())?;
        self.read()?;
        Ok(())
    }

    pub fn write(&self, out: &[u8]) -> Result<(), Lerr> {
        self.serial.write_all(out)?;
        if self.verbose {
            Self::internal_print('>', out)?;
        }
        thread::sleep(Duration::from_millis(75));
        Ok(())
    }

    pub fn read(&mut self) -> Result<(), Lerr> {
        self.read_line()?;
        if self.verbose {
            self.print_buf()?;
        }
        self.handle_result()?;
        Ok(())
    }

    fn read_line(&mut self) -> Result<(), Lerr> {
        self.buf = [0u8; LORA_MSG_BUF];
        let mut count = 0;
        while count < LORA_MSG_BUF {
            self.serial.read_exact(&mut self.buf[count..count + 1])?;
            if self.buf[count] == b'\n' {
                break;
            }
            count += 1;
        }
        Ok(())
    }

    pub fn check_read(&mut self) -> Result<(), Lerr> {
        self.buf = [0u8; LORA_MSG_BUF];
        if self.read_line().is_err() {
            return Ok(());
        };
        if self.verbose {
            self.print_buf()?;
        }
        self.handle_result()?;
        Ok(())
    }

    pub fn get_messages(&mut self) -> Result<Vec<Rcv>, Lerr> {
        let out = self.msg_queue.clone();
        self.msg_queue = Vec::new();
        Ok(out)
    }

    pub fn print_msg_queue(&mut self) -> Result<(), Lerr> {
        for msg in &self.msg_queue {
            println!("{}", msg.fmt());
        }
        self.msg_queue = Vec::new();
        Ok(())
    }

    pub fn print_buf(&self) -> Result<(), Lerr> {
        Self::internal_print('<', &self.buf)?;
        Ok(())
    }

    fn internal_print(header: char, buf: &[u8]) -> Result<(), Lerr> {
        let s = str::from_utf8(buf)?;
        println!("{header} {s}");
        Ok(())
    }

    fn internal_error(line: &str) -> Result<(), Lerr> {
        let s = line[5..].lines().next().ok_or(Lerr::InternalError)?;
        let err = u8::from_str(s)?;
        let msg = match err {
            1 => "There is no line ending (0x0d 0x0a) at the end of the AT Command",
            2 => "The AT Command does not start with \"AT\"",
            4 => "Unknown command",
            5 => "The data to be sent does not match the actual length",
            10 => "TX timeout",
            12 => "CRC error",
            13 => "TX data exceeds 240 bytes",
            14 => "Failed to write flash memory",
            15 => "Unknown failure",
            17 => "Last TX was incomplete",
            18 => "Preamble value is invalid",
            19 => "RX failed, Header error",
            20 => "The time setting value of the \"Smart receiving power saving mode\" is invalid",
            _ => "Unknown error",
        };
        let err_msg = format!("Error {err}: {msg}");
        Err(Lerr::ResponseError(err_msg))
    }

    fn handle_result(&mut self) -> Result<(), Lerr> {
        let buf = str::from_utf8(&self.buf)?;
        //TODO: Should be unnecessary now
        if buf.len() < 4 {
            return Ok(());
        }
        match &buf[..4] {
            "+ERR" => return Self::internal_error(buf),
            "+RCV" => match Rcv::try_from(buf) {
                Ok(rcv) => self.msg_queue.push(rcv),
                Err(e) => println!("RCV error {e}"),
            },
            &_ => (),
        }
        Ok(())
    }
}