rs_matter/interaction_model/
messages.rs

1/*
2 *
3 *    Copyright (c) 2020-2022 Project CHIP Authors
4 *
5 *    Licensed under the Apache License, Version 2.0 (the "License");
6 *    you may not use this file except in compliance with the License.
7 *    You may obtain a copy of the License at
8 *
9 *        http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *    Unless required by applicable law or agreed to in writing, software
12 *    distributed under the License is distributed on an "AS IS" BASIS,
13 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *    See the License for the specific language governing permissions and
15 *    limitations under the License.
16 */
17
18use crate::{
19    data_model::objects::{ClusterId, EndptId},
20    error::{Error, ErrorCode},
21    tlv::{FromTLV, TLVWriter, TagType, ToTLV},
22};
23
24// A generic path with endpoint, clusters, and a leaf
25// The leaf could be command, attribute, event
26#[derive(Default, Clone, Debug, PartialEq, FromTLV, ToTLV)]
27#[tlvargs(datatype = "list")]
28pub struct GenericPath {
29    pub endpoint: Option<EndptId>,
30    pub cluster: Option<ClusterId>,
31    pub leaf: Option<u32>,
32}
33
34impl GenericPath {
35    pub fn new(endpoint: Option<EndptId>, cluster: Option<ClusterId>, leaf: Option<u32>) -> Self {
36        Self {
37            endpoint,
38            cluster,
39            leaf,
40        }
41    }
42
43    /// Returns Ok, if the path is non wildcard, otherwise returns an error
44    pub fn not_wildcard(&self) -> Result<(EndptId, ClusterId, u32), Error> {
45        match *self {
46            GenericPath {
47                endpoint: Some(e),
48                cluster: Some(c),
49                leaf: Some(l),
50            } => Ok((e, c, l)),
51            _ => Err(ErrorCode::Invalid.into()),
52        }
53    }
54    /// Returns true, if the path is wildcard
55    pub fn is_wildcard(&self) -> bool {
56        !matches!(
57            *self,
58            GenericPath {
59                endpoint: Some(_),
60                cluster: Some(_),
61                leaf: Some(_),
62            }
63        )
64    }
65}
66
67pub mod msg {
68
69    use crate::{
70        error::Error,
71        interaction_model::core::IMStatusCode,
72        tlv::{FromTLV, TLVArray, TLVWriter, TagType, ToTLV},
73    };
74
75    use super::ib::{
76        self, AttrData, AttrPath, AttrResp, AttrStatus, CmdData, DataVersionFilter, EventFilter,
77        EventPath,
78    };
79
80    #[derive(Debug, Default, FromTLV, ToTLV)]
81    #[tlvargs(lifetime = "'a")]
82    pub struct SubscribeReq<'a> {
83        pub keep_subs: bool,
84        pub min_int_floor: u16,
85        pub max_int_ceil: u16,
86        pub attr_requests: Option<TLVArray<'a, AttrPath>>,
87        event_requests: Option<TLVArray<'a, EventPath>>,
88        event_filters: Option<TLVArray<'a, EventFilter>>,
89        // The Context Tags are discontiguous for some reason
90        _dummy: Option<bool>,
91        pub fabric_filtered: bool,
92        pub dataver_filters: Option<TLVArray<'a, DataVersionFilter>>,
93    }
94
95    impl<'a> SubscribeReq<'a> {
96        pub fn new(fabric_filtered: bool, min_int_floor: u16, max_int_ceil: u16) -> Self {
97            Self {
98                fabric_filtered,
99                min_int_floor,
100                max_int_ceil,
101                ..Default::default()
102            }
103        }
104
105        pub fn set_attr_requests(mut self, requests: &'a [AttrPath]) -> Self {
106            self.attr_requests = Some(TLVArray::new(requests));
107            self
108        }
109    }
110
111    #[derive(Debug, FromTLV, ToTLV)]
112    pub struct SubscribeResp {
113        pub subs_id: u32,
114        // The Context Tags are discontiguous for some reason
115        _dummy: Option<u32>,
116        pub max_int: u16,
117    }
118
119    impl SubscribeResp {
120        pub fn new(subs_id: u32, max_int: u16) -> Self {
121            Self {
122                subs_id,
123                _dummy: None,
124                max_int,
125            }
126        }
127    }
128
129    #[derive(FromTLV, ToTLV)]
130    pub struct TimedReq {
131        pub timeout: u16,
132    }
133
134    #[derive(FromTLV, ToTLV)]
135    pub struct StatusResp {
136        pub status: IMStatusCode,
137    }
138
139    pub enum InvReqTag {
140        SupressResponse = 0,
141        TimedReq = 1,
142        InvokeRequests = 2,
143    }
144
145    #[derive(FromTLV, ToTLV)]
146    #[tlvargs(lifetime = "'a")]
147    pub struct InvReq<'a> {
148        pub suppress_response: Option<bool>,
149        pub timed_request: Option<bool>,
150        pub inv_requests: Option<TLVArray<'a, CmdData<'a>>>,
151    }
152
153    #[derive(FromTLV, ToTLV, Debug)]
154    #[tlvargs(lifetime = "'a")]
155    pub struct InvResp<'a> {
156        pub suppress_response: Option<bool>,
157        pub inv_responses: Option<TLVArray<'a, ib::InvResp<'a>>>,
158    }
159
160    // This enum is helpful when we are constructing the response
161    // step by step in incremental manner
162    pub enum InvRespTag {
163        SupressResponse = 0,
164        InvokeResponses = 1,
165    }
166
167    #[derive(Default, ToTLV, FromTLV, Debug)]
168    #[tlvargs(lifetime = "'a")]
169    pub struct ReadReq<'a> {
170        pub attr_requests: Option<TLVArray<'a, AttrPath>>,
171        event_requests: Option<TLVArray<'a, EventPath>>,
172        event_filters: Option<TLVArray<'a, EventFilter>>,
173        pub fabric_filtered: bool,
174        pub dataver_filters: Option<TLVArray<'a, DataVersionFilter>>,
175    }
176
177    impl<'a> ReadReq<'a> {
178        pub fn new(fabric_filtered: bool) -> Self {
179            Self {
180                fabric_filtered,
181                ..Default::default()
182            }
183        }
184
185        pub fn set_attr_requests(mut self, requests: &'a [AttrPath]) -> Self {
186            self.attr_requests = Some(TLVArray::new(requests));
187            self
188        }
189    }
190
191    #[derive(FromTLV, ToTLV, Debug)]
192    #[tlvargs(lifetime = "'a")]
193    pub struct WriteReq<'a> {
194        pub supress_response: Option<bool>,
195        timed_request: Option<bool>,
196        pub write_requests: TLVArray<'a, AttrData<'a>>,
197        more_chunked: Option<bool>,
198    }
199
200    impl<'a> WriteReq<'a> {
201        pub fn new(supress_response: bool, write_requests: &'a [AttrData<'a>]) -> Self {
202            let mut w = Self {
203                supress_response: None,
204                write_requests: TLVArray::new(write_requests),
205                timed_request: None,
206                more_chunked: None,
207            };
208            if supress_response {
209                w.supress_response = Some(true);
210            }
211            w
212        }
213    }
214
215    // Report Data
216    #[derive(FromTLV, ToTLV, Debug)]
217    #[tlvargs(lifetime = "'a")]
218    pub struct ReportDataMsg<'a> {
219        pub subscription_id: Option<u32>,
220        pub attr_reports: Option<TLVArray<'a, AttrResp<'a>>>,
221        // TODO
222        pub event_reports: Option<bool>,
223        pub more_chunks: Option<bool>,
224        pub suppress_response: Option<bool>,
225    }
226
227    pub enum ReportDataTag {
228        SubscriptionId = 0,
229        AttributeReports = 1,
230        _EventReport = 2,
231        MoreChunkedMsgs = 3,
232        SupressResponse = 4,
233    }
234
235    // Write Response
236    #[derive(ToTLV, FromTLV, Debug)]
237    #[tlvargs(lifetime = "'a")]
238    pub struct WriteResp<'a> {
239        pub write_responses: TLVArray<'a, AttrStatus>,
240    }
241
242    pub enum WriteRespTag {
243        WriteResponses = 0,
244    }
245}
246
247pub mod ib {
248    use core::fmt::Debug;
249
250    use crate::{
251        data_model::objects::{AttrDetails, AttrId, ClusterId, CmdId, EncodeValue, EndptId},
252        error::{Error, ErrorCode},
253        interaction_model::core::IMStatusCode,
254        tlv::{FromTLV, Nullable, TLVElement, TLVWriter, TagType, ToTLV},
255    };
256    use log::error;
257
258    use super::GenericPath;
259
260    // Command Response
261    #[derive(Clone, FromTLV, ToTLV, Debug)]
262    #[tlvargs(lifetime = "'a")]
263    pub enum InvResp<'a> {
264        Cmd(CmdData<'a>),
265        Status(CmdStatus),
266    }
267
268    impl<'a> InvResp<'a> {
269        pub fn status_new(cmd_path: CmdPath, status: IMStatusCode, cluster_status: u16) -> Self {
270            Self::Status(CmdStatus {
271                path: cmd_path,
272                status: Status::new(status, cluster_status),
273            })
274        }
275    }
276
277    impl<'a> From<CmdData<'a>> for InvResp<'a> {
278        fn from(value: CmdData<'a>) -> Self {
279            Self::Cmd(value)
280        }
281    }
282
283    pub enum InvRespTag {
284        Cmd = 0,
285        Status = 1,
286    }
287
288    impl<'a> From<CmdStatus> for InvResp<'a> {
289        fn from(value: CmdStatus) -> Self {
290            Self::Status(value)
291        }
292    }
293
294    #[derive(FromTLV, ToTLV, Clone, PartialEq, Debug)]
295    pub struct CmdStatus {
296        path: CmdPath,
297        status: Status,
298    }
299
300    impl CmdStatus {
301        pub fn new(path: CmdPath, status: IMStatusCode, cluster_status: u16) -> Self {
302            Self {
303                path,
304                status: Status {
305                    status,
306                    cluster_status,
307                },
308            }
309        }
310    }
311
312    #[derive(Debug, Clone, FromTLV, ToTLV)]
313    #[tlvargs(lifetime = "'a")]
314    pub struct CmdData<'a> {
315        pub path: CmdPath,
316        pub data: EncodeValue<'a>,
317    }
318
319    impl<'a> CmdData<'a> {
320        pub fn new(path: CmdPath, data: EncodeValue<'a>) -> Self {
321            Self { path, data }
322        }
323    }
324
325    pub enum CmdDataTag {
326        Path = 0,
327        Data = 1,
328    }
329
330    // Status
331    #[derive(Debug, Clone, PartialEq, FromTLV, ToTLV)]
332    pub struct Status {
333        pub status: IMStatusCode,
334        pub cluster_status: u16,
335    }
336
337    impl Status {
338        pub fn new(status: IMStatusCode, cluster_status: u16) -> Status {
339            Status {
340                status,
341                cluster_status,
342            }
343        }
344    }
345
346    // Attribute Response
347    #[derive(Clone, FromTLV, ToTLV, PartialEq, Debug)]
348    #[tlvargs(lifetime = "'a")]
349    pub enum AttrResp<'a> {
350        Status(AttrStatus),
351        Data(AttrData<'a>),
352    }
353
354    impl<'a> AttrResp<'a> {
355        pub fn unwrap_data(self) -> AttrData<'a> {
356            match self {
357                AttrResp::Data(d) => d,
358                _ => {
359                    panic!("No data exists");
360                }
361            }
362        }
363    }
364
365    impl<'a> From<AttrData<'a>> for AttrResp<'a> {
366        fn from(value: AttrData<'a>) -> Self {
367            Self::Data(value)
368        }
369    }
370
371    impl<'a> From<AttrStatus> for AttrResp<'a> {
372        fn from(value: AttrStatus) -> Self {
373            Self::Status(value)
374        }
375    }
376
377    pub enum AttrRespTag {
378        Status = 0,
379        Data = 1,
380    }
381
382    // Attribute Data
383    #[derive(Clone, PartialEq, FromTLV, ToTLV, Debug)]
384    #[tlvargs(lifetime = "'a")]
385    pub struct AttrData<'a> {
386        pub data_ver: Option<u32>,
387        pub path: AttrPath,
388        pub data: EncodeValue<'a>,
389    }
390
391    impl<'a> AttrData<'a> {
392        pub fn new(data_ver: Option<u32>, path: AttrPath, data: EncodeValue<'a>) -> Self {
393            Self {
394                data_ver,
395                path,
396                data,
397            }
398        }
399    }
400
401    pub enum AttrDataTag {
402        DataVer = 0,
403        Path = 1,
404        Data = 2,
405    }
406
407    #[derive(Debug)]
408    /// Operations on an Interaction Model List
409    pub enum ListOperation {
410        /// Add (append) an item to the list
411        AddItem,
412        /// Edit an item from the list
413        EditItem(u16),
414        /// Delete item from the list
415        DeleteItem(u16),
416        /// Delete the whole list
417        DeleteList,
418    }
419
420    /// Attribute Lists in Attribute Data are special. Infer the correct meaning using this function
421    pub fn attr_list_write<F>(attr: &AttrDetails, data: &TLVElement, mut f: F) -> Result<(), Error>
422    where
423        F: FnMut(ListOperation, &TLVElement) -> Result<(), Error>,
424    {
425        if let Some(Nullable::NotNull(index)) = attr.list_index {
426            // If list index is valid,
427            //    - this is a modify item or delete item operation
428            if data.null().is_ok() {
429                // If data is NULL, delete item
430                f(ListOperation::DeleteItem(index), data)
431            } else {
432                f(ListOperation::EditItem(index), data)
433            }
434        } else if data.confirm_array().is_ok() {
435            // If data is list, this is either Delete List or OverWrite List operation
436            // in either case, we have to first delete the whole list
437            f(ListOperation::DeleteList, data)?;
438            // Now the data must be a list, that should be added item by item
439
440            let container = data.enter().ok_or(ErrorCode::Invalid)?;
441            for d in container {
442                f(ListOperation::AddItem, &d)?;
443            }
444            Ok(())
445        } else {
446            // If data is not a list, this must be an add operation
447            f(ListOperation::AddItem, data)
448        }
449    }
450
451    #[derive(Debug, Clone, PartialEq, FromTLV, ToTLV)]
452    pub struct AttrStatus {
453        path: AttrPath,
454        status: Status,
455    }
456
457    impl AttrStatus {
458        pub fn new(path: &GenericPath, status: IMStatusCode, cluster_status: u16) -> Self {
459            Self {
460                path: AttrPath::new(path),
461                status: super::ib::Status::new(status, cluster_status),
462            }
463        }
464    }
465
466    // Attribute Path
467    #[derive(Default, Clone, Debug, PartialEq, FromTLV, ToTLV)]
468    #[tlvargs(datatype = "list")]
469    pub struct AttrPath {
470        pub tag_compression: Option<bool>,
471        pub node: Option<u64>,
472        pub endpoint: Option<EndptId>,
473        pub cluster: Option<ClusterId>,
474        pub attr: Option<AttrId>,
475        pub list_index: Option<Nullable<u16>>,
476    }
477
478    impl AttrPath {
479        pub fn new(path: &GenericPath) -> Self {
480            Self {
481                endpoint: path.endpoint,
482                cluster: path.cluster,
483                attr: path.leaf.map(|x| x as u16),
484                ..Default::default()
485            }
486        }
487
488        pub fn to_gp(&self) -> GenericPath {
489            GenericPath::new(self.endpoint, self.cluster, self.attr.map(|x| x as u32))
490        }
491    }
492
493    // Command Path
494    #[derive(Default, Debug, Clone, PartialEq)]
495    pub struct CmdPath {
496        pub path: GenericPath,
497    }
498
499    #[macro_export]
500    macro_rules! cmd_path_ib {
501        ($endpoint:literal,$cluster:ident,$command:expr) => {{
502            use $crate::interaction_model::messages::{ib::CmdPath, GenericPath};
503            CmdPath {
504                path: GenericPath {
505                    endpoint: Some($endpoint),
506                    cluster: Some($cluster),
507                    leaf: Some($command as u32),
508                },
509            }
510        }};
511    }
512
513    impl CmdPath {
514        pub fn new(
515            endpoint: Option<EndptId>,
516            cluster: Option<ClusterId>,
517            command: Option<CmdId>,
518        ) -> Self {
519            Self {
520                path: GenericPath {
521                    endpoint,
522                    cluster,
523                    leaf: command,
524                },
525            }
526        }
527    }
528
529    impl FromTLV<'_> for CmdPath {
530        fn from_tlv(cmd_path: &TLVElement) -> Result<Self, Error> {
531            let c = CmdPath {
532                path: GenericPath::from_tlv(cmd_path)?,
533            };
534
535            if c.path.leaf.is_none() {
536                error!("Wildcard command parameter not supported");
537                Err(ErrorCode::CommandNotFound.into())
538            } else {
539                Ok(c)
540            }
541        }
542    }
543
544    impl ToTLV for CmdPath {
545        fn to_tlv(&self, tw: &mut TLVWriter, tag_type: TagType) -> Result<(), Error> {
546            self.path.to_tlv(tw, tag_type)
547        }
548    }
549
550    #[derive(FromTLV, ToTLV, Clone, Debug)]
551    pub struct ClusterPath {
552        pub node: Option<u64>,
553        pub endpoint: EndptId,
554        pub cluster: ClusterId,
555    }
556
557    #[derive(FromTLV, ToTLV, Clone, Debug)]
558    pub struct DataVersionFilter {
559        pub path: ClusterPath,
560        pub data_ver: u32,
561    }
562
563    #[derive(FromTLV, ToTLV, Clone, Debug)]
564    #[tlvargs(datatype = "list")]
565    pub struct EventPath {
566        pub node: Option<u64>,
567        pub endpoint: Option<EndptId>,
568        pub cluster: Option<ClusterId>,
569        pub event: Option<u32>,
570        pub is_urgent: Option<bool>,
571    }
572
573    #[derive(FromTLV, ToTLV, Clone, Debug)]
574    pub struct EventFilter {
575        pub node: Option<u64>,
576        pub event_min: Option<u64>,
577    }
578}