Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ReadHandler] Report Scheduler class #27553

Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7328172
Added a new class that will handle the scheduling of reports.
lpbeliveau-silabs Jun 27, 2023
c1930a3
Restyled by clang-format
restyled-commits Jun 29, 2023
ed7bfac
Removed un-necessary define in TestReportScheduler and applied refact…
lpbeliveau-silabs Jun 29, 2023
e9812ae
Added TimerDelegate and wrapper functions around calls to Timer. Remo…
lpbeliveau-silabs Jun 29, 2023
498716e
Added VerifyOrReturn after NL_TEST_ASSERTS for nullptr
lpbeliveau-silabs Jun 30, 2023
9a16c68
Completed TimerDelegate class and modified ReadHandlerNodes so they c…
lpbeliveau-silabs Jun 30, 2023
48454fb
Modified TimerDelegate to allow to pass different objects as context
lpbeliveau-silabs Jul 4, 2023
02bf98a
ifdefing out ScheduleRun() to debug failing CI
lpbeliveau-silabs Jul 5, 2023
5e38e01
Added issue # to TODOs, refactored Min/Max Intervals to Min/Max Times…
lpbeliveau-silabs Jul 6, 2023
7c2cf2f
Clarified some comments regarding timing
lpbeliveau-silabs Jul 6, 2023
58df486
Restyled by whitespace
restyled-commits Jul 6, 2023
bfb69d9
Restyled by clang-format
restyled-commits Jul 6, 2023
6d92863
Added interface to GetMonotonicTimestamp in the timer delegate
lpbeliveau-silabs Jul 11, 2023
86aec76
Apply suggestions from code review
lpbeliveau-silabs Jul 11, 2023
64f5041
Completed renaming to eliminate compiling error, moved TestReporScehd…
lpbeliveau-silabs Jul 11, 2023
f015837
Removed useless objects from tests as well as useless typecasting, an…
lpbeliveau-silabs Jul 12, 2023
97683ad
Fixed comment about private methods used in ReportScheduler as a frie…
lpbeliveau-silabs Jul 12, 2023
e81641d
Changed to SetMinReportInterval to SetMinReportingIntervalForTests, r…
lpbeliveau-silabs Jul 13, 2023
2a55ac5
Apply suggestions from code review
lpbeliveau-silabs Jul 13, 2023
a0b9d6e
Restyled by clang-format
restyled-commits Jul 13, 2023
4df646c
Removed all calls to ReadHandler States to prevent Engine calls from …
lpbeliveau-silabs Jul 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/platform/nrfconnect/util/ICDUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,5 @@ CHIP_ERROR ICDUtil::OnSubscriptionRequested(chip::app::ReadHandler & aReadHandle
agreedMaxInterval = kSubscriptionMaxIntervalPublisherLimit;
}

return aReadHandler.SetReportingIntervals(agreedMaxInterval);
return aReadHandler.SetMaxReportingIntervals(agreedMaxInterval);
}
2 changes: 1 addition & 1 deletion examples/platform/silabs/ICDSubscriptionCallback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,5 @@ CHIP_ERROR ICDSubscriptionCallback::OnSubscriptionRequested(chip::app::ReadHandl
decidedMaxInterval = maximumMaxInterval;
}

return aReadHandler.SetReportingIntervals(decidedMaxInterval);
return aReadHandler.SetMaxReportingIntervals(decidedMaxInterval);
}
3 changes: 3 additions & 0 deletions src/app/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ static_library("app") {
"WriteHandler.cpp",
"reporting/Engine.cpp",
"reporting/Engine.h",
"reporting/ReportScheduler.h",
"reporting/ReportSchedulerImpl.cpp",
"reporting/ReportSchedulerImpl.h",
"reporting/reporting.h",
]

Expand Down
78 changes: 75 additions & 3 deletions src/app/ReadHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ namespace app {
using Status = Protocols::InteractionModel::Status;

ReadHandler::ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeContext * apExchangeContext,
InteractionType aInteractionType) :
InteractionType aInteractionType, Observer * observer) :
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
mExchangeCtx(*this),
mManagementCallback(apCallback)
#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
Expand All @@ -63,15 +63,37 @@ ReadHandler::ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeCon
SetStateFlag(ReadHandlerFlags::PrimingReports);

mSessionHandle.Grab(mExchangeCtx->GetSessionHandle());

// TODO (#27672): Uncomment when the ReportScheduler is implemented
#if 0
if (nullptr != observer)
{
if (CHIP_NO_ERROR == SetObserver(observer))
{
mObserver->OnReadHandlerAdded(this);
}
}
#endif
}

