Skip to content

Commit edbdf8a

Browse files
lpbeliveau-silabsrestyled-commitsbzbarsky-apple
authored
[ReadHandler] Report Scheduler class (#27553)
* Added a new class that will handle the scheduling of reports. * Restyled by clang-format * Removed un-necessary define in TestReportScheduler and applied refactor of SetReportingIntervals to SetMaxReportingIntervals to platform code * Added TimerDelegate and wrapper functions around calls to Timer. Remove unnecessary checks for nullptr * Added VerifyOrReturn after NL_TEST_ASSERTS for nullptr * Completed TimerDelegate class and modified ReadHandlerNodes so they carry their own callback * Modified TimerDelegate to allow to pass different objects as context * ifdefing out ScheduleRun() to debug failing CI * Added issue # to TODOs, refactored Min/Max Intervals to Min/Max Timestamp * Clarified some comments regarding timing * Restyled by whitespace * Restyled by clang-format * Added interface to GetMonotonicTimestamp in the timer delegate * Apply suggestions from code review Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Completed renaming to eliminate compiling error, moved TestReporScehduler in reporting namespace, addressed some low hanging fruits * Removed useless objects from tests as well as useless typecasting, and unnecessary check * Fixed comment about private methods used in ReportScheduler as a friend class * Changed to SetMinReportInterval to SetMinReportingIntervalForTests, removed the IsChunkedReport from comment about friend class, added a mock timestamp and timer to test to better control time in simulation for specific timing test cases * Apply suggestions from code review Co-authored-by: Boris Zbarsky <bzbarsky@apple.com> * Restyled by clang-format * Removed all calls to ReadHandler States to prevent Engine calls from the Test as it seems to impact the CI --------- Co-authored-by: Restyled.io <commits@restyled.io> Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
1 parent b3f8db6 commit edbdf8a

File tree

13 files changed

+985
-16
lines changed

13 files changed

+985
-16
lines changed

examples/platform/nrfconnect/util/ICDUtil.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,5 @@ CHIP_ERROR ICDUtil::OnSubscriptionRequested(chip::app::ReadHandler & aReadHandle
3636
agreedMaxInterval = kSubscriptionMaxIntervalPublisherLimit;
3737
}
3838

39-
return aReadHandler.SetReportingIntervals(agreedMaxInterval);
39+
return aReadHandler.SetMaxReportingInterval(agreedMaxInterval);
4040
}

examples/platform/silabs/ICDSubscriptionCallback.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,5 @@ CHIP_ERROR ICDSubscriptionCallback::OnSubscriptionRequested(chip::app::ReadHandl
6161
decidedMaxInterval = maximumMaxInterval;
6262
}
6363

64-
return aReadHandler.SetReportingIntervals(decidedMaxInterval);
64+
return aReadHandler.SetMaxReportingInterval(decidedMaxInterval);
6565
}

src/app/BUILD.gn

+3
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ static_library("app") {
193193
"WriteHandler.cpp",
194194
"reporting/Engine.cpp",
195195
"reporting/Engine.h",
196+
"reporting/ReportScheduler.h",
197+
"reporting/ReportSchedulerImpl.cpp",
198+
"reporting/ReportSchedulerImpl.h",
196199
"reporting/reporting.h",
197200
]
198201

src/app/ReadHandler.cpp

+74-3
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ namespace app {
3939
using Status = Protocols::InteractionModel::Status;
4040

4141
ReadHandler::ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeContext * apExchangeContext,
42-
InteractionType aInteractionType) :
42+
InteractionType aInteractionType, Observer * observer) :
4343
mExchangeCtx(*this),
4444
mManagementCallback(apCallback)
4545
#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
@@ -63,15 +63,37 @@ ReadHandler::ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeCon
6363
SetStateFlag(ReadHandlerFlags::PrimingReports);
6464

6565
mSessionHandle.Grab(mExchangeCtx->GetSessionHandle());
66+
67+
// TODO (#27672): Uncomment when the ReportScheduler is implemented
68+
#if 0
69+
if (nullptr != observer)
70+
{
71+
if (CHIP_NO_ERROR == SetObserver(observer))
72+
{
73+
mObserver->OnReadHandlerCreated(this);
74+
}
75+
}
76+
#endif
6677
}
6778

