Skip to content

Commit

Permalink
[SVE2] Add timer compensation in level control cluster (#22395)
Browse files Browse the repository at this point in the history
* [SVE2] Add timer compensation in level control cluster

Signed-off-by: Andrei Menzopol <[email protected]>

* Restyled by clang-format

* Fix signedness error

Signed-off-by: Andrei Menzopol <[email protected]>

* Add requested changes
 * Move timestamp in state
 * Use callback compute time
 * Use only duration and timestamp

Signed-off-by: Andrei Menzopol <[email protected]>

* Restyled by clang-format

* Resolve conversations
* Change structure and function names
* Change function definition
* Add comments

Signed-off-by: Andrei Menzopol <[email protected]>

* Restyled by clang-format

* Change struct to C++ style

Signed-off-by: Andrei Menzopol <[email protected]>

* Update function, add comments, change names

Signed-off-by: Andrei Menzopol <[email protected]>

* Restyled by clang-format

* Fix condition

Signed-off-by: Andrei Menzopol <[email protected]>
Co-authored-by: Restyled.io <[email protected]>
  • Loading branch information
2 people authored and pull[bot] committed Jul 6, 2023
1 parent 1146787 commit 3992728
Showing 1 changed file with 64 additions and 4 deletions.
68 changes: 64 additions & 4 deletions src/app/clusters/level-control/level-control.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ static bool areStartUpLevelControlServerAttributesNonVolatile(EndpointId endpoin
static constexpr size_t kLevelControlStateTableSize =
EMBER_AF_LEVEL_CONTROL_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT;

struct CallbackScheduleState
{
System::Clock::Timestamp idealTimestamp; // The ideal time-stamp for the next callback to be scheduled.
System::Clock::Milliseconds32 runTime; // The duration of the previous scheduled callback function.
// e.g. running time of emberAfLevelControlClusterServerTickCallback
// when called consecutively
};

typedef struct
{
CommandId commandId;
Expand All @@ -83,6 +91,7 @@ typedef struct
uint32_t eventDurationMs;
uint32_t transitionTimeMs;
uint32_t elapsedTimeMs;
CallbackScheduleState callbackSchedule;
} EmberAfLevelControlState;

static EmberAfLevelControlState stateTable[kLevelControlStateTableSize];
Expand Down Expand Up @@ -116,6 +125,46 @@ static void timerCallback(System::Layer *, void * callbackContext)
emberAfLevelControlClusterServerTickCallback(static_cast<EndpointId>(reinterpret_cast<uintptr_t>(callbackContext)));
}

static uint32_t computeCallbackWaitTimeMs(CallbackScheduleState & callbackSchedule, uint32_t delayMs)
{
auto delay = System::Clock::Milliseconds32(delayMs);
auto waitTime = delay;
const auto currentTime = System::SystemClock().GetMonotonicTimestamp();

// Subsequent call
if (callbackSchedule.runTime.count())
{
// Check whether the previous scheduled callback was late and whether its running time
// is smaller than the desired delay
// If the running time of the scheduled callback is greater than the desired delay
// then do nothing; do not flood the event loop if the device is not fast enough
if ((currentTime > callbackSchedule.idealTimestamp) && (callbackSchedule.runTime < delay))
{
System::Clock::Timestamp latency = currentTime - callbackSchedule.idealTimestamp;

if (latency >= delay)
{
waitTime = System::Clock::Milliseconds32(0);
}
else
{
waitTime -= latency;
}
}
}
// First-time call
else
{
// initialize idealTimestamp
callbackSchedule.idealTimestamp = currentTime;
}

callbackSchedule.idealTimestamp += System::Clock::Milliseconds32(delayMs);
callbackSchedule.runTime = System::Clock::Milliseconds32(0);

return waitTime.count();
}

static void schedule(EndpointId endpoint, uint32_t delayMs)
{
DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds32(delayMs), timerCallback,
Expand Down Expand Up @@ -159,6 +208,7 @@ void emberAfLevelControlClusterServerTickCallback(EndpointId endpoint)
EmberAfLevelControlState * state = getState(endpoint);
EmberAfStatus status;
app::DataModel::Nullable<uint8_t> currentLevel;
const auto callbackStartTimestamp = System::SystemClock().GetMonotonicTimestamp();

if (state == nullptr)
{
Expand All @@ -173,6 +223,7 @@ void emberAfLevelControlClusterServerTickCallback(EndpointId endpoint)
if (status != EMBER_ZCL_STATUS_SUCCESS || currentLevel.IsNull())
{
emberAfLevelControlClusterPrintln("ERR: reading current level %x", status);
state->callbackSchedule.runTime = System::Clock::Milliseconds32(0);
writeRemainingTime(endpoint, 0);
return;
}
Expand Down Expand Up @@ -205,6 +256,7 @@ void emberAfLevelControlClusterServerTickCallback(EndpointId endpoint)
if (status != EMBER_ZCL_STATUS_SUCCESS)
{
emberAfLevelControlClusterPrintln("ERR: writing current level %x", status);
state->callbackSchedule.runTime = System::Clock::Milliseconds32(0);
writeRemainingTime(endpoint, 0);
return;
}
Expand Down Expand Up @@ -242,12 +294,14 @@ void emberAfLevelControlClusterServerTickCallback(EndpointId endpoint)
}
}

state->callbackSchedule.runTime = System::Clock::Milliseconds32(0);
writeRemainingTime(endpoint, 0);
}
else
{
state->callbackSchedule.runTime = System::SystemClock().GetMonotonicTimestamp() - callbackStartTimestamp;
writeRemainingTime(endpoint, static_cast<uint16_t>(state->transitionTimeMs - state->elapsedTimeMs));
schedule(endpoint, state->eventDurationMs);
schedule(endpoint, computeCallbackWaitTimeMs(state->callbackSchedule, state->eventDurationMs));
}
}