#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
ReadHandler::ReadHandler(ManagementCallback & apCallback) :
ReadHandler::ReadHandler(ManagementCallback & apCallback, Observer * observer) :
mExchangeCtx(*this), mManagementCallback(apCallback), mOnConnectedCallback(HandleDeviceConnected, this),
mOnConnectionFailureCallback(HandleDeviceConnectionFailure, this)
{
mInteractionType = InteractionType::Subscribe;
mFlags.ClearAll();

// TODO (#27672): Uncomment when the ReportScheduler is implemented
#if 0
if (nullptr != observer)
{
if (CHIP_NO_ERROR == SetObserver(observer))
{
mObserver->OnReadHandlerAdded(this);
}
}
#endif
}

void ReadHandler::ResumeSubscription(CASESessionManager & caseSessionManager,
Expand Down Expand Up @@ -137,6 +159,14 @@ ReadHandler::~ReadHandler()
InteractionModelEngine::GetInstance()->ReleaseAttributePathList(mpAttributePathList);
InteractionModelEngine::GetInstance()->ReleaseEventPathList(mpEventPathList);
InteractionModelEngine::GetInstance()->ReleaseDataVersionFilterList(mpDataVersionFilterList);

// TODO (#27672): Enable when the ReportScheduler is implemented
#if 0
if (nullptr != mObserver)
{
mObserver->OnReadHandlerRemoved(this);
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
}
#endif
}

void ReadHandler::Close(CloseOptions options)
Expand Down Expand Up @@ -319,6 +349,15 @@ CHIP_ERROR ReadHandler::SendReportData(System::PacketBufferHandle && aPayload, b

if (IsType(InteractionType::Subscribe) && !IsPriming())
{
// TODO (#27672): Enable when the ReportScheduler is implemented and remove call to UpdateReportTimer, will be handled by
// the report Scheduler
#if 0
if (nullptr != mObserver)
{
mObserver->OnReportSent(this);
}
#endif

// Ignore the error from UpdateReportTimer. If we've
// successfully sent the message, we need to return success from
// this method.
Expand Down Expand Up @@ -593,6 +632,13 @@ void ReadHandler::MoveToState(const HandlerState aTargetState)
//
if (aTargetState == HandlerState::GeneratingReports && IsReportableNow())
{
// TODO (#27672): Enable when the ReportScheduler is implemented and remove the call to ScheduleRun()
#if 0
if(nullptr != mObserver)
{
mObserver->OnBecameReportable(this);
}
#endif
InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleRun();
}
}
Expand Down Expand Up @@ -634,6 +680,14 @@ CHIP_ERROR ReadHandler::SendSubscribeResponse()
ReturnErrorOnFailure(writer.Finalize(&packet));
VerifyOrReturnLogError(mExchangeCtx, CHIP_ERROR_INCORRECT_STATE);

// TODO (#27672): Uncomment when the ReportScheduler is implemented and remove call to UpdateReportTimer, handled by
// the report Scheduler
#if 0
if (nullptr != mObserver)
{
mObserver->OnReportSent(this);
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
}
#endif
ReturnErrorOnFailure(UpdateReportTimer());

ClearStateFlag(ReadHandlerFlags::PrimingReports);
Expand Down Expand Up @@ -753,6 +807,7 @@ void ReadHandler::PersistSubscription()
}
}

// TODO (#27672): Remove when ReportScheduler is enabled as timing will now be handled by the ReportScheduler
void ReadHandler::MinIntervalExpiredCallback(System::Layer * apSystemLayer, void * apAppState)
{
VerifyOrReturn(apAppState != nullptr);
Expand All @@ -764,6 +819,7 @@ void ReadHandler::MinIntervalExpiredCallback(System::Layer * apSystemLayer, void
readHandler);
}

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

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

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

if (IsReportableNow())
{
// TODO (#27672): Enable when the ReportScheduler is implemented and remove the call to ScheduleRun()
#if 0
if(nullptr != mObserver)
{
mObserver->OnBecameReportable(this);
}
#endif
InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleRun();
}
}
Expand All @@ -853,9 +917,17 @@ void ReadHandler::SetStateFlag(ReadHandlerFlags aFlag, bool aValue)
{
bool oldReportable = IsReportableNow();
mFlags.Set(aFlag, aValue);

// If we became reportable, schedule a reporting run.
if (!oldReportable && IsReportableNow())
{
// TODO (#27672): Enable when the ReportScheduler is implemented and remove the call to ScheduleRun()
#if 0
if(nullptr != mObserver)
{
mObserver->OnBecameReportable(this);
}
#endif
InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleRun();
}
}
Expand Down
88 changes: 78 additions & 10 deletions src/app/ReadHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ namespace app {
namespace reporting {
class Engine;
class TestReportingEngine;
class ReportScheduler;
class TestReportScheduler;
} // namespace reporting

class InteractionModelEngine;
Expand Down Expand Up @@ -152,6 +154,37 @@ class ReadHandler : public Messaging::ExchangeDelegate
virtual ApplicationCallback * GetAppCallback() = 0;
};

