FactbirdHQ / atat

no_std crate for parsing AT commands
Apache License 2.0
109 stars 29 forks source link

Response with \r\r\n confuses URC Parser #191

Open patsoffice opened 9 months ago

patsoffice commented 9 months ago

Hello,

I am attempting to make use of atat for an application to make and answer voice calls as well as sending and interpreting DTMF tones. The modem I'm using emits the following when a voice call comes in:

\r\r\nRING\r\r\n

I was able to return a single Urc on a ring with the following (note the urc_helper in impl Parser for Urc has a trailing '\r':

#[derive(Clone, Debug)]
pub enum Urc {
    // #[at_urc(b"RING")]
    Ring,
    // #[at_urc(b"RDY")]
    Ready,
    // #[at_urc(b"MISSED_CALL")]
    MissedCall,
    // #[at_urc(b"+RXDTMF")]
    Dtmf,
}

impl AtatUrc for Urc {
    type Response = Urc;
    #[inline]
    fn parse(resp: &[u8]) -> Option<Self::Response> {
        let index = resp.iter().position(|&x| x == b':').unwrap_or(resp.len());
        Some(match &resp[..index] {
            b"MISSED_CALL" => Urc::MissedCall,
            b"RING" => Urc::Ring,
            b"RDY" => Urc::Ready,
            b"+RXDTMF" => Urc::Dtmf,
            _ => return None,
        })
    }
}

impl Parser for Urc {
    fn parse<'a>(buf: &'a [u8]) -> Result<(&'a [u8], usize), ParseError> {
        let (_, r) = alt((
            urc_helper(&b"MISSED_CALL"[..]),
            urc_helper(&b"RING\r"[..]),
            urc_helper(&b"RDY"[..]),
            urc_helper(&b"+RXDTMF"[..]),
        ))(buf)?;
        Ok(r)
    }
}

However, if there is a subsequent \r\r\nRING\r\r\n I seem to stop getting any more data from the Digester (calls to try_next_message_pure() on the Urc subscription channel return None).

I'm not sure where exactly to begin to debug this further as I am quite the Rust n00b.

patsoffice commented 9 months ago

I wrote a simple app (running on an ESP32) to demonstrate the behavior I'm seeing based on the unit test in ingress.rs:

#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
#![feature(utf8_chunks)]

use atat::atat_derive::AtatResp;
use atat::response_channel::ResponseChannel;
use atat::{AtDigester, AtatIngress, AtatUrcChannel, Ingress, UrcChannel};
use heapless::String;

#[derive(Clone, PartialEq, Debug)]
enum Urc {
    // #[at_urc(b"MISSED_CALL")]
    MissedCall,
    // #[at_urc(b"RING")]
    Ring,
}

impl atat::AtatUrc for Urc {
    type Response = Urc;
    #[inline]
    fn parse(resp: &[u8]) -> Option<Self::Response> {
        log::info!("resp: {:?}", resp);
        let index = resp.iter().position(|&x| x == b':').unwrap_or(resp.len());
        log::info!("index: {index}");
        Some(match &resp[..index] {
            b"MISSED_CALL" => Urc::MissedCall,
            b"RING" => Urc::Ring,
            _ => return None,
        })
    }
}

impl atat::Parser for Urc {
    fn parse<'a>(buf: &'a [u8]) -> Result<(&'a [u8], usize), atat::digest::ParseError> {
        log::info!("in parse()");
        let (_, r) = atat::nom::branch::alt((
            atat::digest::parser::urc_helper(&b"MISSED_CALL"[..]),
            atat::digest::parser::urc_helper(&b"RING\r"[..]),
        ))(buf)?;
        Ok(r)
    }
}

#[derive(Debug, Clone, AtatResp)]
pub struct MissedCall {
    #[at_arg(position = 0)]
    pub info: String<32>,
}

#[derive(Debug, Clone, AtatResp)]
pub struct Ring;

#[no_mangle]
fn main() {
    esp_idf_svc::sys::link_patches();
    esp_idf_svc::log::EspLogger::initialize_default();

    let res_channel = ResponseChannel::<100>::new();
    let urc_channel = UrcChannel::<Urc, 10, 1>::new();
    let mut ingress: Ingress<_, Urc, 100, 10, 1> = Ingress::new(
        AtDigester::<Urc>::new(),
        res_channel.publisher().unwrap(),
        urc_channel.publisher(),
    );

    let mut sub = urc_channel.subscribe().unwrap();

    let buf = ingress.write_buf();
    let data = b"\r\r\nRING\r\r\n\r\r\nRING\r\r\n\r\nMISSED_CALL: 10:24PM +15558675309\r\n";
    buf[..data.len()].copy_from_slice(data);
    ingress.try_advance(data.len()).unwrap();

    loop {
        let rc = sub.try_next_message_pure();    

        if rc.is_none() {
            break
        }
        log::info!("got {:?}", rc);
    }
}

The output is:

I (377) main_task: Calling app_main()
I (377) playground_atat: in parse()
I (377) playground_atat: resp: [82, 73, 78, 71]
I (387) playground_atat: index: 4
I (387) playground_atat: in parse()
I (397) playground_atat: resp: [82, 73, 78, 71]
I (397) playground_atat: index: 4
I (407) playground_atat: in parse()
I (407) playground_atat: got Some(Ring)
I (407) playground_atat: got Some(Ring)
I (417) main_task: Returned from app_main()

If there is only one \r\r\nRING\r\r\n then I successfully parse the MISSED_CALL.