Skip to content

Commit 98051bc

Browse files
committed
Actually track attribute IDs associated with atomic write
1 parent 9663f30 commit 98051bc

File tree

4 files changed

+183
-118
lines changed

4 files changed

+183
-118
lines changed

src/app/clusters/thermostat-server/thermostat-server-atomic.cpp

+154-78
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ using namespace chip::app::Clusters::Thermostat::Structs;
2828
using namespace chip::app::Clusters::Globals::Structs;
2929
using namespace chip::Protocols::InteractionModel;
3030

31+
namespace {}
32+
3133
namespace chip {
3234
namespace app {
3335
namespace Clusters {
@@ -133,15 +135,13 @@ bool CountAttributeRequests(const DataModel::DecodableList<chip::AttributeId> at
133135
/// @param attributeRequests The list of requested attributes
134136
/// @param attributeStatusCount The number of attribute statuses in attributeStatuses
135137
/// @param attributeStatuses The status of each requested attribute, plus additional attributes if needed
136-
/// @param requireAll Whether the caller requires all atomic attributes to be represented in attributeRequests
137138
/// @return Status::Success if the request is valid, an error status if it is not
138139
Status BuildAttributeStatuses(const EndpointId endpoint, const DataModel::DecodableList<chip::AttributeId> attributeRequests,
139-
size_t & attributeStatusCount,
140-
Platform::ScopedMemoryBuffer<AtomicAttributeStatusStruct::Type> & attributeStatuses, bool requireAll)
140+
Platform::ScopedMemoryBufferWithSize<AtomicAttributeStatusStruct::Type> & attributeStatuses)
141141
{
142142

143143
bool requestedPresets = false, requestedSchedules = false;
144-
attributeStatusCount = 0;
144+
size_t attributeStatusCount = 0;
145145
if (!CountAttributeRequests(attributeRequests, attributeStatusCount, requestedPresets, requestedSchedules))
146146
{
147147
// We errored reading the list
@@ -152,12 +152,6 @@ Status BuildAttributeStatuses(const EndpointId endpoint, const DataModel::Decoda
152152
// List can't be empty
153153
return Status::InvalidCommand;
154154
}
155-
if (requestedPresets ^ requestedSchedules)
156-
{
157-
// Client requested presets or schedules, but not both, so we need an extra status
158-
// because we will in fact treat the atomic request as applying to both.
159-
attributeStatusCount++;
160-
}
161155
attributeStatuses.Alloc(attributeStatusCount);
162156
for (size_t i = 0; i < attributeStatusCount; ++i)
163157
{
@@ -199,64 +193,152 @@ Status BuildAttributeStatuses(const EndpointId endpoint, const DataModel::Decoda
199193
return Status::InvalidCommand;
200194
}
201195
}
202-
if (requireAll)
196+
return Status::Success;
197+
}
198+
199+
bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint, Optional<AttributeId> attributeId)
200+
{
201+
202+
uint16_t ep =
203+
emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT);
204+
205+
if (ep >= ArraySize(mAtomicWriteSessions))
203206
{
204-
if (!requestedPresets || !requestedSchedules)
207+
return false;
208+
}
209+
auto & atomicWriteSession = mAtomicWriteSessions[ep];
210+
if (atomicWriteSession.state != AtomicWriteState::Open)
211+
{
212+
return false;
213+
}
214+
if (!attributeId.HasValue())
215+
{
216+
return true;
217+
}
218+
for (size_t i = 0; i < atomicWriteSession.attributeIds.AllocatedSize(); ++i)
219+
{
220+
if (atomicWriteSession.attributeIds[i] == attributeId.Value())
205221
{
206-
return Status::InvalidInState;
222+
return true;
207223
}
208224
}
209-
else if (requestedPresets ^ requestedSchedules)
225+
return false;
226+
}
227+
228+
bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint, const Access::SubjectDescriptor & subjectDescriptor,
229+
Optional<AttributeId> attributeId)
230+
{
231+
if (!InAtomicWrite(endpoint, attributeId))
210232
{
211-
// Client requested presets or schedules, but not both, so we add the extra status
212-
attributeStatuses[index].attributeID = requestedSchedules ? Presets::Id : Schedules::Id;
213-
attributeStatuses[index].statusCode = to_underlying(Status::Success);
233+
return false;
214234
}
215-
return Status::Success;
235+
return subjectDescriptor.authMode == Access::AuthMode::kCase &&
236+
GetAtomicWriteOriginatorScopedNodeId(endpoint) == ScopedNodeId(subjectDescriptor.subject, subjectDescriptor.fabricIndex);
216237
}
217238

218-
void ThermostatAttrAccess::ResetAtomicWrite(EndpointId endpoint)
239+
bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint, CommandHandler * commandObj, Optional<AttributeId> attributeId)
219240
{
220-
auto delegate = GetDelegate(endpoint);
221-
if (delegate != nullptr)
241+
if (!InAtomicWrite(endpoint, attributeId))
222242
{
223-
delegate->ClearPendingPresetList();
243+
return false;
224244
}
225-
ClearTimer(endpoint);
226-
SetAtomicWrite(endpoint, ScopedNodeId(), AtomicWriteState::Closed);
245+
ScopedNodeId sourceNodeId = GetSourceScopedNodeId(commandObj);
246+
return GetAtomicWriteOriginatorScopedNodeId(endpoint) == sourceNodeId;
227247
}
228248

