Subscribe: Support for long-read based subscription
The following command works with this: chip-tool any subscribe-by-id 0xFFFFFFFF 0xFFFFFFFF 1 20 12344321 0xFFFF
This commit is contained in:
		
							parent
							
								
									78d14629a8
								
							
						
					
					
						commit
						d0d853d3c4
					
				
					 5 changed files with 116 additions and 67 deletions
				
			
		| 
						 | 
					@ -15,6 +15,8 @@
 | 
				
			||||||
 *    limitations under the License.
 | 
					 *    limitations under the License.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use self::subscribe::SubsCtx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{
 | 
					use super::{
 | 
				
			||||||
    cluster_basic_information::BasicInfoConfig,
 | 
					    cluster_basic_information::BasicInfoConfig,
 | 
				
			||||||
    device_types::device_type_add_root_node,
 | 
					    device_types::device_type_add_root_node,
 | 
				
			||||||
| 
						 | 
					@ -31,7 +33,7 @@ use crate::{
 | 
				
			||||||
        core::{IMStatusCode, OpCode},
 | 
					        core::{IMStatusCode, OpCode},
 | 
				
			||||||
        messages::{
 | 
					        messages::{
 | 
				
			||||||
            ib::{self, AttrData, DataVersionFilter},
 | 
					            ib::{self, AttrData, DataVersionFilter},
 | 
				
			||||||
            msg::{self, InvReq, ReadReq, SubscribeReq, WriteReq},
 | 
					            msg::{self, InvReq, ReadReq, WriteReq},
 | 
				
			||||||
            GenericPath,
 | 
					            GenericPath,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        InteractionConsumer, Transaction,
 | 
					        InteractionConsumer, Transaction,
 | 
				
			||||||
| 
						 | 
					@ -320,9 +322,7 @@ impl InteractionConsumer for DataModel {
 | 
				
			||||||
            let result = match *resume {
 | 
					            let result = match *resume {
 | 
				
			||||||
                ResumeReq::Read(ref mut read) => self.handle_resume_read(read, trans, tw)?,
 | 
					                ResumeReq::Read(ref mut read) => self.handle_resume_read(read, trans, tw)?,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                ResumeReq::Subscribe(mut ctx) => {
 | 
					                ResumeReq::Subscribe(ref mut ctx) => ctx.handle_status_report(trans, tw, self)?,
 | 
				
			||||||
                    self.handle_subscription_confirm(trans, tw, &mut ctx)?
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
            trans.exch.set_data_boxed(resume);
 | 
					            trans.exch.set_data_boxed(resume);
 | 
				
			||||||
            Ok(result)
 | 
					            Ok(result)
 | 
				
			||||||
| 
						 | 
					@ -336,7 +336,7 @@ impl InteractionConsumer for DataModel {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn consume_subscribe(
 | 
					    fn consume_subscribe(
 | 
				
			||||||
        &self,
 | 
					        &self,
 | 
				
			||||||
        req: &SubscribeReq,
 | 
					        rx_buf: &[u8],
 | 
				
			||||||
        trans: &mut Transaction,
 | 
					        trans: &mut Transaction,
 | 
				
			||||||
        tw: &mut TLVWriter,
 | 
					        tw: &mut TLVWriter,
 | 
				
			||||||
    ) -> Result<(OpCode, ResponseRequired), Error> {
 | 
					    ) -> Result<(OpCode, ResponseRequired), Error> {
 | 
				
			||||||
| 
						 | 
					@ -344,7 +344,7 @@ impl InteractionConsumer for DataModel {
 | 
				
			||||||
            error!("Exchange data already set!");
 | 
					            error!("Exchange data already set!");
 | 
				
			||||||
            return Err(Error::InvalidState);
 | 
					            return Err(Error::InvalidState);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        let ctx = self.handle_subscribe_req(req, trans, tw)?;
 | 
					        let ctx = SubsCtx::new(rx_buf, trans, tw, self)?;
 | 
				
			||||||
        trans
 | 
					        trans
 | 
				
			||||||
            .exch
 | 
					            .exch
 | 
				
			||||||
            .set_data_boxed(Box::new(ResumeReq::Subscribe(ctx)));
 | 
					            .set_data_boxed(Box::new(ResumeReq::Subscribe(ctx)));
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,6 +34,7 @@ use crate::{
 | 
				
			||||||
    wb_shrink, wb_unshrink,
 | 
					    wb_shrink, wb_unshrink,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use log::error;
 | 
					use log::error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Encoder for generating a response to a read request
 | 
					/// Encoder for generating a response to a read request
 | 
				
			||||||
pub struct AttrReadEncoder<'a, 'b, 'c> {
 | 
					pub struct AttrReadEncoder<'a, 'b, 'c> {
 | 
				
			||||||
    tw: &'a mut TLVWriter<'b, 'c>,
 | 
					    tw: &'a mut TLVWriter<'b, 'c>,
 | 
				
			||||||
| 
						 | 
					@ -107,13 +108,13 @@ impl<'a, 'b, 'c> Encoder for AttrReadEncoder<'a, 'b, 'c> {
 | 
				
			||||||
pub struct ResumeReadReq {
 | 
					pub struct ResumeReadReq {
 | 
				
			||||||
    /// The Read Request Attribute Path that caused chunking, and this is the path
 | 
					    /// The Read Request Attribute Path that caused chunking, and this is the path
 | 
				
			||||||
    /// that needs to be resumed.
 | 
					    /// that needs to be resumed.
 | 
				
			||||||
    pending_req: Option<Packet<'static>>,
 | 
					    pub pending_req: Option<Packet<'static>>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// The Attribute that couldn't be encoded because our buffer got full. The next chunk
 | 
					    /// The Attribute that couldn't be encoded because our buffer got full. The next chunk
 | 
				
			||||||
    /// will start encoding from this attribute onwards.
 | 
					    /// will start encoding from this attribute onwards.
 | 
				
			||||||
    /// Note that given wildcard reads, one PendingPath in the member above can generated
 | 
					    /// Note that given wildcard reads, one PendingPath in the member above can generated
 | 
				
			||||||
    /// multiple encode paths. Hence this has to be maintained separately.
 | 
					    /// multiple encode paths. Hence this has to be maintained separately.
 | 
				
			||||||
    resume_from: Option<GenericPath>,
 | 
					    pub resume_from: Option<GenericPath>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
impl ResumeReadReq {
 | 
					impl ResumeReadReq {
 | 
				
			||||||
    pub fn new(rx_buf: &[u8], resume_from: &Option<GenericPath>) -> Result<Self, Error> {
 | 
					    pub fn new(rx_buf: &[u8], resume_from: &Option<GenericPath>) -> Result<Self, Error> {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,71 +21,122 @@ use crate::{
 | 
				
			||||||
    error::Error,
 | 
					    error::Error,
 | 
				
			||||||
    interaction_model::{
 | 
					    interaction_model::{
 | 
				
			||||||
        core::OpCode,
 | 
					        core::OpCode,
 | 
				
			||||||
        messages::msg::{self, SubscribeReq, SubscribeResp},
 | 
					        messages::{
 | 
				
			||||||
 | 
					            msg::{self, SubscribeReq, SubscribeResp},
 | 
				
			||||||
 | 
					            GenericPath,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    tlv::{TLVWriter, TagType, ToTLV},
 | 
					    tlv::{self, get_root_node_struct, FromTLV, TLVWriter, TagType, ToTLV},
 | 
				
			||||||
    transport::proto_demux::ResponseRequired,
 | 
					    transport::proto_demux::ResponseRequired,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{DataModel, Transaction};
 | 
					use super::{read::ResumeReadReq, DataModel, Transaction};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static SUBS_ID: AtomicU32 = AtomicU32::new(1);
 | 
					static SUBS_ID: AtomicU32 = AtomicU32::new(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl DataModel {
 | 
					#[derive(PartialEq)]
 | 
				
			||||||
    pub fn handle_subscribe_req(
 | 
					 | 
				
			||||||
        &self,
 | 
					 | 
				
			||||||
        req: &SubscribeReq,
 | 
					 | 
				
			||||||
        trans: &mut Transaction,
 | 
					 | 
				
			||||||
        tw: &mut TLVWriter,
 | 
					 | 
				
			||||||
    ) -> Result<SubsCtx, Error> {
 | 
					 | 
				
			||||||
        let ctx = SubsCtx {
 | 
					 | 
				
			||||||
            state: SubsState::Confirming,
 | 
					 | 
				
			||||||
            // TODO
 | 
					 | 
				
			||||||
            id: SUBS_ID.fetch_add(1, Ordering::SeqCst),
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let read_req = req.to_read_req();
 | 
					 | 
				
			||||||
        tw.start_struct(TagType::Anonymous)?;
 | 
					 | 
				
			||||||
        tw.u32(
 | 
					 | 
				
			||||||
            TagType::Context(msg::ReportDataTag::SubscriptionId as u8),
 | 
					 | 
				
			||||||
            ctx.id,
 | 
					 | 
				
			||||||
        )?;
 | 
					 | 
				
			||||||
        let mut resume_from = None;
 | 
					 | 
				
			||||||
        self.handle_read_attr_array(&read_req, trans, tw, &mut resume_from)?;
 | 
					 | 
				
			||||||
        tw.end_container()?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(ctx)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn handle_subscription_confirm(
 | 
					 | 
				
			||||||
        &self,
 | 
					 | 
				
			||||||
        trans: &mut Transaction,
 | 
					 | 
				
			||||||
        tw: &mut TLVWriter,
 | 
					 | 
				
			||||||
        ctx: &mut SubsCtx,
 | 
					 | 
				
			||||||
    ) -> Result<(OpCode, ResponseRequired), Error> {
 | 
					 | 
				
			||||||
        if ctx.state != SubsState::Confirming {
 | 
					 | 
				
			||||||
            // Not relevant for us
 | 
					 | 
				
			||||||
            trans.complete();
 | 
					 | 
				
			||||||
            return Err(Error::Invalid);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        ctx.state = SubsState::Confirmed;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // TODO
 | 
					 | 
				
			||||||
        let resp = SubscribeResp::new(ctx.id, 40);
 | 
					 | 
				
			||||||
        resp.to_tlv(tw, TagType::Anonymous)?;
 | 
					 | 
				
			||||||
        trans.complete();
 | 
					 | 
				
			||||||
        Ok((OpCode::SubscriptResponse, ResponseRequired::Yes))
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(PartialEq, Clone, Copy)]
 | 
					 | 
				
			||||||
enum SubsState {
 | 
					enum SubsState {
 | 
				
			||||||
    Confirming,
 | 
					    Confirming,
 | 
				
			||||||
    Confirmed,
 | 
					    Confirmed,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone, Copy)]
 | 
					 | 
				
			||||||
pub struct SubsCtx {
 | 
					pub struct SubsCtx {
 | 
				
			||||||
    state: SubsState,
 | 
					    state: SubsState,
 | 
				
			||||||
    id: u32,
 | 
					    id: u32,
 | 
				
			||||||
 | 
					    resume_read_req: Option<ResumeReadReq>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SubsCtx {
 | 
				
			||||||
 | 
					    pub fn new(
 | 
				
			||||||
 | 
					        rx_buf: &[u8],
 | 
				
			||||||
 | 
					        trans: &mut Transaction,
 | 
				
			||||||
 | 
					        tw: &mut TLVWriter,
 | 
				
			||||||
 | 
					        dm: &DataModel,
 | 
				
			||||||
 | 
					    ) -> Result<Self, Error> {
 | 
				
			||||||
 | 
					        let root = get_root_node_struct(rx_buf)?;
 | 
				
			||||||
 | 
					        let req = SubscribeReq::from_tlv(&root)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut ctx = SubsCtx {
 | 
				
			||||||
 | 
					            state: SubsState::Confirming,
 | 
				
			||||||
 | 
					            // TODO
 | 
				
			||||||
 | 
					            id: SUBS_ID.fetch_add(1, Ordering::SeqCst),
 | 
				
			||||||
 | 
					            resume_read_req: None,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let mut resume_from = None;
 | 
				
			||||||
 | 
					        ctx.do_read(&req, trans, tw, dm, &mut resume_from)?;
 | 
				
			||||||
 | 
					        if resume_from.is_some() {
 | 
				
			||||||
 | 
					            // This is a multi-hop read transaction, remember this read request
 | 
				
			||||||
 | 
					            ctx.resume_read_req = Some(ResumeReadReq::new(rx_buf, &resume_from)?);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Ok(ctx)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn handle_status_report(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        trans: &mut Transaction,
 | 
				
			||||||
 | 
					        tw: &mut TLVWriter,
 | 
				
			||||||
 | 
					        dm: &DataModel,
 | 
				
			||||||
 | 
					    ) -> Result<(OpCode, ResponseRequired), Error> {
 | 
				
			||||||
 | 
					        if self.state != SubsState::Confirming {
 | 
				
			||||||
 | 
					            // Not relevant for us
 | 
				
			||||||
 | 
					            trans.complete();
 | 
				
			||||||
 | 
					            return Err(Error::Invalid);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Is there a previous resume read pending
 | 
				
			||||||
 | 
					        if self.resume_read_req.is_some() {
 | 
				
			||||||
 | 
					            let mut resume_read_req = self.resume_read_req.take().unwrap();
 | 
				
			||||||
 | 
					            if let Some(packet) = resume_read_req.pending_req.as_mut() {
 | 
				
			||||||
 | 
					                let rx_buf = packet.get_parsebuf()?.as_borrow_slice();
 | 
				
			||||||
 | 
					                let root = tlv::get_root_node(rx_buf)?;
 | 
				
			||||||
 | 
					                let req = SubscribeReq::from_tlv(&root)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                self.do_read(&req, trans, tw, dm, &mut resume_read_req.resume_from)?;
 | 
				
			||||||
 | 
					                if resume_read_req.resume_from.is_some() {
 | 
				
			||||||
 | 
					                    // More chunks are pending, setup resume_read_req again
 | 
				
			||||||
 | 
					                    self.resume_read_req = Some(resume_read_req);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return Ok((OpCode::ReportData, ResponseRequired::Yes));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // We are here implies that the read is now complete
 | 
				
			||||||
 | 
					        self.confirm_subscription(trans, tw)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn confirm_subscription(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        trans: &mut Transaction,
 | 
				
			||||||
 | 
					        tw: &mut TLVWriter,
 | 
				
			||||||
 | 
					    ) -> Result<(OpCode, ResponseRequired), Error> {
 | 
				
			||||||
 | 
					        self.state = SubsState::Confirmed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // TODO
 | 
				
			||||||
 | 
					        let resp = SubscribeResp::new(self.id, 40);
 | 
				
			||||||
 | 
					        resp.to_tlv(tw, TagType::Anonymous)?;
 | 
				
			||||||
 | 
					        trans.complete();
 | 
				
			||||||
 | 
					        Ok((OpCode::SubscriptResponse, ResponseRequired::Yes))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn do_read(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        req: &SubscribeReq,
 | 
				
			||||||
 | 
					        trans: &mut Transaction,
 | 
				
			||||||
 | 
					        tw: &mut TLVWriter,
 | 
				
			||||||
 | 
					        dm: &DataModel,
 | 
				
			||||||
 | 
					        resume_from: &mut Option<GenericPath>,
 | 
				
			||||||
 | 
					    ) -> Result<(), Error> {
 | 
				
			||||||
 | 
					        let read_req = req.to_read_req();
 | 
				
			||||||
 | 
					        tw.start_struct(TagType::Anonymous)?;
 | 
				
			||||||
 | 
					        tw.u32(
 | 
				
			||||||
 | 
					            TagType::Context(msg::ReportDataTag::SubscriptionId as u8),
 | 
				
			||||||
 | 
					            self.id,
 | 
				
			||||||
 | 
					        )?;
 | 
				
			||||||
 | 
					        dm.handle_read_attr_array(&read_req, trans, tw, resume_from)?;
 | 
				
			||||||
 | 
					        tw.end_container()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,9 +33,9 @@ use log::{error, info};
 | 
				
			||||||
use num;
 | 
					use num;
 | 
				
			||||||
use num_derive::FromPrimitive;
 | 
					use num_derive::FromPrimitive;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::InteractionModel;
 | 
				
			||||||
use super::Transaction;
 | 
					use super::Transaction;
 | 
				
			||||||
use super::TransactionState;
 | 
					use super::TransactionState;
 | 
				
			||||||
use super::{messages::msg::SubscribeReq, InteractionModel};
 | 
					 | 
				
			||||||
use super::{messages::msg::TimedReq, InteractionConsumer};
 | 
					use super::{messages::msg::TimedReq, InteractionConsumer};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Handle messages related to the Interation Model
 | 
					/* Handle messages related to the Interation Model
 | 
				
			||||||
| 
						 | 
					@ -107,10 +107,7 @@ impl InteractionModel {
 | 
				
			||||||
        proto_tx: &mut Packet,
 | 
					        proto_tx: &mut Packet,
 | 
				
			||||||
    ) -> Result<ResponseRequired, Error> {
 | 
					    ) -> Result<ResponseRequired, Error> {
 | 
				
			||||||
        let mut tw = TLVWriter::new(proto_tx.get_writebuf()?);
 | 
					        let mut tw = TLVWriter::new(proto_tx.get_writebuf()?);
 | 
				
			||||||
        let root = get_root_node_struct(rx_buf)?;
 | 
					        let (opcode, resp) = self.consumer.consume_subscribe(rx_buf, trans, &mut tw)?;
 | 
				
			||||||
        let req = SubscribeReq::from_tlv(&root)?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let (opcode, resp) = self.consumer.consume_subscribe(&req, trans, &mut tw)?;
 | 
					 | 
				
			||||||
        proto_tx.set_proto_opcode(opcode as u8);
 | 
					        proto_tx.set_proto_opcode(opcode as u8);
 | 
				
			||||||
        Ok(resp)
 | 
					        Ok(resp)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,7 +23,7 @@ use crate::{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use self::{
 | 
					use self::{
 | 
				
			||||||
    core::OpCode,
 | 
					    core::OpCode,
 | 
				
			||||||
    messages::msg::{InvReq, StatusResp, SubscribeReq, WriteReq},
 | 
					    messages::msg::{InvReq, StatusResp, WriteReq},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(PartialEq)]
 | 
					#[derive(PartialEq)]
 | 
				
			||||||
| 
						 | 
					@ -70,7 +70,7 @@ pub trait InteractionConsumer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn consume_subscribe(
 | 
					    fn consume_subscribe(
 | 
				
			||||||
        &self,
 | 
					        &self,
 | 
				
			||||||
        _req: &SubscribeReq,
 | 
					        _req: &[u8],
 | 
				
			||||||
        _trans: &mut Transaction,
 | 
					        _trans: &mut Transaction,
 | 
				
			||||||
        _tw: &mut TLVWriter,
 | 
					        _tw: &mut TLVWriter,
 | 
				
			||||||
    ) -> Result<(OpCode, ResponseRequired), Error>;
 | 
					    ) -> Result<(OpCode, ResponseRequired), Error>;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue