462 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			462 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
/*
 | 
						|
 *
 | 
						|
 *    Copyright (c) 2020-2022 Project CHIP Authors
 | 
						|
 *
 | 
						|
 *    Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
 *    you may not use this file except in compliance with the License.
 | 
						|
 *    You may obtain a copy of the License at
 | 
						|
 *
 | 
						|
 *        http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 *
 | 
						|
 *    Unless required by applicable law or agreed to in writing, software
 | 
						|
 *    distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
 *    See the License for the specific language governing permissions and
 | 
						|
 *    limitations under the License.
 | 
						|
 */
 | 
						|
 | 
						|
use crate::{
 | 
						|
    error::Error,
 | 
						|
    tlv::{FromTLV, TLVElement, TLVWriter, TagType, ToTLV},
 | 
						|
};
 | 
						|
 | 
						|
// A generic path with endpoint, clusters, and a leaf
 | 
						|
// The leaf could be command, attribute, event
 | 
						|
#[derive(Default, Clone, Copy, Debug, PartialEq, FromTLV, ToTLV)]
 | 
						|
#[tlvargs(datatype = "list")]
 | 
						|
pub struct GenericPath {
 | 
						|
    pub endpoint: Option<u16>,
 | 
						|
    pub cluster: Option<u32>,
 | 
						|
    pub leaf: Option<u32>,
 | 
						|
}
 | 
						|
 | 
						|