6879
#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
69-
ReadHandler::ReadHandler(ManagementCallback & apCallback) :
80+
ReadHandler::ReadHandler(ManagementCallback & apCallback, Observer * observer) :
7081
mExchangeCtx(*this), mManagementCallback(apCallback), mOnConnectedCallback(HandleDeviceConnected, this),
7182
mOnConnectionFailureCallback(HandleDeviceConnectionFailure, this)
7283
{
7384
mInteractionType = InteractionType::Subscribe;
7485
mFlags.ClearAll();
86+
87+
// TODO (#27672): Uncomment when the ReportScheduler is implemented
88+
#if 0
89+
if (nullptr != observer)
90+
{
91+
if (CHIP_NO_ERROR == SetObserver(observer))
92+
{
93+
mObserver->OnReadHandlerCreated(this);
94+
}
95+
}
96+
#endif
7597
}
7698

7799
void ReadHandler::ResumeSubscription(CASESessionManager & caseSessionManager,
@@ -115,6 +137,13 @@ void ReadHandler::ResumeSubscription(CASESessionManager & caseSessionManager,
115137

116138
ReadHandler::~ReadHandler()
117139
{
140+
// TODO (#27672): Enable when the ReportScheduler is implemented and move in Close() after testing
141+
#if 0
142+
if (nullptr != mObserver)
143+
{
144+
mObserver->OnReadHandlerDestroyed(this);
145+
}
146+
#endif
118147
auto * appCallback = mManagementCallback.GetAppCallback();
119148
if (mFlags.Has(ReadHandlerFlags::ActiveSubscription) && appCallback)
120149
{
@@ -319,6 +348,15 @@ CHIP_ERROR ReadHandler::SendReportData(System::PacketBufferHandle && aPayload, b
319348

320349
if (IsType(InteractionType::Subscribe) && !IsPriming())
321350
{
351+
// TODO (#27672): Enable when the ReportScheduler is implemented and remove call to UpdateReportTimer, will be handled by
352+
// the report Scheduler
353+
#if 0
354+
if (nullptr != mObserver)
355+
{
356+
mObserver->OnSubscriptionAction(this);
357+
}
358+
#endif
359+
322360
// Ignore the error from UpdateReportTimer. If we've
323361
// successfully sent the message, we need to return success from
324362
// this method.
@@ -593,6 +631,13 @@ void ReadHandler::MoveToState(const HandlerState aTargetState)
593631
//
594632
if (aTargetState == HandlerState::GeneratingReports && IsReportableNow())
595633
{
634+
// TODO (#27672): Enable when the ReportScheduler is implemented and remove the call to ScheduleRun()
635+
#if 0
636+
if(nullptr != mObserver)
637+
{
638+
mObserver->OnBecameReportable(this);
639+
}
640+
#endif
596641
InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleRun();
597642
}
598643
}
@@ -634,6 +679,14 @@ CHIP_ERROR ReadHandler::SendSubscribeResponse()
634679
ReturnErrorOnFailure(writer.Finalize(&packet));
635680
VerifyOrReturnLogError(mExchangeCtx, CHIP_ERROR_INCORRECT_STATE);
636681

682+
// TODO (#27672): Uncomment when the ReportScheduler is implemented and remove call to UpdateReportTimer, handled by
683+
// the report Scheduler
684+
#if 0
685+
if (nullptr != mObserver)
686+
{
687+
mObserver->OnSubscriptionAction(this);
688+
}
689+
#endif
637690
ReturnErrorOnFailure(UpdateReportTimer());
638691

639692
ClearStateFlag(ReadHandlerFlags::PrimingReports);
@@ -753,6 +806,7 @@ void ReadHandler::PersistSubscription()
753806
}
754807
}
755808

809+
// TODO (#27672): Remove when ReportScheduler is enabled as timing will now be handled by the ReportScheduler
756810
void ReadHandler::MinIntervalExpiredCallback(System::Layer * apSystemLayer, void * apAppState)
757811
{
758812
VerifyOrReturn(apAppState != nullptr);
@@ -764,6 +818,7 @@ void ReadHandler::MinIntervalExpiredCallback(System::Layer * apSystemLayer, void
764818
readHandler);
765819
}
766820

821+
// TODO (#27672): Remove when ReportScheduler is enabled as timing will now be handled by the ReportScheduler
767822
void ReadHandler::MaxIntervalExpiredCallback(System::Layer * apSystemLayer, void * apAppState)
768823
{
769824
VerifyOrReturn(apAppState != nullptr);
@@ -773,6 +828,7 @@ void ReadHandler::MaxIntervalExpiredCallback(System::Layer * apSystemLayer, void
773828
readHandler->mMaxInterval - readHandler->mMinIntervalFloorSeconds);
774829
}
775830

831+
// TODO (#27672): Remove when ReportScheduler is enabled as timing will now be handled by the ReportScheduler
776832
CHIP_ERROR ReadHandler::UpdateReportTimer()
777833
{
778834
InteractionModelEngine::GetInstance()->GetExchangeManager()->GetSessionManager()->SystemLayer()->CancelTimer(
@@ -812,7 +868,7 @@ void ReadHandler::AttributePathIsDirty(const AttributePathParams & aAttributeCha
812868
// Here we just reset the iterator to the beginning of the current cluster, if the dirty path affects it.
813869
// This will ensure the reports are consistent within a single cluster generated from a single path in the request.
814870

815-
// TODO (#16699): Currently we can only gurentee the reports generated from a single path in the request are consistent. The
871+
// TODO (#16699): Currently we can only guarantee the reports generated from a single path in the request are consistent. The
816872
// data might be inconsistent if the user send a request with two paths from the same cluster. We need to clearify the behavior
817873
// or make it consistent.
818874
if (mAttributePathExpandIterator.Get(path) &&
@@ -831,6 +887,13 @@ void ReadHandler::AttributePathIsDirty(const AttributePathParams & aAttributeCha
831887

832888
if (IsReportableNow())
833889
{
890+
// TODO (#27672): Enable when the ReportScheduler is implemented and remove the call to ScheduleRun()
891+
#if 0
892+
if(nullptr != mObserver)
893+
{
894+
mObserver->OnBecameReportable(this);
895+
}
896+
#endif
834897
InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleRun();
835898
}
836899
}
@@ -853,9 +916,17 @@ void ReadHandler::SetStateFlag(ReadHandlerFlags aFlag, bool aValue)
853916
{
854917
bool oldReportable = IsReportableNow();
855918
mFlags.Set(aFlag, aValue);
919+
856920
// If we became reportable, schedule a reporting run.
857921
if (!oldReportable && IsReportableNow())
858922
{
923+
// TODO (#27672): Enable when the ReportScheduler is implemented and remove the call to ScheduleRun()
924+
#if 0
925+
if(nullptr != mObserver)
926+
{
927+
mObserver->OnBecameReportable(this);
928+
}
929+
#endif
859930
InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleRun();
860931
}
861932
}

src/app/ReadHandler.h

+78-9
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ namespace app {
6464
namespace reporting {
6565
class Engine;
6666
class TestReportingEngine;
67+
class ReportScheduler;
68+
class TestReportScheduler;
6769
} // namespace reporting
6870

6971
class InteractionModelEngine;
@@ -152,6 +154,38 @@ class ReadHandler : public Messaging::ExchangeDelegate
152154
virtual ApplicationCallback * GetAppCallback() = 0;
153155
};
154156

157+
// TODO (#27675) : Merge existing callback and observer into one class and have an observer pool in the Readhandler to notify
158+
// every
159+
/*
160+
* Observer class for ReadHandler, meant to allow multiple objects to observe the ReadHandler. Currently only one observer is
161+
* supported but all above callbacks should be merged into observer type and an observer pool should be added to allow multiple
162+
* objects to observe ReadHandler
163+
*/
164+
class Observer
165+
{
166+
public:
167+
virtual ~Observer() = default;
168+
169+
/// @brief Callback invoked to notify a ReadHandler was created and can be registered
170+
/// @param[in] apReadHandler ReadHandler getting added
171+
virtual void OnReadHandlerCreated(ReadHandler * apReadHandler) = 0;
172+
173+
/// @brief Callback invoked when a ReadHandler went from a non reportable state to a reportable state so a report can be
174+
/// sent immediately if the minimal interval allows it. Otherwise the report should be rescheduled to the earliest time
175+
/// allowed.
176+
/// @param[in] apReadHandler ReadHandler that became dirty
177+
virtual void OnBecameReportable(ReadHandler * apReadHandler) = 0;
178+
179+
/// @brief Callback invoked when the read handler needs to make sure to send a message to the subscriber within the next
180+
/// maxInterval time period.
181+
/// @param[in] apReadHandler ReadHandler that has generated a report
182+
virtual void OnSubscriptionAction(ReadHandler * apReadHandler) = 0;
183+
184+
/// @brief Callback invoked when a ReadHandler is getting removed so it can be unregistered
185+
/// @param[in] apReadHandler ReadHandler getting destroyed
186+
virtual void OnReadHandlerDestroyed(ReadHandler * apReadHandler) = 0;
187+
};
188+
155189
/*
156190
* Destructor - as part of destruction, it will abort the exchange context
157191
* if a valid one still exists.
@@ -167,7 +201,8 @@ class ReadHandler : public Messaging::ExchangeDelegate
167201
* The callback passed in has to outlive this handler object.
168202
*
169203
*/
170-
ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeContext * apExchangeContext, InteractionType aInteractionType);
204+
ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeContext * apExchangeContext, InteractionType aInteractionType,
205+
Observer * observer = nullptr);
171206

172207
#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
173208
/**
@@ -177,7 +212,7 @@ class ReadHandler : public Messaging::ExchangeDelegate
177212
* The callback passed in has to outlive this handler object.
178213
*
179214
*/
180-
ReadHandler(ManagementCallback & apCallback);
215+
ReadHandler(ManagementCallback & apCallback, Observer * observer = nullptr);
181216
#endif
182217

183218
const ObjectList<AttributePathParams> * GetAttributePathList() const { return mpAttributePathList; }
@@ -190,13 +225,22 @@ class ReadHandler : public Messaging::ExchangeDelegate
190225
aMaxInterval = mMaxInterval;
191226
}
192227

228+
CHIP_ERROR SetMinReportingIntervalForTests(uint16_t aMinInterval)
229+
{
230+
VerifyOrReturnError(IsIdle(), CHIP_ERROR_INCORRECT_STATE);
231+
VerifyOrReturnError(aMinInterval <= mMaxInterval, CHIP_ERROR_INVALID_ARGUMENT);
232+
// Ensures the new min interval is higher than the subscriber established one.
233+
mMinIntervalFloorSeconds = std::max(mMinIntervalFloorSeconds, aMinInterval);
234+
return CHIP_NO_ERROR;
235+
}
236+
193237
/*
194-
* Set the reporting intervals for the subscription. This SHALL only be called
238+
* Set the maximum reporting interval for the subscription. This SHALL only be called
195239
* from the OnSubscriptionRequested callback above. The restriction is as below
196240
* MinIntervalFloor ≤ MaxInterval ≤ MAX(SUBSCRIPTION_MAX_INTERVAL_PUBLISHER_LIMIT, MaxIntervalCeiling)
197241
* Where SUBSCRIPTION_MAX_INTERVAL_PUBLISHER_LIMIT is set to 60m in the spec.
198242
*/
199-
CHIP_ERROR SetReportingIntervals(uint16_t aMaxInterval)
243+
CHIP_ERROR SetMaxReportingInterval(uint16_t aMaxInterval)
200244
{
201245
VerifyOrReturnError(IsIdle(), CHIP_ERROR_INCORRECT_STATE);
202246
VerifyOrReturnError(mMinIntervalFloorSeconds <= aMaxInterval, CHIP_ERROR_INVALID_ARGUMENT);
@@ -206,6 +250,18 @@ class ReadHandler : public Messaging::ExchangeDelegate
206250
return CHIP_NO_ERROR;
207251
}
208252

253+
/// @brief Add an observer to the read handler, currently only one observer is supported but all other callbacks should be
254+
/// merged with a general observer type to allow multiple object to observe readhandlers
255+
/// @param aObserver observer to be added
256+
/// @return CHIP_ERROR_INVALID_ARGUMENT if passing in nullptr
257+
CHIP_ERROR SetObserver(Observer * aObserver)
258+
{
259+
VerifyOrReturnError(nullptr != aObserver, CHIP_ERROR_INVALID_ARGUMENT);
260+
// TODO (#27675) : After merging the callbacks and observer, change so the method adds a new observer to an observer pool
261+
mObserver = aObserver;
262+
return CHIP_NO_ERROR;
263+
}
264+
209265
private:
210266
PriorityLevel GetCurrentPriority() const { return mCurrentPriority; }
211267
EventNumber & GetEventMin() { return mEventMin; }
@@ -214,13 +270,13 @@ class ReadHandler : public Messaging::ExchangeDelegate
214270
{
215271
// WaitingUntilMinInterval is used to prevent subscription data delivery while we are
216272
// waiting for the min reporting interval to elapse.
217-
WaitingUntilMinInterval = (1 << 0),
273+
WaitingUntilMinInterval = (1 << 0), // TODO (#27672): Remove once ReportScheduler is implemented or change to test flag
218274

219275
// WaitingUntilMaxInterval is used to prevent subscription empty report delivery while we
220276
// are waiting for the max reporting interval to elaps. When WaitingUntilMaxInterval
221277
// becomes false, we are allowed to send an empty report to keep the
222278
// subscription alive on the client.
223-
WaitingUntilMaxInterval = (1 << 1),
279+
WaitingUntilMaxInterval = (1 << 1), // TODO (#27672): Remove once ReportScheduler is implemented
224280

225281
// The flag indicating we are in the middle of a series of chunked report messages, this flag will be cleared during
226282
// sending last chunked message.
@@ -291,6 +347,8 @@ class ReadHandler : public Messaging::ExchangeDelegate
291347

292348
bool IsIdle() const { return mState == HandlerState::Idle; }
293349

350+
// TODO (#27672): Change back to IsReportable once ReportScheduler is implemented so this can assess reportability without
351+
// considering timing. The ReporScheduler will handle timing.
294352
/// @brief Returns whether the ReadHandler is in a state where it can immediately send a report. This function
295353
/// is used to determine whether a report generation should be scheduled for the handler.
296354
bool IsReportableNow() const
@@ -370,6 +428,7 @@ class ReadHandler : public Messaging::ExchangeDelegate
370428

371429
friend class TestReadInteraction;
372430
friend class chip::app::reporting::TestReportingEngine;
431+
friend class chip::app::reporting::TestReportScheduler;
373432

374433
//
375434
// The engine needs to be able to Abort/Close a ReadHandler instance upon completion of work for a given read/subscribe
@@ -379,6 +438,10 @@ class ReadHandler : public Messaging::ExchangeDelegate
379438
friend class chip::app::reporting::Engine;
380439
friend class chip::app::InteractionModelEngine;
381440

441+
// The report scheduler needs to be able to access StateFlag private functions IsGeneratingReports() and IsDirty() to
442+
// know when to schedule a run so it is declared as a friend class.
443+
friend class chip::app::reporting::ReportScheduler;
444+
382445
enum class HandlerState : uint8_t
383446
{
384447
Idle, ///< The handler has been initialized and is ready
@@ -404,10 +467,13 @@ class ReadHandler : public Messaging::ExchangeDelegate
404467

405468
/// @brief This function is called when the min interval timer has expired, it restarts the timer on a timeout equal to the
406469
/// difference between the max interval and the min interval.
407-
static void MinIntervalExpiredCallback(System::Layer * apSystemLayer, void * apAppState);
408-
static void MaxIntervalExpiredCallback(System::Layer * apSystemLayer, void * apAppState);
470+
static void MinIntervalExpiredCallback(System::Layer * apSystemLayer, void * apAppState); // TODO (#27672): Remove once
471+
// ReportScheduler is implemented.
472+
static void MaxIntervalExpiredCallback(System::Layer * apSystemLayer, void * apAppState); // TODO (#27672): Remove once
473+
// ReportScheduler is implemented.
409474
/// @brief This function is called when a report is sent and it restarts the min interval timer.
410-
CHIP_ERROR UpdateReportTimer();
475+
CHIP_ERROR UpdateReportTimer(); // TODO (#27672) : Remove once ReportScheduler is implemented.
476+
411477
CHIP_ERROR SendSubscribeResponse();
412478
CHIP_ERROR ProcessSubscribeRequest(System::PacketBufferHandle && aPayload);
413479
CHIP_ERROR ProcessReadRequest(System::PacketBufferHandle && aPayload);
@@ -520,6 +586,9 @@ class ReadHandler : public Messaging::ExchangeDelegate
520586
BitFlags<ReadHandlerFlags> mFlags;
521587
InteractionType mInteractionType = InteractionType::Read;
522588

589+
// TODO (#27675): Merge all observers into one and that one will dispatch the callbacks to the right place.
590+
Observer * mObserver = nullptr;
591+
523592
#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
524593
// Callbacks to handle server-initiated session success/failure
525594
chip::Callback::Callback<OnDeviceConnected> mOnConnectedCallback;

src/app/reporting/Engine.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,7 @@ void Engine::Run()
636636
ReadHandler * readHandler = imEngine->ActiveHandlerAt(mCurReadHandlerIdx % (uint32_t) imEngine->mReadHandlers.Allocated());
637637
VerifyOrDie(readHandler != nullptr);
638638

639+
// TODO (#27672): Replace with check with Report Scheduler if the read handler is reportable
639640
if (readHandler->IsReportableNow())
640641
{
641642
mRunningReadHandler = readHandler;

0 commit comments

Comments
 (0)