Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Allow streaming/chunked type #89

Open
MathiasKoch opened this issue May 6, 2021 · 1 comment
Open

[RFC] Allow streaming/chunked type #89

MathiasKoch opened this issue May 6, 2021 · 1 comment
Labels
atat_derive Issues related to the atat_derive crate of the workspace atat Issues related to the atat crate of the workspace enhancement New feature or request rfc Request for comment serde_at Issues related to the serde_at crate of the workspace

Comments

@MathiasKoch
Copy link
Member

MathiasKoch commented May 6, 2021

We were toying around with the idea of introducing a newtype wrapper that would allow for streaming/chunked payloads of AT commands.

Motivation

The main motivation would be to be able to reduce the stack usage of the library, by reducing the size of BufLen, as this currently needs to be able to hold the full WORST-CASE command length at all times. For all of our applications, that would be a socket ingress payload, usually in the magnitude of 512 bytes + overhead. Almost every other command we have in our applications of ATAT would be able to run with a much smaller BufLen of say 128 bytes. This could probably be even further reduced by fixing #82.

In our applications of ATAT, BufLen is 1024 bytes.

Command types to consider

  • Large byte payloads (eg. socket RX)
  • Vec types (eg. wifi ssid scan or cellular operator scan)

A typical command suitable for this would be AT+USORD:

Syntax:

AT+USORD=< socket >,< length >

Response:

+USORD: < socket >,< length >,< data in the ASCII [0x00,0xFF] range >
OK

Example:

AT+USORD=3,16
+USORD: 3,16,"16 bytes of data"
OK

Implementation suggestion

#[derive(Clone, AtatCmd)]
#[at_cmd("+USORD", SocketData)]
pub struct ReadSocketData {
    #[at_arg(position = 0)]
    pub socket: SocketHandle,
    #[at_arg(position = 1)]
    pub length: usize,
}

/// Example AT command with streaming payload
#[derive(Clone, AtatResp)]
pub struct SocketData<'a> {
    #[at_arg(position = 0)]
    pub socket: SocketHandle,
    #[at_arg(position = 1)]
    pub length: usize,
    #[at_arg(position = 2)]
    pub data: Streaming<'a, U128>,
}


pub fn example_using_stream() {
    match at_client.send(&ReadSocketData { socket: SocketHandle(0), length: 512 }) {
        Err(nb::Error::WouldBlock) => {
            // This would indicate that the first part of the response has yet to be received
        }
        Err(nb::Error::Other(_)) => {
            // This would indicate that an actual error occured on the AT line, 
            // or a timeout occured while waiting for the first part of the response.
        }
        Ok(response) => {
            // `response.length` can be used to verify that all chunks are received in this case
            println!("Socket: {:?}, length: {:?}", response.socket, response.length);

            // `at_client` is locked for the full lifetime of `response`, 
            // but the ingress part of `ATAT` should still be ingressing and pushing to the response queue.

            loop {
                match response.data.poll_next() {
                    Poll::Pending => {}
                    Poll::Ready(None) => break
                    Poll::Ready(chunk) => {
                        // Data can be enqueued to a socket buffer, one chunk at a time.
                        socket_layer.enqueue_slice(&chunk);
                    }
                }
            }

            // Above loop should be possible to rewrite using async, as:
            while let Some(chunk) in response.data.next().await {
                // Data can be enqueued to a socket buffer, one chunk at a time.
                socket_layer.enqueue_slice(&chunk);
            }
        }
    }
}


// Below here is given by `ATAT`
pub type ResItem<BufLen> = Result<AtItem<BufLen>, InternalError>;

pub enum AtItem<BufLen> {
    Response(Vec<u8, BufLen>),
    Chunk(Vec<u8, BufLen>)
}

pub struct Streaming<'a, BufLen> {
    consumer: &'a ResConsumer<BufLen>,
}

