data_model: A number of changes required for iOS to work
This commit is contained in:
parent
e6238ee2e6
commit
8714758ae4
12 changed files with 125 additions and 28 deletions
|
@ -35,6 +35,7 @@ fn main() {
|
|||
pid: 0x8002,
|
||||
hw_ver: 2,
|
||||
sw_ver: 1,
|
||||
serial_no: "aabbccdd".to_string(),
|
||||
};
|
||||
let dev_att = Box::new(dev_att::HardCodedDevAtt::new());
|
||||
|
||||
|
|
|
@ -17,13 +17,18 @@
|
|||
|
||||
use super::objects::*;
|
||||
use crate::error::*;
|
||||
use num_derive::FromPrimitive;
|
||||
|
||||
pub const ID: u32 = 0x0028;
|
||||
|
||||
#[derive(FromPrimitive)]
|
||||
enum Attributes {
|
||||
DMRevision = 0,
|
||||
VendorId = 2,
|
||||
ProductId = 4,
|
||||
HwVer = 7,
|
||||
SwVer = 9,
|
||||
SerialNo = 0x0f,
|
||||
}
|
||||
|
||||
pub struct BasicInfoConfig {
|
||||
|
@ -31,6 +36,16 @@ pub struct BasicInfoConfig {
|
|||
pub pid: u16,
|
||||
pub hw_ver: u16,
|
||||
pub sw_ver: u32,
|
||||
pub serial_no: String,
|
||||
}
|
||||
|
||||
fn attr_dm_rev_new() -> Result<Attribute, Error> {
|
||||
Attribute::new(
|
||||
Attributes::DMRevision as u16,
|
||||
AttrValue::Uint8(1),
|
||||
Access::RV,
|
||||
Quality::FIXED,
|
||||
)
|
||||
}
|
||||
|
||||
fn attr_vid_new(vid: u16) -> Result<Attribute, Error> {
|
||||
|
@ -69,6 +84,14 @@ fn attr_sw_ver_new(sw_ver: u32) -> Result<Attribute, Error> {
|
|||
)
|
||||
}
|
||||
|
||||
fn attr_serial_no_new(label: String) -> Result<Attribute, Error> {
|
||||
Attribute::new(
|
||||
Attributes::SerialNo as u16,
|
||||
AttrValue::Utf8(label),
|
||||
Access::RV,
|
||||
Quality::FIXED,
|
||||
)
|
||||
}
|
||||
pub struct BasicInfoCluster {
|
||||
base: Cluster,
|
||||
}
|
||||
|
@ -78,10 +101,14 @@ impl BasicInfoCluster {
|
|||
let mut cluster = Box::new(BasicInfoCluster {
|
||||
base: Cluster::new(ID)?,
|
||||
});
|
||||
cluster.base.add_attribute(attr_dm_rev_new()?)?;
|
||||
cluster.base.add_attribute(attr_vid_new(cfg.vid)?)?;
|
||||
cluster.base.add_attribute(attr_pid_new(cfg.pid)?)?;
|
||||
cluster.base.add_attribute(attr_hw_ver_new(cfg.hw_ver)?)?;
|
||||
cluster.base.add_attribute(attr_sw_ver_new(cfg.sw_ver)?)?;
|
||||
cluster
|
||||
.base
|
||||
.add_attribute(attr_serial_no_new(cfg.serial_no)?)?;
|
||||
Ok(cluster)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ impl DataModel {
|
|||
) -> Result<AttrValue, IMStatusCode> {
|
||||
let node = self.node.read().unwrap();
|
||||
let cluster = node.get_cluster(endpoint, cluster)?;
|
||||
cluster.base().read_attribute_raw(attr).map(|a| *a)
|
||||
cluster.base().read_attribute_raw(attr).map(|a| a.clone())
|
||||
}
|
||||
|
||||
// Encode a write attribute from a path that may or may not be wildcard
|
||||
|
|
|
@ -32,6 +32,11 @@ use crate::secure_channel::pake::PaseMgr;
|
|||
use std::sync::Arc;
|
||||
use std::sync::RwLockWriteGuard;
|
||||
|
||||
pub const DEV_TYPE_ROOT_NODE: DeviceType = DeviceType {
|
||||
dtype: 0x0016,
|
||||
drev: 1,
|
||||
};
|
||||
|
||||
type WriteNode<'a> = RwLockWriteGuard<'a, Box<Node>>;
|
||||
|
||||
pub fn device_type_add_root_node(
|
||||
|
@ -43,7 +48,7 @@ pub fn device_type_add_root_node(
|
|||
pase_mgr: PaseMgr,
|
||||
) -> Result<u32, Error> {
|
||||
// Add the root endpoint
|
||||
let endpoint = node.add_endpoint()?;
|
||||
let endpoint = node.add_endpoint(DEV_TYPE_ROOT_NODE)?;
|
||||
if endpoint != 0 {
|
||||
// Somehow endpoint 0 was already added, this shouldn't be the case
|
||||
return Err(Error::Invalid);
|
||||
|
@ -63,8 +68,13 @@ pub fn device_type_add_root_node(
|
|||
Ok(endpoint)
|
||||
}
|
||||
|
||||
const DEV_TYPE_ON_OFF_LIGHT: DeviceType = DeviceType {
|
||||
dtype: 0x0100,
|
||||
drev: 2,
|
||||
};
|
||||
|
||||
pub fn device_type_add_on_off_light(node: &mut WriteNode) -> Result<u32, Error> {
|
||||
let endpoint = node.add_endpoint()?;
|
||||
let endpoint = node.add_endpoint(DEV_TYPE_ON_OFF_LIGHT)?;
|
||||
node.add_cluster(endpoint, OnOffCluster::new()?)?;
|
||||
Ok(endpoint)
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ bitflags! {
|
|||
* - instead of arrays, can use linked-lists to conserve space and avoid the internal fragmentation
|
||||
*/
|
||||
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub enum AttrValue {
|
||||
Int64(i64),
|
||||
Uint8(u8),
|
||||
|
@ -96,6 +96,7 @@ pub enum AttrValue {
|
|||
Uint32(u32),
|
||||
Uint64(u64),
|
||||
Bool(bool),
|
||||
Utf8(String),
|
||||
Custom,
|
||||
}
|
||||
|
||||
|
@ -108,6 +109,7 @@ impl Debug for AttrValue {
|
|||
AttrValue::Uint32(v) => write!(f, "{:?}", *v),
|
||||
AttrValue::Uint64(v) => write!(f, "{:?}", *v),
|
||||
AttrValue::Bool(v) => write!(f, "{:?}", *v),
|
||||
AttrValue::Utf8(v) => write!(f, "{:?}", *v),
|
||||
AttrValue::Custom => write!(f, "custom-attribute"),
|
||||
}?;
|
||||
Ok(())
|
||||
|
@ -123,6 +125,7 @@ impl ToTLV for AttrValue {
|
|||
AttrValue::Uint16(v) => tw.u16(tag_type, *v),
|
||||
AttrValue::Uint32(v) => tw.u32(tag_type, *v),
|
||||
AttrValue::Uint64(v) => tw.u64(tag_type, *v),
|
||||
AttrValue::Utf8(v) => tw.utf8(tag_type, v.as_bytes()),
|
||||
_ => {
|
||||
error!("Attribute type not yet supported");
|
||||
Err(Error::AttributeNotFound)
|
||||
|
|
|
@ -87,7 +87,6 @@ pub trait ClusterType {
|
|||
pub struct Cluster {
|
||||
pub(super) id: u32,
|
||||
attributes: Vec<Attribute>,
|
||||
feature_map: Option<u32>,
|
||||
data_ver: u32,
|
||||
}
|
||||
|
||||
|
@ -96,7 +95,6 @@ impl Cluster {
|
|||
let mut c = Cluster {
|
||||
id,
|
||||
attributes: Vec::with_capacity(ATTRS_PER_CLUSTER),
|
||||
feature_map: None,
|
||||
data_ver: rand::thread_rng().gen_range(0..0xFFFFFFFF),
|
||||
};
|
||||
c.add_default_attributes()?;
|
||||
|
@ -112,22 +110,20 @@ impl Cluster {
|
|||
}
|
||||
|
||||
pub fn set_feature_map(&mut self, map: u32) -> Result<(), Error> {
|
||||
if self.feature_map.is_none() {
|
||||
self.add_attribute(Attribute::new(
|
||||
GlobalElements::FeatureMap as u16,
|
||||
AttrValue::Uint32(map),
|
||||
Access::RV,
|
||||
Quality::NONE,
|
||||
)?)?;
|
||||
} else {
|
||||
self.write_attribute_raw(GlobalElements::FeatureMap as u16, AttrValue::Uint32(map))
|
||||
.map_err(|_| Error::Invalid)?;
|
||||
}
|
||||
self.feature_map = Some(map);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_default_attributes(&mut self) -> Result<(), Error> {
|
||||
// Default feature map is 0
|
||||
self.add_attribute(Attribute::new(
|
||||
GlobalElements::FeatureMap as u16,
|
||||
AttrValue::Uint32(0),
|
||||
Access::RV,
|
||||
Quality::NONE,
|
||||
)?)?;
|
||||
|
||||
self.add_attribute(Attribute::new(
|
||||
GlobalElements::AttributeList as u16,
|
||||
AttrValue::Custom,
|
||||
|
@ -233,8 +229,7 @@ impl Cluster {
|
|||
return;
|
||||
}
|
||||
GlobalElements::FeatureMap => {
|
||||
let val = if let Some(m) = self.feature_map { m } else { 0 };
|
||||
encoder.encode(EncodeValue::Value(&val));
|
||||
encoder.encode(EncodeValue::Value(&attr.value));
|
||||
return;
|
||||
}
|
||||
_ => {
|
||||
|
@ -284,7 +279,7 @@ impl Cluster {
|
|||
) -> Result<(), IMStatusCode> {
|
||||
let a = self.get_attribute_mut(attr_id)?;
|
||||
if a.value != AttrValue::Custom {
|
||||
let mut value = a.value;
|
||||
let mut value = a.value.clone();
|
||||
value
|
||||
.update_from_tlv(data)
|
||||
.map_err(|_| IMStatusCode::Failure)?;
|
||||
|
|
|
@ -115,3 +115,9 @@ pub trait Encoder {
|
|||
/// Encode a status report
|
||||
fn encode_status(&mut self, status: IMStatusCode, cluster_status: u16);
|
||||
}
|
||||
|
||||
#[derive(ToTLV, Copy, Clone)]
|
||||
pub struct DeviceType {
|
||||
pub dtype: u16,
|
||||
pub drev: u16,
|
||||
}
|
||||
|
|
|
@ -19,17 +19,21 @@ use crate::{data_model::objects::ClusterType, error::*, interaction_model::core:
|
|||
|
||||
use std::fmt;
|
||||
|
||||
use super::DeviceType;
|
||||
|
||||
pub const CLUSTERS_PER_ENDPT: usize = 9;
|
||||
|
||||
pub struct Endpoint {
|
||||
dev_type: DeviceType,
|
||||
clusters: Vec<Box<dyn ClusterType>>,
|
||||
}
|
||||
|
||||
pub type BoxedClusters = [Box<dyn ClusterType>];
|
||||
|
||||
impl Endpoint {
|
||||
pub fn new() -> Result<Box<Endpoint>, Error> {
|
||||
pub fn new(dev_type: DeviceType) -> Result<Box<Endpoint>, Error> {
|
||||
Ok(Box::new(Endpoint {
|
||||
dev_type,
|
||||
clusters: Vec::with_capacity(CLUSTERS_PER_ENDPT),
|
||||
}))
|
||||
}
|
||||
|
@ -43,6 +47,10 @@ impl Endpoint {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_dev_type(&self) -> &DeviceType {
|
||||
&self.dev_type
|
||||
}
|
||||
|
||||
fn get_cluster_index(&self, cluster_id: u32) -> Option<usize> {
|
||||
self.clusters.iter().position(|c| c.base().id == cluster_id)
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ use crate::{
|
|||
};
|
||||
use std::fmt;
|
||||
|
||||
use super::DeviceType;
|
||||
|
||||
pub trait ChangeConsumer {
|
||||
fn endpoint_added(&self, id: u16, endpoint: &mut Endpoint) -> Result<(), Error>;
|
||||
}
|
||||
|
@ -59,13 +61,13 @@ impl Node {
|
|||
self.changes_cb = Some(consumer);
|
||||
}
|
||||
|
||||
pub fn add_endpoint(&mut self) -> Result<u32, Error> {
|
||||
pub fn add_endpoint(&mut self, dev_type: DeviceType) -> Result<u32, Error> {
|
||||
let index = self
|
||||
.endpoints
|
||||
.iter()
|
||||
.position(|x| x.is_none())
|
||||
.ok_or(Error::NoSpace)?;
|
||||
let mut endpoint = Endpoint::new()?;
|
||||
let mut endpoint = Endpoint::new(dev_type)?;
|
||||
if let Some(cb) = &self.changes_cb {
|
||||
cb.endpoint_added(index as u16, &mut endpoint)?;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ use crate::data_model::core::DataModel;
|
|||
use crate::data_model::objects::*;
|
||||
use crate::error::*;
|
||||
use crate::interaction_model::messages::GenericPath;
|
||||
use crate::tlv::{TLVWriter, TagType};
|
||||
use crate::tlv::{TLVWriter, TagType, ToTLV};
|
||||
use log::error;
|
||||
|
||||
pub const ID: u32 = 0x001D;
|
||||
|
@ -48,11 +48,28 @@ impl DescriptorCluster {
|
|||
data_model,
|
||||
base: Cluster::new(ID)?,
|
||||
});
|
||||
c.base.add_attribute(attr_devtypelist_new()?)?;
|
||||
c.base.add_attribute(attr_serverlist_new()?)?;
|
||||
c.base.add_attribute(attr_partslist_new()?)?;
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
fn encode_devtype_list(&self, tag: TagType, tw: &mut TLVWriter) {
|
||||
let path = GenericPath {
|
||||
endpoint: Some(self.endpoint_id),
|
||||
cluster: None,
|
||||
leaf: None,
|
||||
};
|
||||
let _ = tw.start_array(tag);
|
||||
let dm = self.data_model.node.read().unwrap();
|
||||
let _ = dm.for_each_endpoint(&path, |_, e| {
|
||||
let dev_type = e.get_dev_type();
|
||||
let _ = dev_type.to_tlv(tw, TagType::Anonymous);
|
||||
Ok(())
|
||||
});
|
||||
let _ = tw.end_container();
|
||||
}
|
||||
|
||||
fn encode_server_list(&self, tag: TagType, tw: &mut TLVWriter) {
|
||||
let path = GenericPath {
|
||||
endpoint: Some(self.endpoint_id),
|
||||
|
@ -69,8 +86,24 @@ impl DescriptorCluster {
|
|||
}
|
||||
|
||||
fn encode_parts_list(&self, tag: TagType, tw: &mut TLVWriter) {
|
||||
// TODO: Support Partslist
|
||||
let path = GenericPath {
|
||||
endpoint: None,
|
||||
cluster: None,
|
||||
leaf: None,
|
||||
};
|
||||
let _ = tw.start_array(tag);
|
||||
if self.endpoint_id == 0 {
|
||||
// TODO: If endpoint is another than 0, need to figure out what to do
|
||||
let dm = self.data_model.node.read().unwrap();
|
||||
let _ = dm.for_each_endpoint(&path, |current_path, _| {
|
||||
if let Some(endpoint_id) = current_path.endpoint {
|
||||
if endpoint_id != 0 {
|
||||
let _ = tw.u16(TagType::Anonymous, endpoint_id);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
let _ = tw.end_container();
|
||||
}
|
||||
}
|
||||
|
@ -85,6 +118,9 @@ impl ClusterType for DescriptorCluster {
|
|||
|
||||
fn read_custom_attribute(&self, encoder: &mut dyn Encoder, attr: &AttrDetails) {
|
||||
match num::FromPrimitive::from_u16(attr.attr_id) {
|
||||
Some(Attributes::DeviceTypeList) => encoder.encode(EncodeValue::Closure(&|tag, tw| {
|
||||
self.encode_devtype_list(tag, tw)
|
||||
})),
|
||||
Some(Attributes::ServerList) => encoder.encode(EncodeValue::Closure(&|tag, tw| {
|
||||
self.encode_server_list(tag, tw)
|
||||
})),
|
||||
|
@ -98,6 +134,14 @@ impl ClusterType for DescriptorCluster {
|
|||
}
|
||||
}
|
||||
|
||||
fn attr_devtypelist_new() -> Result<Attribute, Error> {
|
||||
Attribute::new(
|
||||
Attributes::DeviceTypeList as u16,
|
||||
AttrValue::Custom,
|
||||
Access::RV,
|
||||
Quality::NONE,
|
||||
)
|
||||
}
|
||||
fn attr_serverlist_new() -> Result<Attribute, Error> {
|
||||
Attribute::new(
|
||||
Attributes::ServerList as u16,
|
||||
|
|
|
@ -94,6 +94,7 @@ impl ImEngine {
|
|||
pid: 11,
|
||||
hw_ver: 12,
|
||||
sw_ver: 13,
|
||||
serial_no: "aabbccdd".to_string(),
|
||||
};
|
||||
let dev_att = Box::new(DummyDevAtt {});
|
||||
let fabric_mgr = Arc::new(FabricMgr::new().unwrap());
|
||||
|
|
|
@ -204,10 +204,10 @@ fn read_cluster_id_write_attr(im: &ImEngine, endpoint: u16) -> AttrValue {
|
|||
let node = im.dm.node.read().unwrap();
|
||||
let echo = node.get_cluster(endpoint, echo_cluster::ID).unwrap();
|
||||
|
||||
*echo
|
||||
.base()
|
||||
echo.base()
|
||||
.read_attribute_raw(echo_cluster::Attributes::AttWrite as u16)
|
||||
.unwrap()
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn read_cluster_id_data_ver(im: &ImEngine, endpoint: u16) -> u32 {
|
||||
|
|
Loading…
Add table
Reference in a new issue