forked from project-chip/connectedhomeip
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAttributes.h
307 lines (279 loc) · 13.9 KB
/
Attributes.h
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
/*
*
* Copyright (c) 2021 Project CHIP Authors
* All rights reserved.
*
* 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.
*/
#pragma once
#include "attributes_service/attributes_service.rpc.pb.h"
#include "pigweed/rpc_services/internal/StatusUtils.h"
#include <app-common/zap-generated/attribute-type.h>
#include <app/AppConfig.h>
#include <app/AttributeValueEncoder.h>
#include <app/InteractionModelEngine.h>
#include <app/MessageDef/AttributeReportIBs.h>
#include <app/data-model-provider/ActionReturnStatus.h>
#include <app/data-model-provider/MetadataLookup.h>
#include <app/data-model-provider/OperationTypes.h>
#include <app/data-model-provider/Provider.h>
#include <app/util/attribute-storage.h>
#include <app/util/attribute-table.h>
#include <lib/core/TLV.h>
#include <lib/core/TLVTags.h>
#include <lib/core/TLVTypes.h>
#include <platform/PlatformManager.h>
namespace chip {
namespace rpc {
// Implementation class for chip.rpc.Attributes.
class Attributes : public pw_rpc::nanopb::Attributes::Service<Attributes>
{
public:
::pw::Status Write(const chip_rpc_AttributeWrite & request, pw_protobuf_Empty & response)
{
const void * data;
DeviceLayer::StackLock lock;
switch (request.data.which_data)
{
case chip_rpc_AttributeData_data_bool_tag:
data = &request.data.data.data_bool;
break;
case chip_rpc_AttributeData_data_uint8_tag:
data = &request.data.data.data_uint8;
break;
case chip_rpc_AttributeData_data_uint16_tag:
data = &request.data.data.data_uint16;
break;
case chip_rpc_AttributeData_data_uint32_tag:
data = &request.data.data.data_uint32;
break;
case chip_rpc_AttributeData_data_int8_tag:
data = &request.data.data.data_int8;
break;
case chip_rpc_AttributeData_data_int16_tag:
data = &request.data.data.data_int16;
break;
case chip_rpc_AttributeData_data_int32_tag:
data = &request.data.data.data_int32;
break;
case chip_rpc_AttributeData_data_bytes_tag:
data = &request.data.data.data_bytes;
break;
case chip_rpc_AttributeData_data_single_tag:
data = &request.data.data.data_single;
break;
default:
return pw::Status::InvalidArgument();
}
RETURN_STATUS_IF_NOT_OK(
emberAfWriteAttribute(request.metadata.endpoint, request.metadata.cluster, request.metadata.attribute_id,
const_cast<uint8_t *>(static_cast<const uint8_t *>(data)), request.metadata.type));
return pw::OkStatus();
}
::pw::Status Read(const chip_rpc_AttributeMetadata & request, chip_rpc_AttributeData & response)
{
app::ConcreteAttributePath path(request.endpoint, request.cluster, request.attribute_id);
MutableByteSpan tlvBuffer(response.tlv_data.bytes);
PW_TRY(ReadAttributeIntoTlvBuffer(path, tlvBuffer));
response.tlv_data.size = tlvBuffer.size();
response.has_tlv_data = true;
switch (request.type)
{
case chip_rpc_AttributeType_ZCL_BOOLEAN_ATTRIBUTE_TYPE:
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_Boolean, response.data.data_bool));
response.which_data = chip_rpc_AttributeData_data_bool_tag;
break;
case chip_rpc_AttributeType_ZCL_ENUM8_ATTRIBUTE_TYPE:
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_UnsignedInteger, response.data.data_uint8));
response.which_data = chip_rpc_AttributeData_data_uint8_tag;
break;
case chip_rpc_AttributeType_ZCL_ENUM16_ATTRIBUTE_TYPE:
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_UnsignedInteger, response.data.data_uint16));
response.which_data = chip_rpc_AttributeData_data_uint16_tag;
break;
case chip_rpc_AttributeType_ZCL_INT8U_ATTRIBUTE_TYPE:
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_UnsignedInteger, response.data.data_uint8));
response.which_data = chip_rpc_AttributeData_data_uint8_tag;
break;
case chip_rpc_AttributeType_ZCL_INT16U_ATTRIBUTE_TYPE:
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_UnsignedInteger, response.data.data_uint16));
response.which_data = chip_rpc_AttributeData_data_uint16_tag;
break;
case chip_rpc_AttributeType_ZCL_INT32U_ATTRIBUTE_TYPE:
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_UnsignedInteger, response.data.data_uint32));
response.which_data = chip_rpc_AttributeData_data_uint32_tag;
break;
case chip_rpc_AttributeType_ZCL_INT8S_ATTRIBUTE_TYPE:
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_SignedInteger, response.data.data_int8));
response.which_data = chip_rpc_AttributeData_data_int8_tag;
break;
case chip_rpc_AttributeType_ZCL_INT16S_ATTRIBUTE_TYPE:
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_SignedInteger, response.data.data_int16));
response.which_data = chip_rpc_AttributeData_data_int16_tag;
break;
case chip_rpc_AttributeType_ZCL_INT32S_ATTRIBUTE_TYPE:
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_SignedInteger, response.data.data_int32));
response.which_data = chip_rpc_AttributeData_data_int32_tag;
break;
case chip_rpc_AttributeType_ZCL_SINGLE_ATTRIBUTE_TYPE:
PW_TRY(TlvBufferGetData(tlvBuffer, TLV::kTLVType_FloatingPointNumber, response.data.data_single));
response.which_data = chip_rpc_AttributeData_data_single_tag;
break;
case chip_rpc_AttributeType_ZCL_BITMAP8_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_BITMAP16_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_BITMAP32_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_ARRAY_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_BITMAP64_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT24U_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT40U_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT48U_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT56U_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT64U_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT24S_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT40S_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT48S_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT56S_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_INT64S_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_DOUBLE_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_OCTET_STRING_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_CHAR_STRING_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_LONG_OCTET_STRING_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_LONG_CHAR_STRING_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_STRUCT_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_TOD_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_DATE_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_UTC_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_EPOCH_US_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_EPOCH_S_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_SYSTIME_US_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_PERCENT_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_PERCENT100THS_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_CLUSTER_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_ATTRIB_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_FIELD_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_EVENT_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_COMMAND_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_ACTION_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_TRANS_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_NODE_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_VENDOR_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_DEVTYPE_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_FABRIC_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_GROUP_ID_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_STATUS_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_DATA_VER_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_EVENT_NO_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_ENDPOINT_NO_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_FABRIC_IDX_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_IPADR_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_IPV4ADR_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_IPV6ADR_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_IPV6PRE_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_HWADR_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_NO_DATA_ATTRIBUTE_TYPE:
case chip_rpc_AttributeType_ZCL_UNKNOWN_ATTRIBUTE_TYPE:
default:
break;
// These are currently not returned as decoded data, but can be
// decoded from the returned TLV data.
}
return pw::OkStatus();
}
private:
static constexpr uint8_t kReportContextTag = 0x01;
::pw::Status ReadAttributeIntoTlvBuffer(const app::ConcreteAttributePath & path, MutableByteSpan & tlvBuffer)
{
Access::SubjectDescriptor subjectDescriptor{ .authMode = chip::Access::AuthMode::kPase };
app::AttributeReportIBs::Builder attributeReports;
TLV::TLVWriter writer;
TLV::TLVType outer;
DeviceLayer::StackLock lock;
writer.Init(tlvBuffer);
PW_TRY(ChipErrorToPwStatus(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outer)));
PW_TRY(ChipErrorToPwStatus(attributeReports.Init(&writer, kReportContextTag)));
// TODO: this assumes a singleton data model provider
app::DataModel::Provider * provider = app::InteractionModelEngine::GetInstance()->GetDataModelProvider();
app::DataModel::ReadAttributeRequest request;
request.path = path;
request.operationFlags.Set(app::DataModel::OperationFlags::kInternal);
request.subjectDescriptor = &subjectDescriptor;
app::DataModel::ServerClusterFinder serverClusterFinder(provider);
auto info = serverClusterFinder.Find(path);
if (!info.has_value())
{
return ::pw::Status::NotFound();
}
app::AttributeValueEncoder encoder(attributeReports, subjectDescriptor, path, info->dataVersion,
false /* isFabricFiltered */, nullptr /* attributeEncodingState */);
app::DataModel::ActionReturnStatus result = provider->ReadAttribute(request, encoder);
if (!result.IsSuccess())
{
app::DataModel::ActionReturnStatus::StringStorage storage;
ChipLogError(Support, "Failed to read data: %s", result.c_str(storage));
return ::pw::Status::Internal();
}
attributeReports.EndOfContainer();
PW_TRY(ChipErrorToPwStatus(writer.EndContainer(outer)));
PW_TRY(ChipErrorToPwStatus(writer.Finalize()));
tlvBuffer.reduce_size(writer.GetLengthWritten());
return ::pw::OkStatus();
}
template <typename T>
::pw::Status TlvBufferGetData(ByteSpan tlvBuffer, TLV::TLVType expectedDataType, T & responseData)
{
TLV::TLVReader reader;
reader.Init(tlvBuffer);
// Open outer container
PW_TRY(ChipErrorToPwStatus(reader.Next(TLV::TLVType::kTLVType_Structure, TLV::AnonymousTag())));
TLV::TLVType readerRoot;
PW_TRY(ChipErrorToPwStatus(reader.EnterContainer(readerRoot)));
// Open report container
PW_TRY(ChipErrorToPwStatus(reader.Next(TLV::TLVType::kTLVType_Array, TLV::ContextTag(kReportContextTag))));
TLV::TLVType readerArray;
PW_TRY(ChipErrorToPwStatus(reader.EnterContainer(readerArray)));
// Skip first array element which is the empty array from spec 10.5.4.3
PW_TRY(ChipErrorToPwStatus(reader.Next(TLV::TLVType::kTLVType_Structure, TLV::AnonymousTag())));
// Parse the AttributeDataIB to pull out data
app::AttributeReportIB::Parser reportParser;
PW_TRY(ChipErrorToPwStatus(reportParser.Init(reader)));
app::AttributeDataIB::Parser dataParser;
PW_TRY(ChipErrorToPwStatus(reportParser.GetAttributeData(&dataParser)));
TLV::TLVReader dataReader;
PW_TRY(ChipErrorToPwStatus(dataParser.GetData(&dataReader)));
PW_TRY(CheckTlvTagAndType(&dataReader, TLV::ContextTag(0x2), expectedDataType));
PW_TRY(ChipErrorToPwStatus(dataReader.Get(responseData)));
return ::pw::OkStatus();
}
static ::pw::Status ChipErrorToPwStatus(CHIP_ERROR err)
{
if (err == CHIP_NO_ERROR)
{
return ::pw::OkStatus();
}
else if (err == CHIP_ERROR_BUFFER_TOO_SMALL)
{
return ::pw::Status::ResourceExhausted();
}
return ::pw::Status::Internal();
}
static ::pw::Status CheckTlvTagAndType(TLV::TLVReader * reader, TLV::Tag expectedTag, TLV::TLVType expectedType)
{
if (reader->GetTag() != expectedTag || reader->GetType() != expectedType)
{
return ::pw::Status::NotFound();
}
return ::pw::OkStatus();
}
};
} // namespace rpc
} // namespace chip