// TODO (#27675) : Merge existing callback and observer into one class and have an observer pool in the Readhandler to notify
// every
/*
* Observer class for ReadHandler, meant to allow multiple objects to observe the ReadHandler. Currently only one observer is
* supported but all above callbacks should be merged into observer type and an observer pool should be added to allow multiple
* objects to observe ReadHandler
*/
class Observer
{
public:
virtual ~Observer() = default;

/// @brief Callback invoked to notify a ReadHandler was created and can be registered
/// @param[in] apReadHandler ReadHandler getting added
virtual void OnReadHandlerAdded(ReadHandler * apReadHandler) = 0;

/// @brief Callback invoked when a ReadHandler went from a non reportable state to a reportable state so a report can be
/// sent immediately if the minimal interval allows it. Otherwise the report should be rescheduled to the earliest time
/// allowed.
/// @param[in] apReadHandler ReadHandler that became dirty
virtual void OnBecameReportable(ReadHandler * apReadHandler) = 0;

/// @brief Callback invoked when a Repport is sent so the next report can be scheduled
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
/// @param[in] apReadHandler ReadHandler that has generated a report
virtual void OnReportSent(ReadHandler * apReadHandler) = 0;

/// @brief Callback invoked when a ReadHandler is getting removed so it can be unregistered
/// @param[in] apReadHandler ReadHandler getting destroyed
virtual void OnReadHandlerRemoved(ReadHandler * apReadHandler) = 0;
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
};

/*
* Destructor - as part of destruction, it will abort the exchange context
* if a valid one still exists.
Expand All @@ -167,7 +200,8 @@ class ReadHandler : public Messaging::ExchangeDelegate
* The callback passed in has to outlive this handler object.
*
*/
ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeContext * apExchangeContext, InteractionType aInteractionType);
ReadHandler(ManagementCallback & apCallback, Messaging::ExchangeContext * apExchangeContext, InteractionType aInteractionType,
Observer * observer = nullptr);

#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
/**
Expand All @@ -177,7 +211,7 @@ class ReadHandler : public Messaging::ExchangeDelegate
* The callback passed in has to outlive this handler object.
*
*/
ReadHandler(ManagementCallback & apCallback);
ReadHandler(ManagementCallback & apCallback, Observer * observer = nullptr);
#endif

const ObjectList<AttributePathParams> * GetAttributePathList() const { return mpAttributePathList; }
Expand All @@ -190,13 +224,21 @@ class ReadHandler : public Messaging::ExchangeDelegate
aMaxInterval = mMaxInterval;
}