impl<'a, BufLen> Stream for Streaming<'a, BufLen> {
    type item = Vec<u8, BufLen>;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        match self.consumer.peek() {
            Some(Ok(AtItem::Chunk(bytes)) => {
                unsafe { self.consumer.dequeue_unchecked(); }
                Poll::Ready(Some(bytes))
            }
            // The next item in the queue is a new response
            Some(Ok(AtItem::Response(_)) => {
                Poll::Ready(None)
            }
            // FIXME: Is this correct?
            Some(Err(e)) => Poll::Ready(None),
            None => Poll::Pending
        }
    }
}

impl<'a, BufLen> Drop for Streaming<'a, BufLen> {
    fn drop(&mut self) {
        // Dequeue items until there are no more `AtItem::Chunk`
        while let Some(Ok(AtItem::Chunk(_)) = self.consumer.peek() {
            self.consumer.dequeue();
        }
    }
}

Apart from above implementation steps, the parse() function of commands needs to be altered to handle AtItem::Response & constructing Streaming,
and the Digester needs to be able to handle digesting in chunks. Perhaps a new StreamingDigester would allow non-breaking changes?

Considerations

  • This should preferably not incour significant overhead if not using the Streaming wrapper
  • cmd.parse(..) would need to take a reference to self.res_c, which might be tricky to get right?
  • The lifetime of A::Response might be tricky to get right?
  • Above implementation suggestion will not be able to handle Vec<T> response streaming. See below comment on handling Vec<T>
  • Not sure if this should be implemented with futures::Stream directly, or a similar self-defined trait. Unknown part would be how to handle self: Pin<&mut Self>, cx: &mut Context<'_>
  • Can this be applied to URC's as well?

Limitations

  • The streaming parameter has to be the last parameter of the response! I think this will always be the case anyways, but am not sure? (This could be checked for any type deriving the AtatCmd at compile time)
  • The length / chunk-size of Streaming<'a, U128> (U128 here) has to be equal to BufLen, as the AtItem::Chunk is dequeued on success. Could probably be allowed to <= BufLen, if we kept a chunk index in Streaming, and only dequeued the chunk on the remainder part, but i am not sure we would win anything by it?

Further improvements

