// ====================================================================== // \title RateLimiter.cpp // \author vwong // \brief cpp file for a rate limiter utility class // // \copyright // Copyright (C) 2009-2020 California Institute of Technology. // ALL RIGHTS RESERVED. United States Government Sponsorship // acknowledged. // ====================================================================== #include namespace Utils { RateLimiter ::RateLimiter(U32 counterCycle, U32 timeCycle) : m_counterCycle(counterCycle), m_timeCycle(timeCycle) { this->reset(); } RateLimiter ::RateLimiter() : m_counterCycle(0), m_timeCycle(0) { this->reset(); } void RateLimiter ::setCounterCycle(U32 counterCycle) { this->m_counterCycle = counterCycle; } void RateLimiter ::setTimeCycle(U32 timeCycle) { this->m_timeCycle = timeCycle; } void RateLimiter ::reset() { this->resetCounter(); this->resetTime(); } void RateLimiter ::resetCounter() { this->m_counter = 0; } void RateLimiter ::resetTime() { this->m_time = Fw::Time(); this->m_timeAtNegativeInfinity = true; } void RateLimiter ::setCounter(U32 counter) { this->m_counter = counter; } void RateLimiter ::setTime(Fw::Time time) { this->m_time = time; this->m_timeAtNegativeInfinity = false; } bool RateLimiter ::trigger(Fw::Time time) { // NB: this implements a 4-bit decision, logically equivalent to this pseudo-code // // A = HAS_COUNTER, B = HAS_TIME, C = COUNTER_TRIGGER, D = TIME_TRIGGER // // if (!A && !B) => true // if (A && B) => C || D // if (A) => C // if (B) => D // false // if (this->m_counterCycle == 0 && this->m_timeCycle == 0) { return true; } // evaluate trigger criteria bool shouldTrigger = false; if (this->m_counterCycle > 0) { shouldTrigger = shouldTrigger || this->shouldCounterTrigger(); } if (this->m_timeCycle > 0) { shouldTrigger = shouldTrigger || this->shouldTimeTrigger(time); } // update states if (this->m_counterCycle > 0) { this->updateCounter(shouldTrigger); } if (this->m_timeCycle > 0) { this->updateTime(shouldTrigger, time); } return shouldTrigger; } bool RateLimiter ::trigger() { FW_ASSERT(this->m_timeCycle == 0); return trigger(Fw::Time::zero()); } bool RateLimiter ::shouldCounterTrigger() { FW_ASSERT(this->m_counterCycle > 0); // trigger at 0 bool shouldTrigger = (this->m_counter == 0); return shouldTrigger; } bool RateLimiter ::shouldTimeTrigger(Fw::Time time) { FW_ASSERT(this->m_timeCycle > 0); // trigger at prev trigger time + time cycle seconds OR when time is at negative infinity Fw::Time timeCycle = Fw::Time(this->m_timeCycle, 0); Fw::Time nextTrigger = Fw::Time::add(this->m_time, timeCycle); bool shouldTrigger = (time >= nextTrigger) || this->m_timeAtNegativeInfinity; return shouldTrigger; } void RateLimiter ::updateCounter(bool triggered) { FW_ASSERT(this->m_counterCycle > 0); if (triggered) { // triggered, set to next state this->m_counter = 1; } else { // otherwise, just increment and maybe wrap if (++this->m_counter >= this->m_counterCycle) { this->m_counter = 0; } } } void RateLimiter ::updateTime(bool triggered, Fw::Time time) { FW_ASSERT(this->m_timeCycle > 0); if (triggered) { // mark time of trigger this->m_time = time; } this->m_timeAtNegativeInfinity = false; } } // end namespace Utils