dezibot/src/power/Power.cpp

123 lines
3.2 KiB
C++

/**
* @file Power.h
* @author Phillip Kühne
* @brief This component provides utilities for keeping track of power usage
* consumption.
* @version 0.1
* @date 2024-11-23
*/
#include "Power.h"
static portMUX_TYPE mux;
Power::Power()
{
this->freePowerBudget = this->totalPowerBudget;
}
void Power::begin()
{
if (powerInstance == nullptr)
{
// Double check locking https://www.aristeia.com/Papers/DDJ%5FJul%5FAug%5F2004%5Frevised.pdf
taskENTER_CRITICAL(&mux);
if (powerInstance == nullptr)
{
powerInstance = new Power();
}
taskEXIT_CRITICAL(&mux);
}
else
{
// Power.begin() was called twice!
Serial.println("Power.begin() was called twice! No harm done, but this is a bug.");
}
}
Power *Power::getPowerManager()
{
return powerInstance;
}
bool Power::tryAccquirePowerAllowance(uint16_t neededPower)
{
if (this->freePowerBudget >= neededPower)
{
this->freePowerBudget -= neededPower;
return true;
}
else
{
return false;
}
}
void Power::releasePower(uint16_t power)
{
if (this->freePowerBudget + power <= this->totalPowerBudget)
{
this->freePowerBudget += power;
}
else // TODO: Maybe we should actually throw an error here, since obviouslsy someone used us wrong.
{
this->freePowerBudget = this->totalPowerBudget;
}
// Check if there are tasks waiting for power
checkWaitingTasks();
}
bool Power::waitForPowerAllowance(uint16_t neededPower, TickType_t ticksToWait)
{
if (tryAccquirePowerAllowance(neededPower))
{
return true;
}
else
{
// Suspend the task while waiting for power to be available
TaskHandle_t currentTask = xTaskGetCurrentTaskHandle();
TickType_t initialTickCount = xTaskGetTickCount();
waitingTasks.push(currentTask);
uint32_t notificationValue;
BaseType_t notificationStatus = xTaskNotifyWait(0, 0, &notificationValue, ticksToWait);
// Code below will be executed after the task is woken up
while (notificationStatus == pdPASS)
{
if (notificationValue == POWER_AVAILABLE)
{
// We were woken up because new power is available, check if it is enough
if (tryAccquirePowerAllowance(neededPower))
{
return true;
}
else
{
// Still not enough power available for us. Wait the remaining ticks.
xTaskNotifyWait(0, 0, &notificationValue, ticksToWait - (xTaskGetTickCount() - initialTickCount));
}
}
}
if (notificationStatus == pdFALSE)
{
// We waited long enough...
return false;
}
else
{
// Should be impossible to reach
throw "Reached impossible state";
}
}
}
void Power::checkWaitingTasks(void)
{
// Check if there are tasks waiting for power
if (!waitingTasks.empty())
{
TaskHandle_t task = waitingTasks.front();
waitingTasks.pop();
xTaskNotify(task, POWER_AVAILABLE, eSetValueWithOverwrite);
}
}