blob: 4b45ab9d1b6debd6ac2c01ae0da7221bef98f29c [file] [log] [blame]
/*
*
* Copyright (c) 2023 Project CHIP Authors
*
* 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.
*/
#include <app/InteractionModelEngine.h>
#include <app/reporting/ReportSchedulerImpl.h>
namespace chip {
namespace app {
namespace reporting {
using Seconds16 = System::Clock::Seconds16;
using Milliseconds32 = System::Clock::Milliseconds32;
using Timeout = System::Clock::Timeout;
using Timestamp = System::Clock::Timestamp;
using ReadHandlerNode = ReportScheduler::ReadHandlerNode;
/// @brief Callback called when the report timer expires to schedule an engine run regardless of the state of the ReadHandlers, as
/// the engine already verifies that read handlers are reportable before sending a report
static void ReportTimerCallback()
{
InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleRun();
}
ReportSchedulerImpl::ReportSchedulerImpl(TimerDelegate * aTimerDelegate) : ReportScheduler(aTimerDelegate)
{
VerifyOrDie(nullptr != mTimerDelegate);
}
/// @brief When a ReadHandler is added, register it, which will schedule an engine run
void ReportSchedulerImpl::OnReadHandlerCreated(ReadHandler * aReadHandler)
{
RegisterReadHandler(aReadHandler);
}
/// @brief When a ReadHandler becomes reportable, schedule, verifies if the min interval of a handleris elapsed. If not,
/// reschedule the report to happen when the min interval is elapsed. If it is, schedule an engine run.
void ReportSchedulerImpl::OnBecameReportable(ReadHandler * aReadHandler)
{
ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
VerifyOrReturn(nullptr != node);
Milliseconds32 newTimeout;
if (node->IsReportableNow())
{
// If the handler is reportable now, just schedule a report immediately
newTimeout = Milliseconds32(0);
}
else
{
// If the handler is not reportable now, schedule a report for the min interval
newTimeout = node->GetMinTimestamp() - mTimerDelegate->GetCurrentMonotonicTimestamp();
}
ScheduleReport(newTimeout, node);
}
void ReportSchedulerImpl::OnSubscriptionAction(ReadHandler * apReadHandler)
{
ReadHandlerNode * node = FindReadHandlerNode(apReadHandler);
VerifyOrReturn(nullptr != node);
// Schedule callback for max interval by computing the difference between the max timestamp and the current timestamp
node->SetIntervalTimeStamps(apReadHandler);
Milliseconds32 newTimeout = node->GetMaxTimestamp() - mTimerDelegate->GetCurrentMonotonicTimestamp();
ScheduleReport(newTimeout, node);
}
/// @brief When a ReadHandler is removed, unregister it, which will cancel any scheduled report
void ReportSchedulerImpl::OnReadHandlerDestroyed(ReadHandler * aReadHandler)
{
UnregisterReadHandler(aReadHandler);
}
CHIP_ERROR ReportSchedulerImpl::RegisterReadHandler(ReadHandler * aReadHandler)
{
ReadHandlerNode * newNode = FindReadHandlerNode(aReadHandler);
// Handler must not be registered yet; it's just being constructed.
VerifyOrDie(nullptr == newNode);
// The NodePool is the same size as the ReadHandler pool from the IM Engine, so we don't need a check for size here since if a
// ReadHandler was created, space should be available.
newNode = mNodesPool.CreateObject(aReadHandler, mTimerDelegate, ReportTimerCallback);
mReadHandlerList.PushBack(newNode);
ChipLogProgress(DataManagement,
"Registered a ReadHandler that will schedule a report between system Timestamp: %" PRIu64
" and system Timestamp %" PRIu64 ".",
newNode->GetMinTimestamp().count(), newNode->GetMaxTimestamp().count());
Timestamp now = mTimerDelegate->GetCurrentMonotonicTimestamp();
Milliseconds32 newTimeout;
// If the handler is reportable, schedule a report for the min interval, otherwise schedule a report for the max interval
if (newNode->IsReportableNow())
{
// If the handler is reportable now, just schedule a report immediately
newTimeout = Milliseconds32(0);
}
else if (IsReadHandlerReportable(aReadHandler) && (newNode->GetMinTimestamp() > now))
{
// If the handler is reportable now, but the min interval is not elapsed, schedule a report for the moment the min interval
// has elapsed
newTimeout = newNode->GetMinTimestamp() - now;
}
else
{
// If the handler is not reportable now, schedule a report for the max interval
newTimeout = newNode->GetMaxTimestamp() - now;
}
ReturnErrorOnFailure(ScheduleReport(newTimeout, newNode));
return CHIP_NO_ERROR;
}
CHIP_ERROR ReportSchedulerImpl::ScheduleReport(Timeout timeout, ReadHandlerNode * node)
{
// Cancel Report if it is currently scheduled
CancelSchedulerTimer(node);
StartSchedulerTimer(node, timeout);
return CHIP_NO_ERROR;
}
void ReportSchedulerImpl::CancelReport(ReadHandler * aReadHandler)
{
ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
VerifyOrReturn(nullptr != node);
CancelSchedulerTimer(node);
}
void ReportSchedulerImpl::UnregisterReadHandler(ReadHandler * aReadHandler)
{
CancelReport(aReadHandler);
ReadHandlerNode * removeNode = FindReadHandlerNode(aReadHandler);
// Nothing to remove if the handler is not found in the list
VerifyOrReturn(nullptr != removeNode);
mReadHandlerList.Remove(removeNode);
mNodesPool.ReleaseObject(removeNode);
}
void ReportSchedulerImpl::UnregisterAllHandlers()
{
while (!mReadHandlerList.Empty())
{
ReadHandler * firstReadHandler = mReadHandlerList.begin()->GetReadHandler();
UnregisterReadHandler(firstReadHandler);
}
}
bool ReportSchedulerImpl::IsReportScheduled(ReadHandler * aReadHandler)
{
ReadHandlerNode * node = FindReadHandlerNode(aReadHandler);
VerifyOrReturnValue(nullptr != node, false);
return CheckSchedulerTimerActive(node);
}
CHIP_ERROR ReportSchedulerImpl::StartSchedulerTimer(ReadHandlerNode * node, System::Clock::Timeout aTimeout)
{
// Schedule Report
return mTimerDelegate->StartTimer(node, aTimeout);
}
void ReportSchedulerImpl::CancelSchedulerTimer(ReadHandlerNode * node)
{
mTimerDelegate->CancelTimer(node);
}
bool ReportSchedulerImpl::CheckSchedulerTimerActive(ReadHandlerNode * node)
{
return mTimerDelegate->IsTimerActive(node);
}
} // namespace reporting
} // namespace app
} // namespace chip