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
use zb_aps::Data;
use zb_core::destination::Device;
use zb_core::endpoint::Reserved;
use zb_core::{Endpoint, short_id};
use zb_hw::Metadata;
use zb_nwk::Source;
use zb_zcl::Cluster;
use zb_zdp::{CLUSTER_ID_RESPONSE_MASK, Command};
/// Correlation key for pending transceiver responses.
///
/// The coordinator stores outstanding ZCL and ZDP requests under an `Index` and
/// removes the matching entry again when a response frame arrives. The key uses
/// the addressing and protocol fields that are expected to be mirrored by the
/// response: the remote node id, endpoint, cluster id, profile id, optional
/// manufacturer code, and transaction sequence number.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Index {
/// The network short address of the remote node.
short_id: u16,
/// The endpoint used for the request/response exchange.
endpoint: Result<Endpoint, Reserved>,
/// The request cluster id used for response matching.
cluster_id: u16,
/// The application profile id used for the exchange.
profile_id: u16,
/// The optional ZCL manufacturer code used by manufacturer-specific frames.
manufacturer_code: Option<u16>,
/// The transaction sequence number of the request/response exchange.
seq: u8,
}
impl Index {
/// Create a response correlation key from normalized response-matching
/// fields.
#[must_use]
pub const fn new(
short_id: u16,
endpoint: Result<Endpoint, Reserved>,
cluster_id: u16,
profile_id: u16,
manufacturer_code: Option<u16>,
seq: u8,
) -> Self {
Self {
short_id,
endpoint,
cluster_id,
profile_id,
manufacturer_code,
seq,
}
}
/// Create the response correlation key for a sent ZCL payload.
///
/// The generated key uses the outbound APS addressing metadata together
/// with the ZCL metadata and transaction sequence number. Incoming ZCL
/// responses can then be matched by reconstructing the same key from their
/// APS and ZCL headers.
#[must_use]
pub fn from_zcl_command(
destination: Device,
seq: u8,
metadata: Metadata,
manufacturer_code: Option<u16>,
) -> Self {
Self::new(
destination.device().into(),
Ok(destination.endpoint()),
metadata.cluster_id(),
metadata.profile().into(),
manufacturer_code,
seq,
)
}
/// Create the response correlation key for a sent ZDP command.
///
/// ZDP commands are exchanged on the data endpoint and do not carry a ZCL
/// manufacturer code, so the key is built from the command id, profile, and
/// transaction sequence number.
#[must_use]
pub fn from_zdp_command(device: short_id::Device, seq: u8, metadata: Metadata) -> Self {
Self::new(
device.into(),
Ok(Endpoint::Data),
metadata.cluster_id(),
metadata.profile().into(),
None,
seq,
)
}
/// Create the response correlation key for a received ZCL frame.
///
/// The incoming frame contributes the APS and ZCL header fields, while the
/// [`Source`] contributes the remote node id that sent the response.
pub fn from_received_zcl_frame(source: Source, frame: &Data<zb_zcl::Frame<Cluster>>) -> Self {
Self::from_aps_and_zcl_headers(source.node_id(), frame.header(), frame.payload().header())
}
/// Create the response correlation key for a received ZDP response frame.
///
/// ZDP response cluster ids carry [`CLUSTER_ID_RESPONSE_MASK`]. The mask is
/// toggled away before indexing so the response matches the key that was
/// stored for the original request command.
#[must_use]
pub fn from_received_zdp_frame(source: Source, frame: &zb_zdp::Frame<Command>) -> Self {
Self::new(
source.node_id(),
Ok(Endpoint::Data),
frame.data().cluster_id() ^ CLUSTER_ID_RESPONSE_MASK,
frame.data().profile().into(),
None,
frame.seq(),
)
}
/// Build the ZCL response correlation key from APS and ZCL headers.
fn from_aps_and_zcl_headers(
short_id: u16,
aps_header: zb_aps::data::Header,
zcl_header: zb_zcl::Header,
) -> Self {
Self::new(
short_id,
aps_header.source_endpoint(),
aps_header.cluster_id(),
aps_header.profile_id(),
zcl_header.manufacturer_code(),
zcl_header.seq(),
)
}
}