Skip to content

Commit 78d1462

Browse files
committed
ReadReq: Multi-hop read is fully functional
1 parent dd47aa9 commit 78d1462

File tree

4 files changed

+75
-46
lines changed

4 files changed

+75
-46
lines changed

matter/src/data_model/core/mod.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use crate::{
3131
core::{IMStatusCode, OpCode},
3232
messages::{
3333
ib::{self, AttrData, DataVersionFilter},
34-
msg::{self, InvReq, ReadReq, ReportDataTag::SupressResponse, SubscribeReq, WriteReq},
34+
msg::{self, InvReq, ReadReq, SubscribeReq, WriteReq},
3535
GenericPath,
3636
},
3737
InteractionConsumer, Transaction,
@@ -266,7 +266,7 @@ impl InteractionConsumer for DataModel {
266266
let mut resume_from = None;
267267
let root = tlv::get_root_node(rx_buf)?;
268268
let req = ReadReq::from_tlv(&root)?;
269-
self.handle_read_attr_array(&req, trans, tw, &mut resume_from)?;
269+
self.handle_read_req(&req, trans, tw, &mut resume_from)?;
270270
if resume_from.is_some() {
271271
// This is a multi-hop read transaction, remember this read request
272272
let resume = read::ResumeReadReq::new(rx_buf, &resume_from)?;
@@ -275,10 +275,6 @@ impl InteractionConsumer for DataModel {
275275
return Err(Error::InvalidState);
276276
}
277277
trans.exch.set_data_boxed(Box::new(ResumeReq::Read(resume)));
278-
} else {
279-
tw.bool(TagType::Context(SupressResponse as u8), true)?;
280-
// Mark transaction complete, if not chunked
281-
trans.complete();
282278
}
283279
Ok(())
284280
}

matter/src/data_model/core/read.rs

Lines changed: 67 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,25 @@
1515
* limitations under the License.
1616
*/
1717

18-
use crate::data_model::{core::DataModel, objects::*};
19-
use crate::interaction_model::core::OpCode;
20-
use crate::tlv::FromTLV;
21-
use crate::transport::packet::Packet;
22-
use crate::transport::proto_demux::ResponseRequired;
2318
use crate::{
2419
acl::{AccessReq, Accessor},
20+
data_model::{core::DataModel, objects::*},
2521
error::*,
2622
interaction_model::{
27-
core::IMStatusCode,
23+
core::{IMStatusCode, OpCode},
2824
messages::{
2925
ib::{self, DataVersionFilter},
3026
msg::{self, ReadReq, ReportDataTag::MoreChunkedMsgs, ReportDataTag::SupressResponse},
3127
GenericPath,
3228
},
3329
Transaction,
3430
},
35-
tlv::{self, TLVArray, TLVWriter, TagType, ToTLV},
31+
tlv::{self, FromTLV, TLVArray, TLVWriter, TagType, ToTLV},
32+
transport::{packet::Packet, proto_demux::ResponseRequired},
33+
utils::writebuf::WriteBuf,
34+
wb_shrink, wb_unshrink,
3635
};
37-
36+
use log::error;
3837
/// Encoder for generating a response to a read request
3938
pub struct AttrReadEncoder<'a, 'b, 'c> {
4039
tw: &'a mut TLVWriter<'b, 'c>,
@@ -205,15 +204,27 @@ impl DataModel {
205204

206205
/// Process an array of Attribute Read Requests
207206
///
208-
/// This API returns whether the read response is chunked or not
207+
/// When the API returns the chunked read is on, if *resume_from is Some(x) otherwise
208+
/// the read is complete
209209
pub(super) fn handle_read_attr_array(
210210
&self,
211211
read_req: &ReadReq,
212212
trans: &mut Transaction,
213-
tw: &mut TLVWriter,
213+
old_tw: &mut TLVWriter,
214214
resume_from: &mut Option<GenericPath>,
215215
) -> Result<(), Error> {
216-
let mut attr_encoder = AttrReadEncoder::new(tw);
216+
let old_wb = old_tw.get_buf();
217+
// Note, this function may be called from multiple places: a) an actual read
218+
// request, a b) resumed read request, c) subscribe request or d) resumed subscribe
219+
// request. Hopefully 18 is sufficient to address all those scenarios.
220+
//
221+
// This is the amount of space we reserve for other things to be attached towards
222+
// the end
223+
const RESERVE_SIZE: usize = 18;
224+
let mut new_wb = wb_shrink!(old_wb, RESERVE_SIZE);
225+
let mut tw = TLVWriter::new(&mut new_wb);
226+
227+
let mut attr_encoder = AttrReadEncoder::new(&mut tw);
217228
if let Some(filters) = &read_req.dataver_filters {
218229
attr_encoder.set_data_ver_filters(filters);
219230
}
@@ -238,20 +249,54 @@ impl DataModel {
238249
&mut attr_details,
239250
resume_from,
240251
);
252+
if result.is_err() {
253+
break;
254+
}
241255
}
242-
tw.end_container()?;
256+
// Now that all the read reports are captured, let's use the old_tw that is
257+
// the full writebuf, and hopefully as all the necessary space to store this
258+
wb_unshrink!(old_wb, new_wb);
259+
old_tw.end_container()?; // Finish the AttrReports
260+
243261
if result.is_err() {
244262
// If there was an error, indicate chunking. The resume_read_req would have been
245-
// already populated from in the loop above.
246-
tw.bool(TagType::Context(MoreChunkedMsgs as u8), true)?;
247-
return Ok(());
263+
// already populated in the loop above.
264+
old_tw.bool(TagType::Context(MoreChunkedMsgs as u8), true)?;
265+
} else {
266+
// A None resume_from indicates no chunking
267+
*resume_from = None;
248268
}
249269
}
250-
// A None resume_from indicates no chunking
251-
*resume_from = None;
252270
Ok(())
253271
}
254272