CHIP_ERROR SetMinReportingIntervals(uint16_t aMinInterval)
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
{
VerifyOrReturnError(IsIdle(), CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(aMinInterval <= mMaxInterval, CHIP_ERROR_INVALID_ARGUMENT);
mMinIntervalFloorSeconds = aMinInterval;
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
return CHIP_NO_ERROR;
}

/*
* Set the reporting intervals for the subscription. This SHALL only be called
* Set the maximum reporting intervals for the subscription. This SHALL only be called
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
* from the OnSubscriptionRequested callback above. The restriction is as below
* MinIntervalFloor ≤ MaxInterval ≤ MAX(SUBSCRIPTION_MAX_INTERVAL_PUBLISHER_LIMIT, MaxIntervalCeiling)
* Where SUBSCRIPTION_MAX_INTERVAL_PUBLISHER_LIMIT is set to 60m in the spec.
*/
CHIP_ERROR SetReportingIntervals(uint16_t aMaxInterval)
CHIP_ERROR SetMaxReportingIntervals(uint16_t aMaxInterval)
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
{
VerifyOrReturnError(IsIdle(), CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(mMinIntervalFloorSeconds <= aMaxInterval, CHIP_ERROR_INVALID_ARGUMENT);
Expand All @@ -206,6 +248,18 @@ class ReadHandler : public Messaging::ExchangeDelegate
return CHIP_NO_ERROR;
}

/// @brief Add an observer to the read handler, currently only one observer is supported but all other callbacks should be
/// merged with a general observer type to allow multiple object to observe readhandlers
/// @param aObserver observer to be added
/// @return CHIP_ERROR_INVALID_ARGUMENT if passing in nullptr
CHIP_ERROR SetObserver(Observer * aObserver)
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
{
VerifyOrReturnError(nullptr != aObserver, CHIP_ERROR_INVALID_ARGUMENT);
// TODO (#27675) : After merging the callbacks and observer, change so the method adds a new observer to an observer pool
mObserver = aObserver;
return CHIP_NO_ERROR;
}

private:
PriorityLevel GetCurrentPriority() const { return mCurrentPriority; }
EventNumber & GetEventMin() { return mEventMin; }
Expand All @@ -214,13 +268,13 @@ class ReadHandler : public Messaging::ExchangeDelegate
{
// WaitingUntilMinInterval is used to prevent subscription data delivery while we are
// waiting for the min reporting interval to elapse.
WaitingUntilMinInterval = (1 << 0),
WaitingUntilMinInterval = (1 << 0), // TODO (#27672): Remove once ReportScheduler is implemented or change to test flag

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

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

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

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

friend class TestReadInteraction;
friend class chip::app::reporting::TestReportingEngine;
friend class TestReportScheduler;

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

// The report scheduler needs to be able to access StateFlag private functions to know when to schedule a run so it is declared
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
// as a friend class.
friend class chip::app::reporting::ReportScheduler;

enum class HandlerState : uint8_t
{
Idle, ///< The handler has been initialized and is ready
Expand All @@ -404,10 +465,13 @@ class ReadHandler : public Messaging::ExchangeDelegate

/// @brief This function is called when the min interval timer has expired, it restarts the timer on a timeout equal to the
/// difference between the max interval and the min interval.
static void MinIntervalExpiredCallback(System::Layer * apSystemLayer, void * apAppState);
static void MaxIntervalExpiredCallback(System::Layer * apSystemLayer, void * apAppState);
static void MinIntervalExpiredCallback(System::Layer * apSystemLayer, void * apAppState); // TODO (#27672): Remove once
// ReportScheduler is implemented.
static void MaxIntervalExpiredCallback(System::Layer * apSystemLayer, void * apAppState); // TODO (#27672): Remove once
// ReportScheduler is implemented.
/// @brief This function is called when a report is sent and it restarts the min interval timer.
CHIP_ERROR UpdateReportTimer();
CHIP_ERROR UpdateReportTimer(); // TODO (#27672) : Remove once ReportScheduler is implemented.

CHIP_ERROR SendSubscribeResponse();
CHIP_ERROR ProcessSubscribeRequest(System::PacketBufferHandle && aPayload);
CHIP_ERROR ProcessReadRequest(System::PacketBufferHandle && aPayload);
Expand Down Expand Up @@ -492,7 +556,7 @@ class ReadHandler : public Messaging::ExchangeDelegate
// The last schedule event number snapshoted in the beginning when preparing to fill new events to reports
EventNumber mLastScheduledEventNumber = 0;

// TODO: We should shutdown the transaction when the session expires.
// TODO : We should shutdown the transaction when the session expires.
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
SessionHolder mSessionHandle;

Messaging::ExchangeHolder mExchangeCtx;
Expand Down Expand Up @@ -520,6 +584,10 @@ class ReadHandler : public Messaging::ExchangeDelegate
BitFlags<ReadHandlerFlags> mFlags;
InteractionType mInteractionType = InteractionType::Read;

// TODO (#27675): Several objects are behaving as observers for this class, there should be a single
lpbeliveau-silabs marked this conversation as resolved.
Show resolved Hide resolved
// type for this and an array or a pool to store them.
Observer * mObserver = nullptr;

#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
// Callbacks to handle server-initiated session success/failure
chip::Callback::Callback<OnDeviceConnected> mOnConnectedCallback;
Expand Down
1 change: 1 addition & 0 deletions src/app/reporting/Engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,7 @@ void Engine::Run()
ReadHandler * readHandler = imEngine->ActiveHandlerAt(mCurReadHandlerIdx % (uint32_t) imEngine->mReadHandlers.Allocated());
VerifyOrDie(readHandler != nullptr);

// TODO (#27672): Replace with check with Report Scheduler if the read handler is reportable
if (readHandler->IsReportableNow())
{
mRunningReadHandler = readHandler;
Expand Down
Loading
Loading