ReadRequest: Chunked Messages - Part 1

- Create the structure so that when the WriteBuf gets full, we return back with MoreChunkedMessages note
- When the WriteBuf gets full, we also make a note of the current attribute, so that we can resume from this attribute
onwards while sending the next chunk
This commit is contained in:
Kedar Sovani 2023-02-18 14:50:12 +05:30
parent 1807c606a0
commit 60ded6d5fb
3 changed files with 62 additions and 17 deletions

View file

@ -153,14 +153,21 @@ impl DataModel {
}
}
// Encode a read attribute from a path that may or may not be wildcard
/// Encode a read attribute from a path that may or may not be wildcard
///
/// If the buffer gets full while generating the read response, we will return
/// an Err(path), where the path is the path that we should resume from, for the next chunk.
/// This facilitates chunk management
fn handle_read_attr_path(
node: &Node,
accessor: &Accessor,
attr_encoder: &mut AttrReadEncoder,
attr_details: &mut AttrDetails,
) {
resume_from: &mut Option<GenericPath>,
) -> Result<(), GenericPath> {
let mut status = Ok(());
let path = attr_encoder.path;
// Skip error reporting for wildcard paths, don't for concrete paths
attr_encoder.skip_error(path.is_wildcard());
@ -174,6 +181,16 @@ impl DataModel {
return Ok(());
}
if let Some(r) = resume_from {
// If resume_from is valid, and we haven't hit the resume_from yet, skip encoding
if r != path {
return Ok(());
} else {
// Else, wipe out the resume_from so subsequent paths can be encoded
*resume_from = None;
}
}
attr_details.attr_id = path.leaf.unwrap_or_default() as u16;
// Overwrite the previous path with the concrete path
attr_encoder.set_path(*path);
@ -181,12 +198,17 @@ impl DataModel {
attr_encoder.set_data_ver(cluster_data_ver);
let mut access_req = AccessReq::new(accessor, path, Access::READ);
Cluster::read_attribute(c, &mut access_req, attr_encoder, attr_details);
if attr_encoder.is_buffer_full() {
// Buffer is full, next time resume from this attribute
status = Err(*path);
}
Ok(())
});
if let Err(e) = result {
// We hit this only if this is a non-wildcard path
attr_encoder.encode_status(e, 0);
}
status
}
// Handle command from a path that may or may not be wildcard
@ -320,19 +342,42 @@ impl InteractionConsumer for DataModel {
.tw
.start_array(TagType::Context(msg::ReportDataTag::AttributeReports as u8))?;
let mut result = Ok(());
let mut resume_from = None;
for attr_path in attr_requests.iter() {
attr_encoder.set_path(attr_path.to_gp());
// Extract the attr_path fields into various structures
attr_details.list_index = attr_path.list_index;
attr_details.fab_idx = accessor.fab_idx;
DataModel::handle_read_attr_path(
result = DataModel::handle_read_attr_path(
&node,
&accessor,
&mut attr_encoder,
&mut attr_details,
&mut resume_from,
);
if result.is_err() {
break;
}
}
tw.end_container()?;
if let Err(path) = result {
// If there was an error, make a note of this 'path' to resume for the next chunk
tw.bool(
TagType::Context(msg::ReportDataTag::MoreChunkedMsgs as u8),
true,
)?;
tw.bool(
TagType::Context(msg::ReportDataTag::SupressResponse as u8),
false,
)?;
} else {
tw.bool(
TagType::Context(msg::ReportDataTag::SupressResponse as u8),
true,
)?;
trans.complete();
}
}
Ok(())
}

View file

@ -223,7 +223,7 @@ pub mod msg {
SubscriptionId = 0,
AttributeReports = 1,
_EventReport = 2,
_MoreChunkedMsgs = 3,
MoreChunkedMsgs = 3,
SupressResponse = 4,
}

View file

@ -20,12 +20,11 @@ use crate::{
interaction_model::core::OpCode,
tlv::{get_root_node_struct, FromTLV, TLVWriter, TagType},
transport::{packet::Packet, proto_demux::ResponseRequired},
utils::writebuf::WriteBuf,
wb_shrink, wb_unshrink,
};
use super::{
messages::msg::{self, ReadReq},
InteractionModel, Transaction,
};
use super::{messages::msg::ReadReq, InteractionModel, Transaction};
impl InteractionModel {
pub fn handle_read_req(
@ -35,21 +34,22 @@ impl InteractionModel {
proto_tx: &mut Packet,
) -> Result<ResponseRequired, Error> {
proto_tx.set_proto_opcode(OpCode::ReportData as u8);
let mut tw = TLVWriter::new(proto_tx.get_writebuf()?);
// We have to do these gymnastics because we have to reserve some bytes towards the
// end of the slice for adding our terminating TLVs
const RESERVE_SIZE: usize = 8;
let proto_tx_wb = proto_tx.get_writebuf()?;
let mut child_wb = wb_shrink!(proto_tx_wb, RESERVE_SIZE);
let mut tw = TLVWriter::new(&mut child_wb);
let root = get_root_node_struct(rx_buf)?;
let read_req = ReadReq::from_tlv(&root)?;
tw.start_struct(TagType::Anonymous)?;
self.consumer.consume_read_attr(&read_req, trans, &mut tw)?;
// Supress response always true for read interaction
tw.bool(
TagType::Context(msg::ReportDataTag::SupressResponse as u8),
true,
)?;
tw.end_container()?;
trans.complete();
// Now that we have everything, start using the proto_tx_wb, by unshrinking it
wb_unshrink!(proto_tx_wb, child_wb);
let mut tw = TLVWriter::new(proto_tx_wb);
tw.end_container()?;
Ok(ResponseRequired::Yes)
}
}