273+
/// Handle a read request
274+
///
275+
/// This could be called from an actual read request or a resumed read request. Subscription
276+
/// requests do not come to this function.
277+
/// When the API returns the chunked read is on, if *resume_from is Some(x) otherwise
278+
/// the read is complete
279+
pub fn handle_read_req(
280+
&self,
281+
read_req: &ReadReq,
282+
trans: &mut Transaction,
283+
tw: &mut TLVWriter,
284+
resume_from: &mut Option<GenericPath>,
285+
) -> Result<(OpCode, ResponseRequired), Error> {
286+
tw.start_struct(TagType::Anonymous)?;
287+
288+
self.handle_read_attr_array(read_req, trans, tw, resume_from)?;
289+
290+
if resume_from.is_none() {
291+
tw.bool(TagType::Context(SupressResponse as u8), true)?;
292+
// Mark transaction complete, if not chunked
293+
trans.complete();
294+
}
295+
tw.end_container()?;
296+
Ok((OpCode::ReportData, ResponseRequired::Yes))
297+
}
298+
299+
/// Handle a resumed read request
255300
pub fn handle_resume_read(
256301
&self,
257302
resume_read_req: &mut ResumeReadReq,
@@ -263,16 +308,11 @@ impl DataModel {
263308
let root = tlv::get_root_node(rx_buf)?;
264309
let req = ReadReq::from_tlv(&root)?;
265310

266-
tw.start_struct(TagType::Anonymous)?;
267-
self.handle_read_attr_array(&req, trans, tw, &mut resume_read_req.resume_from)?;
268-
269-
if resume_read_req.resume_from.is_none() {
270-
tw.bool(TagType::Context(SupressResponse as u8), true)?;
271-
// Mark transaction complete, if not chunked
272-
trans.complete();
273-
}
274-
tw.end_container()?;
311+
self.handle_read_req(&req, trans, tw, &mut resume_read_req.resume_from)
312+
} else {
313+
// No pending req, is that even possible?
314+
error!("This shouldn't have happened");
315+
Ok((OpCode::Reserved, ResponseRequired::No))
275316
}
276-
Ok((OpCode::ReportData, ResponseRequired::Yes))
277317
}
278318
}

matter/src/interaction_model/read.rs

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@
1818
use crate::{
1919
error::Error,
2020
interaction_model::core::OpCode,
21-
tlv::{TLVWriter, TagType},
21+
tlv::TLVWriter,
2222
transport::{packet::Packet, proto_demux::ResponseRequired},
23-
utils::writebuf::WriteBuf,
24-
wb_shrink, wb_unshrink,
2523
};
2624

2725
use super::{InteractionModel, Transaction};
@@ -34,20 +32,11 @@ impl InteractionModel {
3432
proto_tx: &mut Packet,
3533
) -> Result<ResponseRequired, Error> {
3634
proto_tx.set_proto_opcode(OpCode::ReportData as u8);
37-
// We have to do these gymnastics because we have to reserve some bytes towards the
38-
// end of the slice for adding our terminating TLVs
39-
const RESERVE_SIZE: usize = 8;
4035
let proto_tx_wb = proto_tx.get_writebuf()?;
41-
let mut child_wb = wb_shrink!(proto_tx_wb, RESERVE_SIZE);
42-
let mut tw = TLVWriter::new(&mut child_wb);
36+
let mut tw = TLVWriter::new(proto_tx_wb);
4337

44-
tw.start_struct(TagType::Anonymous)?;
4538
self.consumer.consume_read_attr(rx_buf, trans, &mut tw)?;
4639

47-
//Now that we have everything, start using the proto_tx_wb, by unshrinking it
48-
wb_unshrink!(proto_tx_wb, child_wb);
49-
let mut tw = TLVWriter::new(proto_tx_wb);
50-
tw.end_container()?;
5140
Ok(ResponseRequired::Yes)
5241
}
5342
}

matter/src/tlv/writer.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,10 @@ impl<'a, 'b> TLVWriter<'a, 'b> {
264264
pub fn rewind_to(&mut self, anchor: usize) {
265265
self.buf.rewind_tail_to(anchor);
266266
}
267+
268+
pub fn get_buf<'c>(&'c mut self) -> &'c mut WriteBuf<'a> {
269+
self.buf
270+
}
267271
}
268272

269273
#[cfg(test)]

0 commit comments

Comments
 (0)