  • It would be awesome if we could fill out the size_hint() function somehow, on the responses where that would be known/can be calculated. For example for the above SocketData, the size_hint would be response.length / 128.
@MathiasKoch MathiasKoch added enhancement New feature or request atat Issues related to the atat crate of the workspace atat_derive Issues related to the atat_derive crate of the workspace serde_at Issues related to the serde_at crate of the workspace rfc Request for comment labels May 6, 2021
@MathiasKoch
Copy link
Member Author

Above implementation suggestion will not be able to handle Vec response streaming.

An example of an AT command that would benefit alot from streaming Vec<T> like handling would be AT+UWSCAN.

Example of `AT+UWSCAN`

AT+UWSCAN
+UWSCAN:EA63DA77C485,1,"emendo-Blackbird",1,-54,18,8,8
+UWSCAN:E063DA74C38A,1,"Blackbird-device",1,-62,18,8,8
+UWSCAN:6009C32A1811,1,"Factbird-Duo-768e8aa",1,-40,0,0,0
+UWSCAN:E063DA74A587,1,"Blackbird-device",1,-59,18,8,8
+UWSCAN:E063DA77C485,1,"Blackbird-device",1,-54,18,8,8
+UWSCAN:E663DA74A587,1,"emendo-guest",1,-60,18,8,8
+UWSCAN:E063DA74C38A,1,"Blackbird-device",1,-62,18,8,8
+UWSCAN:EA63DA74A587,1,"emendo-Blackbird",1,-59,18,8,8
+UWSCAN:E663DA77C485,1,"emendo-guest",1,-54,18,8,8
+UWSCAN:E263DA74C38A,1,"emendo-guest",1,-62,18,8,8
+UWSCAN:EA63DA77C485,1,"emendo-Blackbird",1,-54,18,8,8
+UWSCAN:F263DA74C38A,1,"emendo-Blackbird",1,-62,18,8,8
+UWSCAN:6009C32A1811,1,"Factbird-Duo-768e8aa",1,-39,0,0,0
+UWSCAN:E063DA74C38A,1,"Blackbird-device",1,-62,18,8,8
+UWSCAN:E063DA77C485,1,"Blackbird-device",1,-53,18,8,8
+UWSCAN:B4FBE4C732FD,1,"Blackbird-device",1,-76,18,8,8
+UWSCAN:E663DA77C485,1,"emendo-guest",1,-54,18,8,8
+UWSCAN:EA63DA77C485,1,"emendo-Blackbird",1,-53,18,8,8
+UWSCAN:E063DA74C38A,1,"Blackbird-device",1,-62,18,8,8
+UWSCAN:E263DA74C38A,1,"emendo-guest",1,-62,18,8,8
+UWSCAN:F263DA74C38A,1,"emendo-Blackbird",1,-62,18,8,8
+UWSCAN:E063DA74A587,1,"Blackbird-device",1,-60,18,8,8
+UWSCAN:B4FBE4C7325C,1,"Blackbird-device",1,-74,18,8,8
+UWSCAN:EA63DA77C485,1,"emendo-Blackbird",1,-53,18,8,8
+UWSCAN:BAFBE4C7325C,1,"emendo-guest",1,-75,18,8,8
+UWSCAN:E663DA74A587,1,"emendo-guest",1,-60,18,8,8
+UWSCAN:BAFBE4C732FD,1,"emendo-guest",1,-76,18,8,8
+UWSCAN:EA63DA74A587,1,"emendo-Blackbird",1,-60,18,8,8
+UWSCAN:EA63DA74A587,1,"emendo-Blackbird",1,-59,18,8,8
+UWSCAN:EA63DA74A587,1,"emendo-Blackbird",1,-60,18,8,8
+UWSCAN:BEFBE4C732FD,1,"emendo-Blackbird",1,-77,18,8,8
+UWSCAN:BEFBE4C7325C,1,"emendo-Blackbird",1,-75,18,8,8
+UWSCAN:E663DA74A587,1,"emendo-guest",1,-59,18,8,8
+UWSCAN:B4FBE4C7325C,1,"Blackbird-device",1,-75,18,8,8
+UWSCAN:E263DA74C38A,1,"emendo-guest",1,-62,18,8,8
+UWSCAN:EA63DA74A664,1,"emendo-Blackbird",6,-56,18,8,8
+UWSCAN:E063DA74A664,1,"Blackbird-device",6,-56,18,8,8
+UWSCAN:B4FBE4C7331A,1,"Blackbird-device",6,-65,18,8,8
+UWSCAN:E663DA74A664,1,"emendo-guest",6,-56,18,8,8
+UWSCAN:BAFBE4C7331A,1,"emendo-guest",6,-65,18,8,8
+UWSCAN:EA63DA74A664,1,"emendo-Blackbird",6,-56,18,8,8
+UWSCAN:BAFBE4C7331A,1,"emendo-guest",6,-65,18,8,8
+UWSCAN:BEFBE4C7331A,1,"emendo-Blackbird",6,-64,18,8,8
+UWSCAN:EA63DA74C515,1,"emendo-Blackbird",6,-69,18,8,8
+UWSCAN:E063DA74A664,1,"Blackbird-device",6,-56,18,8,8
+UWSCAN:B4FBE4C731AA,1,"Blackbird-device",6,-70,18,8,8
+UWSCAN:E663DA74A664,1,"emendo-guest",6,-55,18,8,8
+UWSCAN:E063DA74C515,1,"Blackbird-device",6,-69,18,8,8
+UWSCAN:EA63DA74A664,1,"emendo-Blackbird",6,-57,18,8,8
+UWSCAN:B4FBE4C7331A,1,"Blackbird-device",6,-65,18,8,8
+UWSCAN:E663DA74C515,1,"emendo-guest",6,-69,18,8,8
+UWSCAN:BAFBE4C7331A,1,"emendo-guest",6,-64,18,8,8
+UWSCAN:B4FBE4C731AA,1,"Blackbird-device",6,-71,18,8,8
+UWSCAN:BEFBE4C731AA,1,"emendo-Blackbird",6,-71,18,8,8
+UWSCAN:BEFBE4C7331A,1,"emendo-Blackbird",6,-65,18,8,8
+UWSCAN:BEFBE4C7331A,1,"emendo-Blackbird",6,-65,18,8,8
+UWSCAN:E663DA74C62C,1,"emendo-guest",11,-60,18,8,8
+UWSCAN:001E42258D9C,1,"RUT_8D9C_2G",11,-62,18,12,4
+UWSCAN:E063DA74A530,1,"Blackbird-device",11,-64,18,8,8
+UWSCAN:B4FBE4C731B2,1,"Blackbird-device",11,-71,18,8,8
+UWSCAN:B4FBE4C731B2,1,"Blackbird-device",11,-71,18,8,8
+UWSCAN:E663DA74A530,1,"emendo-guest",11,-64,18,8,8
+UWSCAN:E063DA74C62C,1,"Blackbird-device",11,-60,18,8,8
+UWSCAN:BAFBE4C731B2,1,"emendo-guest",11,-72,18,8,8
+UWSCAN:BEFBE4C731B2,1,"emendo-Blackbird",11,-72,18,8,8
+UWSCAN:E663DA74C62C,1,"emendo-guest",11,-60,18,8,8
+UWSCAN:EA63DA74A530,1,"emendo-Blackbird",11,-64,18,8,8
+UWSCAN:EA63DA74C62C,1,"emendo-Blackbird",11,-59,18,8,8
+UWSCAN:EA63DA74C62C,1,"emendo-Blackbird",11,-59,18,8,8
+UWSCAN:E063DA74C62C,1,"Blackbird-device",11,-59,18,8,8
+UWSCAN:E663DA74C62C,1,"emendo-guest",11,-60,18,8,8
+UWSCAN:001E42258D9C,1,"RUT_8D9C_2G",11,-60,18,12,4
+UWSCAN:E063DA74A530,1,"Blackbird-device",11,-64,18,8,8
+UWSCAN:E663DA74A530,1,"emendo-guest",11,-64,18,8,8
+UWSCAN:E663DA74A530,1,"emendo-guest",11,-64,18,8,8
+UWSCAN:EA63DA74A530,1,"emendo-Blackbird",11,-64,18,8,8
+UWSCAN:EA63DA74C62C,1,"emendo-Blackbird",11,-59,18,8,8
+UWSCAN:B4FBE4C731B2,1,"Blackbird-device",11,-71,18,8,8
+UWSCAN:60634C263349,1,"DWR-921-3348",11,-89,26,12,4
+UWSCAN:EA63DA74A530,1,"emendo-Blackbird",11,-64,18,8,8
+UWSCAN:9CB6D03EB4A3,1,"TEST",36,-41,0,0,0
+UWSCAN:9CB6D03EB4A3,1,"TEST",36,-41,0,0,0
+UWSCAN:EA63DA75C62C,1,"emendo-Blackbird",36,-57,18,8,8
+UWSCAN:E063DA75C62C,1,"Blackbird-device",36,-58,18,8,8
+UWSCAN:E663DA75C62C,1,"emendo-guest",36,-58,18,8,8
+UWSCAN:E063DA75C62C,1,"Blackbird-device",36,-58,18,8,8
+UWSCAN:E663DA75C62C,1,"emendo-guest",36,-58,18,8,8
+UWSCAN:EA63DA75C62C,1,"emendo-Blackbird",36,-58,18,8,8
+UWSCAN:EA63DA75C62C,1,"emendo-Blackbird",36,-58,18,8,8
+UWSCAN:E063DA75C62C,1,"Blackbird-device",36,-58,18,8,8
+UWSCAN:D8616240D55F,1,"ClickShare-ThinkingStudio",40,-88,18,8,8
+UWSCAN:D8616240D55F,1,"ClickShare-ThinkingStudio",40,-88,18,8,8
+UWSCAN:D8616240D55F,1,"ClickShare-ThinkingStudio",40,-88,18,8,8
+UWSCAN:B4FBE4C832FD,1,"Blackbird-device",44,-86,18,8,8
+UWSCAN:B4FBE4C832FD,1,"Blackbird-device",44,-87,18,8,8
+UWSCAN:BAFBE4C832FD,1,"emendo-guest",44,-87,18,8,8
+UWSCAN:BAFBE4C832FD,1,"emendo-guest",44,-87,18,8,8
+UWSCAN:BEFBE4C832FD,1,"emendo-Blackbird",44,-87,18,8,8
+UWSCAN:BEFBE4C832FD,1,"emendo-Blackbird",44,-87,18,8,8
+UWSCAN:B4FBE4C832FD,1,"Blackbird-device",44,-88,18,8,8
+UWSCAN:B4FBE4C8331A,1,"Blackbird-device",48,-70,18,8,8
+UWSCAN:BAFBE4C8331A,1,"emendo-guest",48,-71,18,8,8
+UWSCAN:BEFBE4C8331A,1,"emendo-Blackbird",48,-71,18,8,8
+UWSCAN:B4FBE4C8331A,1,"Blackbird-device",48,-71,18,8,8
+UWSCAN:BAFBE4C8331A,1,"emendo-guest",48,-71,18,8,8
+UWSCAN:BEFBE4C8331A,1,"emendo-Blackbird",48,-71,18,8,8
+UWSCAN:BAFBE4C8331A,1,"emendo-guest",48,-71,18,8,8
+UWSCAN:BEFBE4C831B2,1,"emendo-Blackbird",52,-79,18,8,8
+UWSCAN:B4FBE4C831B2,1,"Blackbird-device",52,-80,18,8,8
+UWSCAN:BAFBE4C831B2,1,"emendo-guest",52,-79,18,8,8
+UWSCAN:BEFBE4C831B2,1,"emendo-Blackbird",52,-85,18,8,8
+UWSCAN:B4FBE4C831B2,1,"Blackbird-device",52,-79,18,8,8
+UWSCAN:BAFBE4C831B2,1,"emendo-guest",52,-79,18,8,8
+UWSCAN:BEFBE4C831B2,1,"emendo-Blackbird",52,-79,18,8,8
+UWSCAN:B4FBE4C831B2,1,"Blackbird-device",52,-79,18,8,8
+UWSCAN:BAFBE4C831B2,1,"emendo-guest",52,-79,18,8,8
+UWSCAN:BEFBE4C831B2,1,"emendo-Blackbird",52,-79,18,8,8
+UWSCAN:BEFBE4C831B2,1,"emendo-Blackbird",52,-79,18,8,8
+UWSCAN:EA63DA75C515,1,"emendo-Blackbird",56,-80,18,8,8
+UWSCAN:E063DA75C38A,1,"Blackbird-device",56,-70,18,8,8
+UWSCAN:E063DA75C515,1,"Blackbird-device",56,-79,18,8,8
+UWSCAN:E263DA75C38A,1,"emendo-guest",56,-69,18,8,8
+UWSCAN:E663DA75C515,1,"emendo-guest",56,-79,18,8,8
+UWSCAN:F263DA75C38A,1,"emendo-Blackbird",56,-69,18,8,8
+UWSCAN:EA63DA75C515,1,"emendo-Blackbird",56,-79,18,8,8
+UWSCAN:E063DA75C38A,1,"Blackbird-device",56,-69,18,8,8
+UWSCAN:E063DA75C515,1,"Blackbird-device",56,-79,18,8,8
+UWSCAN:E063DA75C515,1,"Blackbird-device",56,-79,18,8,8
+UWSCAN:F263DA75C38A,1,"emendo-Blackbird",56,-69,18,8,8
+UWSCAN:E063DA75C38A,1,"Blackbird-device",56,-69,18,8,8
+UWSCAN:E063DA75C38A,1,"Blackbird-device",56,-69,18,8,8
+UWSCAN:E063DA75C38A,1,"Blackbird-device",56,-69,18,8,8
+UWSCAN:E063DA75C38A,1,"Blackbird-device",56,-69,18,8,8
+UWSCAN:E663DA75C515,1,"emendo-guest",56,-79,18,8,8
+UWSCAN:E263DA75C38A,1,"emendo-guest",56,-69,18,8,8
+UWSCAN:E263DA75C38A,1,"emendo-guest",56,-69,18,8,8
+UWSCAN:EA63DA75C515,1,"emendo-Blackbird",56,-79,18,8,8
+UWSCAN:F263DA75C38A,1,"emendo-Blackbird",56,-70,18,8,8
+UWSCAN:E063DA75C38A,1,"Blackbird-device",56,-70,18,8,8
+UWSCAN:E063DA75C515,1,"Blackbird-device",56,-79,18,8,8
+UWSCAN:E263DA75C38A,1,"emendo-guest",56,-69,18,8,8
+UWSCAN:E263DA75C38A,1,"emendo-guest",56,-69,18,8,8
+UWSCAN:F263DA75C38A,1,"emendo-Blackbird",56,-69,18,8,8
+UWSCAN:E663DA75C515,1,"emendo-guest",56,-79,18,8,8
+UWSCAN:F263DA75C38A,1,"emendo-Blackbird",56,-69,18,8,8
+UWSCAN:EA63DA75C515,1,"emendo-Blackbird",56,-79,18,8,8
+UWSCAN:EA63DA75C515,1,"emendo-Blackbird",56,-79,18,8,8
+UWSCAN:E063DA75C38A,1,"Blackbird-device",56,-69,18,8,8
+UWSCAN:E063DA75C515,1,"Blackbird-device",56,-79,18,8,8
+UWSCAN:E063DA75C515,1,"Blackbird-device",56,-79,18,8,8
+UWSCAN:E663DA75C515,1,"emendo-guest",56,-79,18,8,8
+UWSCAN:E663DA75C515,1,"emendo-guest",56,-79,18,8,8
+UWSCAN:EA63DA75C515,1,"emendo-Blackbird",56,-79,18,8,8
+UWSCAN:EA63DA75C515,1,"emendo-Blackbird",56,-79,18,8,8
+UWSCAN:EA63DA75C515,1,"emendo-Blackbird",56,-80,18,8,8
+UWSCAN:E063DA75C515,1,"Blackbird-device",56,-80,18,8,8
+UWSCAN:BEFBE4C8325C,1,"emendo-Blackbird",60,-78,18,8,8
+UWSCAN:B4FBE4C8325C,1,"Blackbird-device",60,-79,18,8,8
+UWSCAN:BAFBE4C8325C,1,"emendo-guest",60,-79,18,8,8
+UWSCAN:BEFBE4C8325C,1,"emendo-Blackbird",60,-79,18,8,8
+UWSCAN:B4FBE4C8325C,1,"Blackbird-device",60,-79,18,8,8
+UWSCAN:BAFBE4C8325C,1,"emendo-guest",60,-79,18,8,8
+UWSCAN:BAFBE4C8325C,1,"emendo-guest",60,-80,18,8,8
+UWSCAN:BEFBE4C8325C,1,"emendo-Blackbird",60,-80,18,8,8
+UWSCAN:BEFBE4C8325C,1,"emendo-Blackbird",60,-79,18,8,8
+UWSCAN:E063DA75A664,1,"Blackbird-device",64,-57,18,8,8
+UWSCAN:E663DA75A664,1,"emendo-guest",64,-58,18,8,8
+UWSCAN:EA63DA75A664,1,"emendo-Blackbird",64,-58,18,8,8
+UWSCAN:E063DA75A664,1,"Blackbird-device",64,-58,18,8,8
+UWSCAN:E663DA75A664,1,"emendo-guest",64,-57,18,8,8
+UWSCAN:E063DA75A664,1,"Blackbird-device",64,-57,18,8,8
+UWSCAN:E663DA75A664,1,"emendo-guest",64,-57,18,8,8
+UWSCAN:EA63DA75A664,1,"emendo-Blackbird",64,-58,18,8,8
+UWSCAN:E063DA75A664,1,"Blackbird-device",64,-57,18,8,8
+UWSCAN:E663DA75A664,1,"emendo-guest",64,-57,18,8,8
+UWSCAN:EA63DA75A664,1,"emendo-Blackbird",64,-58,18,8,8
+UWSCAN:E063DA75A664,1,"Blackbird-device",64,-58,18,8,8
+UWSCAN:EA63DA78C485,1,"emendo-Blackbird",100,-63,18,8,8
+UWSCAN:E063DA78C485,1,"Blackbird-device",100,-63,18,8,8
+UWSCAN:E663DA78C485,1,"emendo-guest",100,-63,18,8,8
+UWSCAN:EA63DA78C485,1,"emendo-Blackbird",100,-64,18,8,8
+UWSCAN:E063DA78C485,1,"Blackbird-device",100,-63,18,8,8
+UWSCAN:E663DA78C485,1,"emendo-guest",100,-64,18,8,8
+UWSCAN:EA63DA78C485,1,"emendo-Blackbird",100,-64,18,8,8
+UWSCAN:E663DA78C485,1,"emendo-guest",100,-64,18,8,8
+UWSCAN:E063DA78C485,1,"Blackbird-device",100,-64,18,8,8
+UWSCAN:E663DA78C485,1,"emendo-guest",100,-64,18,8,8
+UWSCAN:EA63DA78C485,1,"emendo-Blackbird",100,-64,18,8,8
+UWSCAN:EA63DA78C485,1,"emendo-Blackbird",100,-64,18,8,8
OK

After the implementation of framed queues, this could be solved using an additional ResponseHeader variant as:

pub enum ResponseHeader {
    Complete,
    Line,
    Chunk,
}

As well as the introduction of another AT wrapper type for responses, next to Streaming:

pub struct Lines<'a, A: AtatResp, const N: usize> {
    consumer: &'a FrameConsumer<'a, N>,
}


impl<'a,  A: AtatResp, const N: usize> Stream for Lines<'a,  A, N> {
    type item = A;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        match self.consumer.peek() {
            Some(Ok(ResponseHeader::Line(bytes)) => {
                unsafe { self.consumer.dequeue_unchecked(); }
                let s: A = A::parse(bytes);
                Poll::Ready(Some(s))
            }
            // The next item in the queue is a new response
            Some(Ok(ResponseHeader::Response(_)) => {
                Poll::Ready(None)
            }
            // FIXME: Is this correct?
            Some(Err(e)) => Poll::Ready(None),
            None => Poll::Pending
        }
    }
}

impl<'a,  A: AtatResp, const N: usize> Drop for Lines<'a,  A, N> {
    fn drop(&mut self) {
        // Dequeue items until there are no more `ResponseHeader::Line`
        while let Some(Ok(AtItem::Line(_)) = self.consumer.peek() {
            self.consumer.dequeue();
        }
    }
}

such that

#[derive(Clone, AtatResp)]
pub struct Wifi {
    ssid: String<64>
}

#[derive(Clone, AtatCmd)]
#[at_cmd("+USORD", Lines<'_, Wifi>)]
pub struct ScanWifi;

pub fn example_using_lines() {
    match at_client.send(&ScanWifi) {
        Err(nb::Error::WouldBlock) => {
            // This would indicate that the first part of the response has yet to be received
        }
        Err(nb::Error::Other(_)) => {
            // This would indicate that an actual error occured on the AT line, 
            // or a timeout occured while waiting for the first part of the response.
        }
        Ok(response) => {
            // `at_client` is locked for the full lifetime of `response`, 
            // but the ingress part of `ATAT` should still be ingressing and pushing to the response queue.

            loop {
                match response.poll_next() {
                    Poll::Pending => {}
                    Poll::Ready(None) => break
                    Poll::Ready(w: Wifi) => {
                        // Found wifi accesspoints can be dealt with one at a time.
                        println!("Found wifi with ssid: {:?}", w.ssid);
                    }
                }
            }
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
atat_derive Issues related to the atat_derive crate of the workspace atat Issues related to the atat crate of the workspace enhancement New feature or request rfc Request for comment serde_at Issues related to the serde_at crate of the workspace
Projects
None yet
Development

No branches or pull requests

1 participant