229-
bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint)
249+
bool ThermostatAttrAccess::InAtomicWrite(
250+
EndpointId endpoint, CommandHandler * commandObj,
251+
Platform::ScopedMemoryBufferWithSize<AtomicAttributeStatusStruct::Type> & attributeStatuses)
230252
{
231-
232253
uint16_t ep =
233254
emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT);
234255

235-
if (ep < ArraySize(mAtomicWriteSessions))
256+
if (ep >= ArraySize(mAtomicWriteSessions))
236257
{
237-
return mAtomicWriteSessions[ep].state == AtomicWriteState::Open;
258+
return false;
238259
}
239-
return false;
260+
auto & atomicWriteSession = mAtomicWriteSessions[ep];
261+
if (atomicWriteSession.state != AtomicWriteState::Open)
262+
{
263+
return false;
264+
}
265+
if (atomicWriteSession.attributeIds.AllocatedSize() == 0 ||
266+
atomicWriteSession.attributeIds.AllocatedSize() != attributeStatuses.AllocatedSize())
267+
{
268+
return false;
269+
}
270+
for (size_t i = 0; i < atomicWriteSession.attributeIds.AllocatedSize(); ++i)
271+
{
272+
bool hasAttribute = false;
273+
auto attributeId = atomicWriteSession.attributeIds[i];
274+
for (size_t j = 0; j < attributeStatuses.AllocatedSize(); ++j)
275+
{
276+
auto & attributeStatus = attributeStatuses[j];
277+
if (attributeStatus.attributeID == attributeId)
278+
{
279+
hasAttribute = true;
280+
break;
281+
}
282+
}
283+
if (!hasAttribute)
284+
{
285+
return false;
286+
}
287+
}
288+
return true;
240289
}
241290

