1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
use std::collections::{BTreeMap, BTreeSet};
use zb_core::{Cluster, Endpoint, FullAddress};
use zb_zdp::{BindReq, Destination};
use crate::{Error, LocalNode, StatusExt, Zdp};
/// Trait for sending ZDP bind requests.
pub trait Binding {
/// Bind one source endpoint and cluster to the given destination.
///
/// # Errors
///
/// Returns an [`Error`] if the ZDP request cannot be queued, transmission or reception fails,
/// the response is invalid, or it completes with a non-success ZDP status.
fn bind(
&self,
address: FullAddress,
src_endpoint: Endpoint,
cluster: Cluster,
destination: Destination,
) -> impl Future<Output = Result<(), Error>> + Send;
/// Bind multiple endpoint/cluster pairs to the same destination.
///
/// The returned map contains one result per source endpoint. If an endpoint has multiple
/// clusters, the last cluster result for that endpoint is stored.
fn bind_all(
&self,
address: FullAddress,
src_endpoint_clusters: BTreeMap<Endpoint, BTreeSet<Cluster>>,
destination: Destination,
) -> impl Future<Output = BTreeMap<Endpoint, Result<(), Error>>> + Send
where
Self: Sync,
{
async move {
let mut results = BTreeMap::new();
for (endpoint, clusters) in src_endpoint_clusters {
for cluster in clusters {
results.insert(
endpoint,
self.bind(address, endpoint, cluster, destination).await,
);
}
}
results
}
}
/// Bind matching remote endpoint output clusters to local coordinator endpoints.
///
/// This method reads the coordinator IEEE address and local simple descriptors through
/// [`LocalNode`]. For each local endpoint, it intersects that descriptor's input clusters with
/// every remote source endpoint's output clusters, then sends ZDP bind requests for the
/// matching clusters only.
///
/// The outer `Result` represents local coordinator lookup failures, such as failing to read the
/// coordinator IEEE address or local endpoint descriptors. The returned map contains per-source
/// endpoint bind results for requests that were attempted.
///
/// If several local endpoints can receive clusters from the same remote source endpoint, later
/// local endpoint results overwrite earlier results for that source endpoint in the returned
/// map.
fn bind_all_to_self(
&self,
address: FullAddress,
src_endpoint_clusters: BTreeMap<Endpoint, BTreeSet<Cluster>>,
) -> impl Future<Output = Result<BTreeMap<Endpoint, Result<(), Error>>, Error>> + Send
where
Self: LocalNode + Sync,
{
async move {
let mut results = BTreeMap::new();
let dst_address = self.get_ieee_address().await?;
for (dst_endpoint, descriptor) in self
.get_endpoints()
.await?
.into_iter()
.enumerate()
.map_while(|(index, descriptor)| {
Endpoint::try_from(u8::try_from(index.checked_add(1)?).ok()?)
.ok()
.map(|endpoint| (endpoint, descriptor))
})
{
let input_clusters: BTreeSet<_> = descriptor
.input_clusters()
.iter()
.copied()
.filter_map(|cluster| Cluster::try_from(cluster).ok())
.collect();
let mut endpoint_clusters_to_bind = BTreeMap::new();
for (src_endpoint, output_clusters) in &src_endpoint_clusters {
endpoint_clusters_to_bind.insert(
*src_endpoint,
input_clusters
.intersection(output_clusters)
.copied()
.collect(),
);
}
results.extend(
self.bind_all(
address,
endpoint_clusters_to_bind,
Destination::Extended {
address: dst_address,
endpoint: dst_endpoint,
},
)
.await,
);
}
Ok(results)
}
}
}
impl<T> Binding for T
where
T: Zdp + Sync,
{
async fn bind(
&self,
address: FullAddress,
endpoint: Endpoint,
cluster: Cluster,
destination: Destination,
) -> Result<(), Error> {
self.communicate(
address.short_id(),
BindReq::new(
address.ieee_address(),
endpoint,
cluster.into(),
destination,
),
)
.await?
.await?
.status()
.ensure_success()
}
}