Expand Down Expand Up @@ -677,8 +731,10 @@ static EmberAfStatus moveToLevelHandler(EndpointId endpoint, CommandId commandId

state->storedLevel = storedLevel;

state->callbackSchedule.runTime = System::Clock::Milliseconds32(0);

// The setup was successful, so mark the new state as active and return.
schedule(endpoint, state->eventDurationMs);
schedule(endpoint, computeCallbackWaitTimeMs(state->callbackSchedule, state->eventDurationMs));
status = EMBER_ZCL_STATUS_SUCCESS;

if (commandId == Commands::MoveToLevelWithOnOff::Id)
Expand Down Expand Up @@ -803,8 +859,10 @@ static void moveHandler(EndpointId endpoint, CommandId commandId, uint8_t moveMo
// storedLevel is not used for Move commands.
state->storedLevel = INVALID_STORED_LEVEL;

state->callbackSchedule.runTime = System::Clock::Milliseconds32(0);

// The setup was successful, so mark the new state as active and return.
schedule(endpoint, state->eventDurationMs);
schedule(endpoint, computeCallbackWaitTimeMs(state->callbackSchedule, state->eventDurationMs));
status = EMBER_ZCL_STATUS_SUCCESS;

send_default_response:
Expand Down Expand Up @@ -929,8 +987,10 @@ static void stepHandler(EndpointId endpoint, CommandId commandId, uint8_t stepMo
// storedLevel is not used for Step commands
state->storedLevel = INVALID_STORED_LEVEL;

state->callbackSchedule.runTime = System::Clock::Milliseconds32(0);

// The setup was successful, so mark the new state as active and return.
schedule(endpoint, state->eventDurationMs);
schedule(endpoint, computeCallbackWaitTimeMs(state->callbackSchedule, state->eventDurationMs));
status = EMBER_ZCL_STATUS_SUCCESS;

send_default_response:
Expand Down

0 comments on commit 3992728

Please sign in to comment.