242-
bool ThermostatAttrAccess::InAtomicWrite(const Access::SubjectDescriptor & subjectDescriptor, EndpointId endpoint)
291+
bool ThermostatAttrAccess::SetAtomicWrite(
292+
EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state,
293+
Platform::ScopedMemoryBufferWithSize<AtomicAttributeStatusStruct::Type> & attributeStatuses)
243294
{
244-
if (!InAtomicWrite(endpoint))
295+
uint16_t ep =
296+
emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT);
297+
298+
if (ep >= ArraySize(mAtomicWriteSessions))
245299
{
246300
return false;
247301
}
248-
return subjectDescriptor.authMode == Access::AuthMode::kCase &&
249-
GetAtomicWriteOriginatorScopedNodeId(endpoint) == ScopedNodeId(subjectDescriptor.subject, subjectDescriptor.fabricIndex);
302+
303+
auto & atomicWriteSession = mAtomicWriteSessions[ep];
304+
atomicWriteSession.endpointId = endpoint;
305+
if (!atomicWriteSession.attributeIds.Alloc(attributeStatuses.AllocatedSize()))
306+
{
307+
atomicWriteSession.state = AtomicWriteState::Closed;
308+
atomicWriteSession.nodeId = ScopedNodeId();
309+
return false;
310+
}
311+
312+
atomicWriteSession.state = state;
313+
atomicWriteSession.nodeId = originatorNodeId;
314+
315+
for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i)
316+
{
317+
atomicWriteSession.attributeIds[i] = attributeStatuses[i].attributeID;
318+
}
319+
return true;
250320
}
251321

252-
bool ThermostatAttrAccess::InAtomicWrite(CommandHandler * commandObj, EndpointId endpoint)
322+
void ThermostatAttrAccess::ResetAtomicWrite(EndpointId endpoint)
253323
{
254-
if (!InAtomicWrite(endpoint))
324+
auto delegate = GetDelegate(endpoint);
325+
if (delegate != nullptr)
255326
{
256-
return false;
327+
delegate->ClearPendingPresetList();
257328
}
258-
ScopedNodeId sourceNodeId = GetSourceScopedNodeId(commandObj);
259-
return GetAtomicWriteOriginatorScopedNodeId(endpoint) == sourceNodeId;
329+
ClearTimer(endpoint);
330+
uint16_t ep =
331+
emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT);
332+
333+
if (ep >= ArraySize(mAtomicWriteSessions))
334+
{
335+
return;
336+
}
337+
auto & atomicWriteSession = mAtomicWriteSessions[ep];
338+
atomicWriteSession.state = AtomicWriteState::Closed;
339+
atomicWriteSession.endpointId = endpoint;
340+
atomicWriteSession.nodeId = ScopedNodeId();
341+
atomicWriteSession.attributeIds.Alloc(0);
260342
}
261343

262344
ScopedNodeId ThermostatAttrAccess::GetAtomicWriteOriginatorScopedNodeId(const EndpointId endpoint)
@@ -273,13 +355,13 @@ ScopedNodeId ThermostatAttrAccess::GetAtomicWriteOriginatorScopedNodeId(const En
273355
}
274356

275357
void SendAtomicResponse(CommandHandler * commandObj, const ConcreteCommandPath & commandPath, Status status,
276-
const Platform::ScopedMemoryBuffer<AtomicAttributeStatusStruct::Type> & attributeStatuses,
277-
size_t attributeRequestCount, Optional<uint16_t> timeout = NullOptional)
358+
const Platform::ScopedMemoryBufferWithSize<AtomicAttributeStatusStruct::Type> & attributeStatuses,
359+
Optional<uint16_t> timeout = NullOptional)
278360
{
279361
Commands::AtomicResponse::Type response;
280362
response.statusCode = to_underlying(status);
281363
response.attributeStatus =
282-
DataModel::List<const AtomicAttributeStatusStruct::Type>(attributeStatuses.Get(), attributeRequestCount);
364+
DataModel::List<const AtomicAttributeStatusStruct::Type>(attributeStatuses.Get(), attributeStatuses.AllocatedSize());
283365
response.timeout = timeout;
284366
commandObj->AddResponse(commandPath, response);
285367
}
@@ -298,16 +380,15 @@ void ThermostatAttrAccess::BeginAtomicWrite(CommandHandler * commandObj, const C
298380
return;
299381
}
300382