impl GenericPath {
 | 
						|
    pub fn new(endpoint: Option<u16>, cluster: Option<u32>, leaf: Option<u32>) -> Self {
 | 
						|
        Self {
 | 
						|
            endpoint,
 | 
						|
            cluster,
 | 
						|
            leaf,
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /// Returns Ok, if the path is non wildcard, otherwise returns an error
 | 
						|
    pub fn not_wildcard(&self) -> Result<(u16, u32, u32), Error> {
 | 
						|
        match *self {
 | 
						|
            GenericPath {
 | 
						|
                endpoint: Some(e),
 | 
						|
                cluster: Some(c),
 | 
						|
                leaf: Some(l),
 | 
						|
            } => Ok((e, c, l)),
 | 
						|
            _ => Err(Error::Invalid),
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /// Returns true, if the path is wildcard
 | 
						|
    pub fn is_wildcard(&self) -> bool {
 | 
						|
        !matches!(
 | 
						|
            *self,
 | 
						|
            GenericPath {
 | 
						|
                endpoint: Some(_),
 | 
						|
                cluster: Some(_),
 | 
						|
                leaf: Some(_),
 | 
						|
            }
 | 
						|
        )
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
pub mod msg {
 | 
						|
 | 
						|
    use crate::{
 | 
						|
        error::Error,
 | 
						|
        interaction_model::core::IMStatusCode,
 | 
						|
        tlv::{FromTLV, TLVArray, TLVElement, TLVWriter, TagType, ToTLV},
 | 
						|
    };
 | 
						|
 | 
						|
    use super::ib::{AttrData, AttrPath, AttrResp, CmdData, DataVersionFilter};
 | 
						|
 | 
						|
    #[derive(FromTLV)]
 | 
						|
    pub struct TimedReq {
 | 
						|
        pub timeout: u16,
 | 
						|
    }
 | 
						|
 | 
						|
    #[derive(ToTLV)]
 | 
						|
    pub struct StatusResp {
 | 
						|
        pub status: IMStatusCode,
 | 
						|
    }
 | 
						|
 | 
						|
    #[derive(FromTLV)]
 | 
						|
    #[tlvargs(lifetime = "'a")]
 | 
						|
    pub struct InvReq<'a> {
 | 
						|
        pub suppress_response: Option<bool>,
 | 
						|
        pub timed_request: Option<bool>,
 | 
						|
        pub inv_requests: Option<TLVArray<'a, CmdData<'a>>>,
 | 
						|
    }
 | 
						|
 | 
						|
    pub enum InvRespTag {
 | 
						|
        SupressResponse = 0,
 | 
						|
        InvokeResponses = 1,
 | 
						|
    }
 | 
						|
 | 
						|
    pub enum InvReqTag {
 | 
						|
        SupressResponse = 0,
 | 
						|
        TimedReq = 1,
 | 
						|
        InvokeRequests = 2,
 | 
						|
    }
 | 
						|
 | 
						|
    #[derive(Default, ToTLV, FromTLV)]
 | 
						|
    #[tlvargs(lifetime = "'a")]
 | 
						|
    pub struct ReadReq<'a> {
 | 
						|
        pub attr_requests: Option<TLVArray<'a, AttrPath>>,
 | 
						|
        event_requests: Option<bool>,
 | 
						|
        event_filters: Option<bool>,
 | 
						|
        pub fabric_filtered: bool,
 | 
						|
        pub dataver_filters: Option<TLVArray<'a, DataVersionFilter>>,
 | 
						|
    }
 | 
						|
 | 
						|
    impl<'a> ReadReq<'a> {
 | 
						|
        pub fn new(fabric_filtered: bool) -> Self {
 | 
						|
            Self {
 | 
						|
                fabric_filtered,
 | 
						|
                ..Default::default()
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        pub fn set_attr_requests(mut self, requests: &'a [AttrPath]) -> Self {
 | 
						|
            self.attr_requests = Some(TLVArray::new(requests));
 | 
						|
            self
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    #[derive(ToTLV, FromTLV)]
 | 
						|
    #[tlvargs(lifetime = "'b")]
 | 
						|
    pub struct WriteReq<'a, 'b> {
 | 
						|
        pub supress_response: Option<bool>,
 | 
						|
        timed_request: Option<bool>,
 | 
						|
        pub write_requests: TLVArray<'a, AttrData<'b>>,
 | 
						|
        more_chunked: Option<bool>,
 | 
						|
    }
 | 
						|
 | 
						|
    impl<'a, 'b> WriteReq<'a, 'b> {
 | 
						|
        pub fn new(supress_response: bool, write_requests: &'a [AttrData<'b>]) -> Self {
 | 
						|
            let mut w = Self {
 | 
						|
                supress_response: None,
 | 
						|
                write_requests: TLVArray::new(write_requests),
 | 
						|
                timed_request: None,
 | 
						|
                more_chunked: None,
 | 
						|
            };
 | 
						|
            if supress_response {
 | 
						|
                w.supress_response = Some(true);
 | 
						|
            }
 | 
						|
            w
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Report Data
 | 
						|
    #[derive(FromTLV, ToTLV)]
 | 
						|
    #[tlvargs(lifetime = "'a")]
 | 
						|
    pub struct ReportDataMsg<'a> {
 | 
						|
        pub subscription_id: Option<u32>,
 | 
						|
        pub attr_reports: Option<TLVArray<'a, AttrResp<'a>>>,
 | 
						|
        // TODO
 | 
						|
        pub event_reports: Option<bool>,
 | 
						|
        pub more_chunks: Option<bool>,
 | 
						|
        pub suppress_response: Option<bool>,
 | 
						|
    }
 | 
						|
 | 
						|
    pub enum ReportDataTag {
 | 
						|
        _SubscriptionId = 0,
 | 
						|
        AttributeReports = 1,
 | 
						|
        _EventReport = 2,
 | 
						|
        _MoreChunkedMsgs = 3,
 | 
						|
        SupressResponse = 4,
 | 
						|
    }
 | 
						|
 | 
						|
    // Write Response
 | 
						|
    pub enum WriteRespTag {
 | 
						|
        WriteResponses = 0,
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
pub mod ib {
 | 
						|
    use std::fmt::Debug;
 | 
						|
 | 
						|
    use crate::{
 | 
						|
        data_model::objects::{AttrDetails, EncodeValue},
 | 
						|
        error::Error,
 | 
						|
        interaction_model::core::IMStatusCode,
 | 
						|
        tlv::{FromTLV, Nullable, TLVElement, TLVWriter, TagType, ToTLV},
 | 
						|
    };
 | 
						|
    use log::error;
 | 
						|
 | 
						|
    use super::GenericPath;
 | 
						|
 | 
						|
    // Command Response
 | 
						|
    #[derive(Clone, Copy, FromTLV, ToTLV)]
 | 
						|
    #[tlvargs(lifetime = "'a")]
 | 
						|
    pub enum InvResp<'a> {
 | 
						|
        Cmd(CmdData<'a>),
 | 
						|
        Status(CmdStatus),
 | 
						|
    }
 | 
						|
 | 
						|
    impl<'a> InvResp<'a> {
 | 
						|
        pub fn cmd_new(endpoint: u16, cluster: u32, cmd: u16, data: EncodeValue<'a>) -> Self {
 | 
						|
            Self::Cmd(CmdData::new(
 | 
						|
                CmdPath::new(Some(endpoint), Some(cluster), Some(cmd)),
 | 
						|
                data,
 | 
						|
            ))
 | 
						|
        }
 | 
						|
 | 
						|
        pub fn status_new(cmd_path: CmdPath, status: IMStatusCode, cluster_status: u16) -> Self {
 | 
						|
            Self::Status(CmdStatus {
 | 
						|
                path: cmd_path,
 | 
						|
                status: Status::new(status, cluster_status),
 | 
						|
            })
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    #[derive(FromTLV, ToTLV, Copy, Clone, PartialEq, Debug)]
 | 
						|
    pub struct CmdStatus {
 | 
						|
        path: CmdPath,
 | 
						|
        status: Status,
 | 
						|
    }
 | 
						|
 | 
						|
    impl CmdStatus {
 | 
						|
        pub fn new(path: CmdPath, status: IMStatusCode, cluster_status: u16) -> Self {
 | 
						|
            Self {
 | 
						|
                path,
 | 
						|
                status: Status {
 | 
						|
                    status,
 | 
						|
                    cluster_status,
 | 
						|
                },
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    #[derive(Debug, Clone, Copy, FromTLV, ToTLV)]
 | 
						|
    #[tlvargs(lifetime = "'a")]
 | 
						|
    pub struct CmdData<'a> {
 | 
						|
        pub path: CmdPath,
 | 
						|
        pub data: EncodeValue<'a>,
 | 
						|
    }
 | 
						|
 | 
						|
    impl<'a> CmdData<'a> {
 | 
						|
        pub fn new(path: CmdPath, data: EncodeValue<'a>) -> Self {
 | 
						|
            Self { path, data }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Status
 | 
						|
    #[derive(Debug, Clone, Copy, PartialEq, FromTLV, ToTLV)]
 | 
						|
    pub struct Status {
 | 
						|
        pub status: IMStatusCode,
 | 
						|
        pub cluster_status: u16,
 | 
						|
    }
 | 
						|
 | 
						|
    impl Status {
 | 
						|
        pub fn new(status: IMStatusCode, cluster_status: u16) -> Status {
 | 
						|
            Status {
 | 
						|
                status,
 | 
						|
                cluster_status,
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Attribute Response
 | 
						|
    #[derive(Clone, Copy, FromTLV, ToTLV, PartialEq, Debug)]
 | 
						|
    #[tlvargs(lifetime = "'a")]
 | 
						|
    pub enum AttrResp<'a> {
 | 
						|
        Status(AttrStatus),
 | 
						|
        Data(AttrData<'a>),
 | 
						|
    }
 | 
						|
 | 
						|
    impl<'a> AttrResp<'a> {
 | 
						|
        pub fn new(data_ver: u32, path: &AttrPath, data: EncodeValue<'a>) -> Self {
 | 
						|
            AttrResp::Data(AttrData::new(Some(data_ver), *path, data))
 | 
						|
        }
 | 
						|
 | 
						|
        pub fn unwrap_data(self) -> AttrData<'a> {
 | 
						|
            match self {
 | 
						|
                AttrResp::Data(d) => d,
 | 
						|
                _ => {
 | 
						|
                    panic!("No data exists");
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Attribute Data
 | 
						|
    #[derive(Clone, Copy, PartialEq, FromTLV, ToTLV, Debug)]
 | 
						|
    #[tlvargs(lifetime = "'a")]
 | 
						|
    pub struct AttrData<'a> {
 | 
						|
        pub data_ver: Option<u32>,
 | 
						|
        pub path: AttrPath,
 | 
						|
        pub data: EncodeValue<'a>,
 | 
						|
    }
 | 
						|
 | 
						|
    impl<'a> AttrData<'a> {
 | 
						|
        pub fn new(data_ver: Option<u32>, path: AttrPath, data: EncodeValue<'a>) -> Self {
 | 
						|
            Self {
 | 
						|
                data_ver,
 | 
						|
                path,
 | 
						|
                data,
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    #[derive(Debug)]
 | 
						|
    /// Operations on an Interaction Model List
 | 
						|
    pub enum ListOperation {
 | 
						|
        /// Add (append) an item to the list
 | 
						|
        AddItem,
 | 
						|
        /// Edit an item from the list
 | 
						|
        EditItem(u16),
 | 
						|
        /// Delete item from the list
 | 
						|
        DeleteItem(u16),
 | 
						|
        /// Delete the whole list
 | 
						|
        DeleteList,
 | 
						|
    }
 | 
						|
 | 
						|
    /// Attribute Lists in Attribute Data are special. Infer the correct meaning using this function
 | 
						|
    pub fn attr_list_write<F>(
 | 
						|
        attr: &AttrDetails,
 | 
						|
        data: &TLVElement,
 | 
						|
        mut f: F,
 | 
						|
    ) -> Result<(), IMStatusCode>
 | 
						|
    where
 | 
						|
        F: FnMut(ListOperation, &TLVElement) -> Result<(), IMStatusCode>,
 | 
						|
    {
 | 
						|
        if let Some(Nullable::NotNull(index)) = attr.list_index {
 | 
						|
            // If list index is valid,
 | 
						|
            //    - this is a modify item or delete item operation
 | 
						|
            if data.null().is_ok() {
 | 
						|
                // If data is NULL, delete item
 | 
						|
                f(ListOperation::DeleteItem(index), data)
 | 
						|
            } else {
 | 
						|
                f(ListOperation::EditItem(index), data)
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            if data.confirm_array().is_ok() {
 | 
						|
                // If data is list, this is either Delete List or OverWrite List operation
 | 
						|
                // in either case, we have to first delete the whole list
 | 
						|
                f(ListOperation::DeleteList, data)?;
 | 
						|
                // Now the data must be a list, that should be added item by item
 | 
						|
 | 
						|
                let container = data.enter().ok_or(Error::Invalid)?;
 | 
						|
                for d in container {
 | 
						|
                    f(ListOperation::AddItem, &d)?;
 | 
						|
                }
 | 
						|
                Ok(())
 | 
						|
            } else {
 | 
						|
                // If data is not a list, this must be an add operation
 | 
						|
                f(ListOperation::AddItem, data)
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    #[derive(Debug, Clone, Copy, PartialEq, FromTLV, ToTLV)]
 | 
						|
    pub struct AttrStatus {
 | 
						|
        path: AttrPath,
 | 
						|
        status: Status,
 | 
						|
    }
 | 
						|
 | 
						|
    impl AttrStatus {
 | 
						|
        pub fn new(path: &GenericPath, status: IMStatusCode, cluster_status: u16) -> Self {
 | 
						|
            Self {
 | 
						|
                path: AttrPath::new(path),
 | 
						|
                status: super::ib::Status::new(status, cluster_status),
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Attribute Path
 | 
						|
    #[derive(Default, Clone, Copy, Debug, PartialEq, FromTLV, ToTLV)]
 | 
						|
    #[tlvargs(datatype = "list")]
 | 
						|
    pub struct AttrPath {
 | 
						|
        pub tag_compression: Option<bool>,
 | 
						|
        pub node: Option<u64>,
 | 
						|
        pub endpoint: Option<u16>,
 | 
						|
        pub cluster: Option<u32>,
 | 
						|
        pub attr: Option<u16>,
 | 
						|
        pub list_index: Option<Nullable<u16>>,
 | 
						|
    }
 | 
						|
 | 
						|
    impl AttrPath {
 | 
						|
        pub fn new(path: &GenericPath) -> Self {
 | 
						|
            Self {
 | 
						|
                endpoint: path.endpoint,
 | 
						|
                cluster: path.cluster,
 | 
						|
                attr: path.leaf.map(|x| x as u16),
 | 
						|
                ..Default::default()
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        pub fn to_gp(&self) -> GenericPath {
 | 
						|
            GenericPath::new(self.endpoint, self.cluster, self.attr.map(|x| x as u32))
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Command Path
 | 
						|
    #[derive(Default, Debug, Copy, Clone, PartialEq)]
 | 
						|
    pub struct CmdPath {
 | 
						|
        pub path: GenericPath,
 | 
						|
    }
 | 
						|
 | 
						|
    #[macro_export]
 | 
						|
    macro_rules! cmd_path_ib {
 | 
						|
        ($endpoint:literal,$cluster:ident,$command:expr) => {{
 | 
						|
            use $crate::interaction_model::messages::{ib::CmdPath, GenericPath};
 | 
						|
            CmdPath {
 | 
						|
                path: GenericPath {
 | 
						|
                    endpoint: Some($endpoint),
 | 
						|
                    cluster: Some($cluster),
 | 
						|
                    leaf: Some($command as u32),
 | 
						|
                },
 | 
						|
            }
 | 
						|
        }};
 | 
						|
    }
 | 
						|
 | 
						|
    impl CmdPath {
 | 
						|
        pub fn new(endpoint: Option<u16>, cluster: Option<u32>, command: Option<u16>) -> Self {
 | 
						|
            Self {
 | 
						|
                path: GenericPath {
 | 
						|
                    endpoint,
 | 
						|
                    cluster,
 | 
						|
                    leaf: command.map(|a| a as u32),
 | 
						|
                },
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    impl FromTLV<'_> for CmdPath {
 | 
						|
        fn from_tlv(cmd_path: &TLVElement) -> Result<Self, Error> {
 | 
						|
            let c = CmdPath {
 | 
						|
                path: GenericPath::from_tlv(cmd_path)?,
 | 
						|
            };
 | 
						|
 | 
						|
            if c.path.leaf.is_none() {
 | 
						|
                error!("Wildcard command parameter not supported");
 | 
						|
                Err(Error::CommandNotFound)
 | 
						|
            } else {
 | 
						|
                Ok(c)
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    impl ToTLV for CmdPath {
 | 
						|
        fn to_tlv(&self, tw: &mut TLVWriter, tag_type: TagType) -> Result<(), Error> {
 | 
						|
            self.path.to_tlv(tw, tag_type)
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    #[derive(FromTLV, ToTLV, Copy, Clone)]
 | 
						|
    pub struct ClusterPath {
 | 
						|
        pub node: Option<u64>,
 | 
						|
        pub endpoint: u16,
 | 
						|
        pub cluster: u32,
 | 
						|
    }
 | 
						|
 | 
						|
    #[derive(FromTLV, ToTLV, Copy, Clone)]
 | 
						|
    pub struct DataVersionFilter {
 | 
						|
        pub path: ClusterPath,
 | 
						|
        pub data_ver: u32,
 | 
						|
    }
 | 
						|
}
 |