URC with length and binary content
mschnell1 opened this issue · 7 comments
Some Modems send URCs with a payload length specification followed by a stream of non transparentized bytes.
AFAIU, the ATAT "URC" Parser can't handle this.
More generally, I seem to understand that the ATAT derive macros provide handling of "streaming" (serializing / deserializing / parsing) according to the types of the elements of a struct. Here unquoted binary information can be denoted by the heapless_bytes::Bytes
type. But with same the payload length is defined by the Bytes count (when sending) and can't be denoted by another field in the message (when receiving).
To me it seems that for the case in question it would be good if it would be possible to specify an alternate parser (e.g. by an attribute macro in the appropriate Urc enum arm or in the OnReceive struct denoted by same). By that any kind of message could be handled.
Or maybe the parser can be defined to decode the the message up to length field and the binary payload after that can be received by some additional feature.
(I would be inclined to contribute to ATAT in order to implement such an enhancement. But I am relatively new to Rust and only have a faint impression on macro programming. So I would need some support on finding a starting point....)
-Michael
I found that the modem I use perfectly does what I expect:
AT+QMTPUBEX
can send a block of binary data as a payload (no quotes used here)- the URC
+QMTRCV
- when the optionalmsg_length
field is enabled - perfectly provides the binary payload (but silly, the payload is additionally wrapped in double-quotes, but quotes inside the payload are transferred normally)
(Instead of allowing for defining an optional msg_length
to be stated with +QMTRCV (and the payload still documented as type "String") they better ad provided an option for a completely alternate URC such as +QMTRVCEX that has a length an unquoted binary payload.)
When using a string type in the URC Receive struct, of course we get a parsing error, when the received payload contains a double quote.
Astonishingly I don't get a parsing error when the payload contains a Comma, LF or CR character.
(Astonishingly) I do get parsing errors, when the received payload contains characters greater or equal 128. Supposedly this is as Rust requires Strings to be UTF-8 code.
-Michael
I found that the modem I use, when doing TCP transfer rather than MQTT transfer, provides a similar answer scheme with the +QIRD
block. Other than with +QMTRECV
here the binary information is separated from the length by CR
LF
( rather than by ,
"
with MQTT). (These modems are weird :( )
Did anybody already use such TCP communication via Modem by means of ATAT ?
I found that it's possible to link one's own parser to an arm of the AtatUrc
derived Urc
enum by not deriving the OnReceived...
struct from AtatResp
.
For a test I did this:
#[derive(Clone /* , AtatResp*/)]
pub struct OnReceivedTest {
pub client_idx: u32,
pub msgid: u32,
pub topic: String<0x100>,
pub payload_len: u32,
pub payload: atat::heapless_bytes::Bytes<0x100>,
}
impl<'a> atat::serde_at::serde::Deserialize<'a> for OnReceivedTest {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: atat::serde_at::serde::Deserializer<'a>,
{
let r = OnReceivedTest {
client_idx: 0,
msgid: 0,
topic: "".into(),
payload_len: 0,
payload: atat::heapless_bytes::Bytes::<0x100>::new(),
};
Ok(r)
}
}
Now I suppose I can do the deserialize()
function to decode any data from the modem following the appropriate keyword denoted by the Urc
enum.
I wonder if / how I can call the standard deserializer to decode the fields coming in before the binary payload.
Ranting on ....
In order to use serde and the "other site" of serde provided by ATAT, for parsing the non-binary part of the +QMTRECV
urc (or similar stuff), I suppose I should do something like
#[derive(Clone, AtatResp)]
pub struct QMTRecvHeader {
pub client_idx: u32,
pub msgid: u32,
pub topic: String<0x100>,
pub payload_len: u32,
}
#[derive(Clone)]
pub struct OnReceivedQMTPayload {
pub header: QMTRecvHeader,
pub payload: atat::heapless_bytes::Bytes<0x100>,
}
impl<'a> atat::serde_at::serde::Deserialize<'a> for OnReceivedQMTPayload {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: atat::serde_at::serde::Deserializer<'a>,
{
// create a resul for testing
let mut r = OnReceivedQMTPayload {
header: QMTRecvHeader {
client_idx: 0,
msgid: 0,
topic: "".into(),
payload_len: 0,
},
payload: atat::heapless_bytes::Bytes::<0x100>::new(),
};
let visitor = ???
r.header = deserializer.deserialize_any(visitor)?;
or somehow using
atat::serde_at::deserialize_struct();
Ok(r)
}
}
Now trying to learn
- what
deserialize_struct
might do - how such a serdes
visitor
needs to be constructed, then - checking if
deserialize_any()
indeed makes sense and finally - finding out how my parser can receive the following
payload_len
bytes from ATAT. (maybe bydeserialize_bytes
? )
-Michael
I suppse the appropriate serde documentation will be needed to be adhered.
-> https://serde.rs/custom-serialization.html
I think for this it would be nice if we could provide a struct type that can be used for the field, with the general structure along https://docs.rs/tokio-util/latest/tokio_util/codec/length_delimited/