Skip to content

Commit 0f3f240

Browse files
[Scenes] SceneHandler helpers (project-chip#30245)
* Added helpers to reduce code copied over when created scene new scene handlers * Applied comments * Update src/app/clusters/scenes-server/SceneHandlerImpl.h Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> --------- Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
1 parent ae55316 commit 0f3f240

9 files changed

+350
-252
lines changed

src/app/chip_data_model.gni

+2
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ template("chip_data_model") {
163163
"${_app_root}/clusters/on-off-server/on-off-server.h",
164164
"${_app_root}/clusters/scenes-server/ExtensionFieldSets.h",
165165
"${_app_root}/clusters/scenes-server/ExtensionFieldSetsImpl.h",
166+
"${_app_root}/clusters/scenes-server/SceneHandlerImpl.h",
166167
"${_app_root}/clusters/scenes-server/SceneTable.h",
167168
"${_app_root}/clusters/scenes-server/SceneTableImpl.h",
168169
"${_app_root}/clusters/scenes-server/scenes-server.h",
@@ -278,6 +279,7 @@ template("chip_data_model") {
278279
sources += [
279280
"${_app_root}/clusters/${cluster}/${cluster}.cpp",
280281
"${_app_root}/clusters/scenes-server/ExtensionFieldSetsImpl.cpp",
282+
"${_app_root}/clusters/scenes-server/SceneHandlerImpl.cpp",
281283
"${_app_root}/clusters/scenes-server/SceneTableImpl.cpp",
282284
]
283285
} else if (cluster == "operational-state-server") {

src/app/clusters/on-off-server/on-off-server.cpp

+9-111
Original file line numberDiff line numberDiff line change
@@ -104,101 +104,15 @@ static constexpr size_t kOnOffMaxEnpointCount =
104104
EMBER_AF_ON_OFF_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT;
105105

106106
#ifdef EMBER_AF_PLUGIN_SCENES
107-
static EmberEventControl sceneHandlerEventControls[kOnOffMaxEnpointCount];
108107
static void sceneOnOffCallback(EndpointId endpoint);
108+
using OnOffEndPointPair = scenes::DefaultSceneHandlerImpl::EndpointStatePair<bool>;
109+
using OnOffTransitionTimeInterface =
110+
scenes::DefaultSceneHandlerImpl::TransitionTimeInterface<kOnOffMaxEnpointCount, EMBER_AF_ON_OFF_CLUSTER_SERVER_ENDPOINT_COUNT>;
109111

110112
class DefaultOnOffSceneHandler : public scenes::DefaultSceneHandlerImpl
111113
{
112114
public:
113-
/// @brief Struct to keep track of the desired state of the OnOff attribute between ApplyScene and
114-
/// transition time expiration
115-
struct EndpointStatePair
116-
{
117-
EndpointStatePair(EndpointId endpoint = kInvalidEndpointId, bool status = false) : mEndpoint(endpoint), mState(status) {}
118-
EndpointId mEndpoint;
119-
bool mState;
120-
};
121-
122-
/// @brief Struct holding an array of EndpointStatePair. Handles insertion, get and removal by EndpointID.
123-
/// TODO: Implement generic object to handle this boilerplate array manipulation
124-
struct StatePairBuffer
125-
{
126-
bool IsEmpty() const { return (mPairCount == 0); }
127-
128-
CHIP_ERROR FindPair(const EndpointId endpoint, uint16_t & found_index) const
129-
{
130-
VerifyOrReturnError(!IsEmpty(), CHIP_ERROR_NOT_FOUND);
131-
for (found_index = 0; found_index < mPairCount; found_index++)
132-
{
133-
if (endpoint == mStatePairBuffer[found_index].mEndpoint)
134-
{
135-
return CHIP_NO_ERROR;
136-
}
137-
}
138-
139-
return CHIP_ERROR_NOT_FOUND;
140-
}
141-
142-
CHIP_ERROR InsertPair(const EndpointStatePair & status)
143-
{
144-
uint16_t idx;
145-
CHIP_ERROR err = FindPair(status.mEndpoint, idx);
146-
147-
if (CHIP_NO_ERROR == err)
148-
{
149-
mStatePairBuffer[idx] = status;
150-
}
151-
else if (mPairCount < MAX_ENDPOINT_COUNT)
152-
{
153-
// if not found, insert at the end
154-
mStatePairBuffer[mPairCount] = status;
155-
mPairCount++;
156-
}
157-
else
158-
{
159-
return CHIP_ERROR_NO_MEMORY;
160-
}
161-
162-
return CHIP_NO_ERROR;
163-
}
164-
165-
CHIP_ERROR GetPair(const EndpointId endpoint, EndpointStatePair & status) const
166-
{
167-
uint16_t idx;
168-
ReturnErrorOnFailure(FindPair(endpoint, idx));
169-
170-
status = mStatePairBuffer[idx];
171-
return CHIP_NO_ERROR;
172-
}
173-
174-
/// @brief Removes Pair and decrements Pair count if the endpoint existed in the array
175-
/// @param endpoint : endpoint id of the pair
176-
CHIP_ERROR RemovePair(const EndpointId endpoint)
177-
{
178-
uint16_t position;
179-
VerifyOrReturnValue(CHIP_NO_ERROR == FindPair(endpoint, position), CHIP_NO_ERROR);
180-
181-
uint16_t nextPos = static_cast<uint16_t>(position + 1);
182-
uint16_t moveNum = static_cast<uint16_t>(mPairCount - nextPos);
183-
184-
// Compress array after removal, if the removed position is not the last
185-
if (moveNum)
186-
{
187-
memmove(&mStatePairBuffer[position], &mStatePairBuffer[nextPos], sizeof(EndpointStatePair) * moveNum);
188-
}
189-
190-
mPairCount--;
191-
// Clear last occupied position
192-
mStatePairBuffer[mPairCount].mEndpoint = kInvalidEndpointId;
193-
194-
return CHIP_NO_ERROR;
195-
}
196-
197-
uint16_t mPairCount;
198-
EndpointStatePair mStatePairBuffer[kOnOffMaxEnpointCount];
199-
};
200-
201-
StatePairBuffer mSceneEndpointStatePairs;
115+
DefaultSceneHandlerImpl::StatePairBuffer<bool, kOnOffMaxEnpointCount> mSceneEndpointStatePairs;
202116
// As per spec, 1 attribute is scenable in the on off cluster
203117
static constexpr uint8_t scenableAttributeCount = 1;
204118

@@ -279,7 +193,7 @@ class DefaultOnOffSceneHandler : public scenes::DefaultSceneHandlerImpl
279193
auto & decodePair = pair_iterator.GetValue();
280194
VerifyOrReturnError(decodePair.attributeID == Attributes::OnOff::Id, CHIP_ERROR_INVALID_ARGUMENT);
281195
ReturnErrorOnFailure(
282-
mSceneEndpointStatePairs.InsertPair(EndpointStatePair(endpoint, static_cast<bool>(decodePair.attributeValue))));
196+
mSceneEndpointStatePairs.InsertPair(OnOffEndPointPair(endpoint, static_cast<bool>(decodePair.attributeValue))));
283197
}
284198
// Verify that the EFS was completely read
285199
CHIP_ERROR err = pair_iterator.GetStatus();
@@ -299,38 +213,22 @@ class DefaultOnOffSceneHandler : public scenes::DefaultSceneHandlerImpl
299213
Scenes::ScenesServer::Instance().IsHandlerRegistered(endpoint, LevelControlServer::GetSceneHandler())))
300214
#endif
301215
{
302-
OnOffServer::Instance().scheduleTimerCallbackMs(sceneEventControl(endpoint), timeMs);
216+
OnOffServer::Instance().scheduleTimerCallbackMs(mTransitionTimeInterface.sceneEventControl(endpoint), timeMs);
303217
}
304218

305219
return CHIP_NO_ERROR;
306220
}
307221

308222
private:
309-
/**
310-
* @brief Configures EventControl callback when setting On Off through scenes callback
311-
*
312-
* @param[in] endpoint endpoint to start timer for
313-
* @return EmberEventControl* configured event control
314-
*/
315-
EmberEventControl * sceneEventControl(EndpointId endpoint)
316-
{
317-
EmberEventControl * controller =
318-
OnOffServer::Instance().getEventControl(endpoint, Span<EmberEventControl>(sceneHandlerEventControls));
319-
VerifyOrReturnValue(controller != nullptr, nullptr);
320-
321-
controller->endpoint = endpoint;
322-
controller->callback = &sceneOnOffCallback;
323-
324-
return controller;
325-
}
223+
OnOffTransitionTimeInterface mTransitionTimeInterface = OnOffTransitionTimeInterface(Attributes::OnOff::Id, sceneOnOffCallback);
326224
};
327225
static DefaultOnOffSceneHandler sOnOffSceneHandler;
328226

329227
static void sceneOnOffCallback(EndpointId endpoint)
330228
{
331-
DefaultOnOffSceneHandler::EndpointStatePair savedState;
229+
OnOffEndPointPair savedState;
332230
ReturnOnFailure(sOnOffSceneHandler.mSceneEndpointStatePairs.GetPair(endpoint, savedState));
333-
chip::CommandId command = (savedState.mState) ? Commands::On::Id : Commands::Off::Id;
231+
CommandId command = (savedState.mValue) ? Commands::On::Id : Commands::Off::Id;
334232
OnOffServer::Instance().setOnOffValue(endpoint, command, false);
335233
ReturnOnFailure(sOnOffSceneHandler.mSceneEndpointStatePairs.RemovePair(endpoint));
336234
}

src/app/clusters/scenes-server/BUILD.gn

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ static_library("scenes") {
2020
"ExtensionFieldSets.h",
2121
"ExtensionFieldSetsImpl.cpp",
2222
"ExtensionFieldSetsImpl.h",
23+
"SceneHandlerImpl.cpp",
24+
"SceneHandlerImpl.h",
2325
"SceneTable.h",
2426
"SceneTableImpl.cpp",
2527
"SceneTableImpl.h",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
*
3+
* Copyright (c) 2023 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+
18+
#include <app/clusters/scenes-server/SceneHandlerImpl.h>
19+
20+
namespace chip {
21+
namespace scenes {
22+
23+
CHIP_ERROR
24+
DefaultSceneHandlerImpl::EncodeAttributeValueList(const List<AttributeValuePairType> & aVlist, MutableByteSpan & serializedBytes)
25+
{
26+
TLV::TLVWriter writer;
27+
writer.Init(serializedBytes);
28+
ReturnErrorOnFailure(app::DataModel::Encode(writer, TLV::AnonymousTag(), aVlist));
29+
serializedBytes.reduce_size(writer.GetLengthWritten());
30+
31+
return CHIP_NO_ERROR;
32+
}
33+
34+
CHIP_ERROR DefaultSceneHandlerImpl::DecodeAttributeValueList(const ByteSpan & serializedBytes,
35+
DecodableList<AttributeValuePairDecodableType> & aVlist)
36+
{
37+
TLV::TLVReader reader;
38+
39+
reader.Init(serializedBytes);
40+
ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Array, TLV::AnonymousTag()));
41+
ReturnErrorOnFailure(aVlist.Decode(reader));
42+
43+
return CHIP_NO_ERROR;
44+
}
45+
46+
CHIP_ERROR
47+
DefaultSceneHandlerImpl::SerializeAdd(EndpointId endpoint, const ExtensionFieldSetDecodableType & extensionFieldSet,
48+
MutableByteSpan & serializedBytes)
49+
{
50+
AttributeValuePairType aVPairs[kMaxAvPair];
51+
52+
size_t pairTotal = 0;
53+
// Verify size of list
54+
ReturnErrorOnFailure(extensionFieldSet.attributeValueList.ComputeSize(&pairTotal));
55+
VerifyOrReturnError(pairTotal <= ArraySize(aVPairs), CHIP_ERROR_BUFFER_TOO_SMALL);
56+
57+
uint8_t pairCount = 0;
58+
auto pair_iterator = extensionFieldSet.attributeValueList.begin();
59+
while (pair_iterator.Next())
60+
{
61+
aVPairs[pairCount] = pair_iterator.GetValue();
62+
pairCount++;
63+
}
64+
ReturnErrorOnFailure(pair_iterator.GetStatus());
65+
List<AttributeValuePairType> attributeValueList(aVPairs, pairCount);
66+
67+
return EncodeAttributeValueList(attributeValueList, serializedBytes);
68+
}
69+
70+
CHIP_ERROR DefaultSceneHandlerImpl::Deserialize(EndpointId endpoint, ClusterId cluster, const ByteSpan & serializedBytes,
71+
ExtensionFieldSetType & extensionFieldSet)
72+
{
73+
DecodableList<AttributeValuePairDecodableType> attributeValueList;
74+
75+
ReturnErrorOnFailure(DecodeAttributeValueList(serializedBytes, attributeValueList));
76+
77+
// Verify size of list
78+
size_t pairTotal = 0;
79+
ReturnErrorOnFailure(attributeValueList.ComputeSize(&pairTotal));
80+
VerifyOrReturnError(pairTotal <= ArraySize(mAVPairs), CHIP_ERROR_BUFFER_TOO_SMALL);
81+
82+
uint8_t pairCount = 0;
83+
auto pair_iterator = attributeValueList.begin();
84+
while (pair_iterator.Next())
85+
{
86+
mAVPairs[pairCount] = pair_iterator.GetValue();
87+
pairCount++;
88+
};
89+
ReturnErrorOnFailure(pair_iterator.GetStatus());
90+
91+
extensionFieldSet.clusterID = cluster;
92+
extensionFieldSet.attributeValueList = mAVPairs;
93+
extensionFieldSet.attributeValueList.reduce_size(pairCount);
94+
95+
return CHIP_NO_ERROR;
96+
}
97+
98+
} // namespace scenes
99+
} // namespace chip

0 commit comments

Comments
 (0)