301-
size_t attributeStatusCount = 0;
302-
Platform::ScopedMemoryBuffer<AtomicAttributeStatusStruct::Type> attributeStatuses;
303-
auto status = BuildAttributeStatuses(endpoint, commandData.attributeRequests, attributeStatusCount, attributeStatuses, false);
383+
Platform::ScopedMemoryBufferWithSize<AtomicAttributeStatusStruct::Type> attributeStatuses;
384+
auto status = BuildAttributeStatuses(endpoint, commandData.attributeRequests, attributeStatuses);
304385
if (status != Status::Success)
305386
{
306387
commandObj->AddStatus(commandPath, status);
307388
return;
308389
}
309390

310-
if (gThermostatAttrAccess.InAtomicWrite(commandObj, endpoint))
391+
if (InAtomicWrite(endpoint, commandObj))
311392
{
312393
// This client already has an open atomic write
313394
commandObj->AddStatus(commandPath, Status::InvalidInState);
@@ -342,15 +423,15 @@ void ThermostatAttrAccess::BeginAtomicWrite(CommandHandler * commandObj, const C
342423
}
343424

344425
status = Status::Success;
345-
for (size_t i = 0; i < attributeStatusCount; ++i)
426+
for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i)
346427
{
347428
auto & attributeStatus = attributeStatuses[i];
348429
auto statusCode = Status::Success;
349430
switch (attributeStatus.attributeID)
350431
{
351432
case Presets::Id:
352433
case Schedules::Id:
353-
statusCode = gThermostatAttrAccess.InAtomicWrite(endpoint) ? Status::Busy : Status::Success;
434+
statusCode = InAtomicWrite(endpoint, MakeOptional(attributeStatus.attributeID)) ? Status::Busy : Status::Success;
354435
break;
355436
default:
356437
statusCode = Status::InvalidCommand;
@@ -372,14 +453,24 @@ void ThermostatAttrAccess::BeginAtomicWrite(CommandHandler * commandObj, const C
372453

373454
if (status == Status::Success)
374455
{
375-
// This is a valid request to open an atomic write. Tell the delegate it
376-
// needs to keep track of a pending preset list now.
377-
delegate->InitializePendingPresets();
378-
ScheduleTimer(endpoint, timeout);
379-
SetAtomicWrite(endpoint, GetSourceScopedNodeId(commandObj), AtomicWriteState::Open);
456+
if (!SetAtomicWrite(endpoint, GetSourceScopedNodeId(commandObj), AtomicWriteState::Open, attributeStatuses))
457+
{
458+
for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i)
459+
{
460+
attributeStatuses[i].statusCode = to_underlying(Status::ResourceExhausted);
461+
}
462+
status = Status::Failure;
463+
}
464+
else
465+
{
466+
// This is a valid request to open an atomic write. Tell the delegate it
467+
// needs to keep track of a pending preset list now.
468+
delegate->InitializePendingPresets();
469+
ScheduleTimer(endpoint, timeout);
470+
}
380471
}
381472

382-
SendAtomicResponse(commandObj, commandPath, status, attributeStatuses, attributeStatusCount, MakeOptional(timeout.count()));
473+
SendAtomicResponse(commandObj, commandPath, status, attributeStatuses, MakeOptional(timeout.count()));
383474
}
384475

385476
void ThermostatAttrAccess::CommitAtomicWrite(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
@@ -395,23 +486,22 @@ void ThermostatAttrAccess::CommitAtomicWrite(CommandHandler * commandObj, const
395486
return;
396487
}
397488

398-
size_t attributeStatusCount = 0;
399-
Platform::ScopedMemoryBuffer<AtomicAttributeStatusStruct::Type> attributeStatuses;
400-
auto status = BuildAttributeStatuses(endpoint, commandData.attributeRequests, attributeStatusCount, attributeStatuses, true);
489+
Platform::ScopedMemoryBufferWithSize<AtomicAttributeStatusStruct::Type> attributeStatuses;
490+
auto status = BuildAttributeStatuses(endpoint, commandData.attributeRequests, attributeStatuses);
401491
if (status != Status::Success)
402492
{
403493
commandObj->AddStatus(commandPath, status);
404494
return;
405495
}
406496

407-
if (!gThermostatAttrAccess.InAtomicWrite(commandObj, endpoint))
497+
if (!InAtomicWrite(endpoint, commandObj, attributeStatuses))
408498
{
409499
commandObj->AddStatus(commandPath, Status::InvalidInState);
410500
return;
411501
}
412502

413503
status = Status::Success;
414-
for (size_t i = 0; i < attributeStatusCount; ++i)
504+
for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i)
415505
{
416506
auto & attributeStatus = attributeStatuses[i];
417507
auto statusCode = Status::Success;
@@ -443,7 +533,7 @@ void ThermostatAttrAccess::CommitAtomicWrite(CommandHandler * commandObj, const
443533
}
444534

445535
ResetAtomicWrite(endpoint);
446-
SendAtomicResponse(commandObj, commandPath, status, attributeStatuses, attributeStatusCount);
536+
SendAtomicResponse(commandObj, commandPath, status, attributeStatuses);
447537
}
448538

449539
void ThermostatAttrAccess::RollbackAtomicWrite(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
@@ -460,16 +550,15 @@ void ThermostatAttrAccess::RollbackAtomicWrite(CommandHandler * commandObj, cons
460550
return;
461551
}
462552

463-
size_t attributeStatusCount = 0;
464-
Platform::ScopedMemoryBuffer<AtomicAttributeStatusStruct::Type> attributeStatuses;
465-
auto status = BuildAttributeStatuses(endpoint, commandData.attributeRequests, attributeStatusCount, attributeStatuses, true);
553+
Platform::ScopedMemoryBufferWithSize<AtomicAttributeStatusStruct::Type> attributeStatuses;
554+
auto status = BuildAttributeStatuses(endpoint, commandData.attributeRequests, attributeStatuses);
466555
if (status != Status::Success)
467556
{
468557
commandObj->AddStatus(commandPath, status);
469558
return;
470559
}
471560

472-
if (!gThermostatAttrAccess.InAtomicWrite(commandObj, endpoint))
561+
if (!InAtomicWrite(endpoint, commandObj, attributeStatuses))
473562
{
474563
// There's no open atomic write
475564
commandObj->AddStatus(commandPath, Status::InvalidInState);
@@ -478,7 +567,7 @@ void ThermostatAttrAccess::RollbackAtomicWrite(CommandHandler * commandObj, cons
478567

479568
ResetAtomicWrite(endpoint);
480569

481-
for (size_t i = 0; i < attributeStatusCount; ++i)
570+
for (size_t i = 0; i < attributeStatuses.AllocatedSize(); ++i)
482571
{
483572
auto & attributeStatus = attributeStatuses[i];
484573
switch (attributeStatus.attributeID)
@@ -493,20 +582,7 @@ void ThermostatAttrAccess::RollbackAtomicWrite(CommandHandler * commandObj, cons
493582
}
494583
}
495584

496-
SendAtomicResponse(commandObj, commandPath, status, attributeStatuses, attributeStatusCount);
497-
}
498-
499-
void ThermostatAttrAccess::SetAtomicWrite(EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state)
500-
{
501-
uint16_t ep =
502-
emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT);
503-
504-
if (ep < ArraySize(mAtomicWriteSessions))
505-
{
506-
mAtomicWriteSessions[ep].state = state;
507-
mAtomicWriteSessions[ep].endpointId = endpoint;
508-
mAtomicWriteSessions[ep].nodeId = originatorNodeId;
509-
}
585+
SendAtomicResponse(commandObj, commandPath, status, attributeStatuses);
510586
}
511587

512588
} // namespace Thermostat

0 commit comments

Comments
 (0)