/* * * 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, pub cluster: Option, pub leaf: Option, } impl GenericPath { pub fn new(endpoint: Option, cluster: Option, leaf: Option) -> 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, ToTLV)] pub struct TimedReq { pub timeout: u16, } #[derive(FromTLV, ToTLV)] pub struct StatusResp { pub status: IMStatusCode, } #[derive(FromTLV)] #[tlvargs(lifetime = "'a")] pub struct InvReq<'a> { pub suppress_response: Option, pub timed_request: Option, pub inv_requests: Option>>, } 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>, event_requests: Option, event_filters: Option, pub fabric_filtered: bool, pub dataver_filters: Option>, } 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, timed_request: Option, pub write_requests: TLVArray<'a, AttrData<'b>>, more_chunked: Option, } 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, pub attr_reports: Option>>, // TODO pub event_reports: Option, pub more_chunks: Option, pub suppress_response: Option, } 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, pub path: AttrPath, pub data: EncodeValue<'a>, } impl<'a> AttrData<'a> { pub fn new(data_ver: Option, 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( 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, pub node: Option, pub endpoint: Option, pub cluster: Option, pub attr: Option, pub list_index: Option>, } 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, cluster: Option, command: Option) -> Self { Self { path: GenericPath { endpoint, cluster, leaf: command.map(|a| a as u32), }, } } } impl FromTLV<'_> for CmdPath { fn from_tlv(cmd_path: &TLVElement) -> Result { 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, pub endpoint: u16, pub cluster: u32, } #[derive(FromTLV, ToTLV, Copy, Clone)] pub struct DataVersionFilter { pub path: ClusterPath, pub data_ver: u32, } }