| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // ====================================================================== | ||
| 2 | // \title RateLimiter.cpp | ||
| 3 | // \author vwong | ||
| 4 | // \brief cpp file for a rate limiter utility class | ||
| 5 | // | ||
| 6 | // \copyright | ||
| 7 | // Copyright (C) 2009-2020 California Institute of Technology. | ||
| 8 | // ALL RIGHTS RESERVED. United States Government Sponsorship | ||
| 9 | // acknowledged. | ||
| 10 | // ====================================================================== | ||
| 11 | |||
| 12 | #include <Utils/RateLimiter.hpp> | ||
| 13 | |||
| 14 | namespace Utils { | ||
| 15 | |||
| 16 | ✗ | RateLimiter ::RateLimiter(U32 counterCycle, U32 timeCycle) : m_counterCycle(counterCycle), m_timeCycle(timeCycle) { | |
| 17 | ✗ | this->reset(); | |
| 18 | ✗ | } | |
| 19 | |||
| 20 | ✗ | RateLimiter ::RateLimiter() : m_counterCycle(0), m_timeCycle(0) { | |
| 21 | ✗ | this->reset(); | |
| 22 | ✗ | } | |
| 23 | |||
| 24 | ✗ | void RateLimiter ::setCounterCycle(U32 counterCycle) { | |
| 25 | ✗ | this->m_counterCycle = counterCycle; | |
| 26 | ✗ | } | |
| 27 | |||
| 28 | ✗ | void RateLimiter ::setTimeCycle(U32 timeCycle) { | |
| 29 | ✗ | this->m_timeCycle = timeCycle; | |
| 30 | ✗ | } | |
| 31 | |||
| 32 | ✗ | void RateLimiter ::reset() { | |
| 33 | ✗ | this->resetCounter(); | |
| 34 | ✗ | this->resetTime(); | |
| 35 | ✗ | } | |
| 36 | |||
| 37 | ✗ | void RateLimiter ::resetCounter() { | |
| 38 | ✗ | this->m_counter = 0; | |
| 39 | ✗ | } | |
| 40 | |||
| 41 | ✗ | void RateLimiter ::resetTime() { | |
| 42 | ✗ | this->m_time = Fw::Time(); | |
| 43 | ✗ | this->m_timeAtNegativeInfinity = true; | |
| 44 | ✗ | } | |
| 45 | |||
| 46 | ✗ | void RateLimiter ::setCounter(U32 counter) { | |
| 47 | ✗ | this->m_counter = counter; | |
| 48 | ✗ | } | |
| 49 | |||
| 50 | ✗ | void RateLimiter ::setTime(Fw::Time time) { | |
| 51 | ✗ | this->m_time = time; | |
| 52 | ✗ | this->m_timeAtNegativeInfinity = false; | |
| 53 | ✗ | } | |
| 54 | |||
| 55 | ✗ | bool RateLimiter ::trigger(Fw::Time time) { | |
| 56 | // NB: this implements a 4-bit decision, logically equivalent to this pseudo-code | ||
| 57 | // | ||
| 58 | // A = HAS_COUNTER, B = HAS_TIME, C = COUNTER_TRIGGER, D = TIME_TRIGGER | ||
| 59 | // | ||
| 60 | // if (!A && !B) => true | ||
| 61 | // if (A && B) => C || D | ||
| 62 | // if (A) => C | ||
| 63 | // if (B) => D | ||
| 64 | // false | ||
| 65 | // | ||
| 66 | ✗ | if (this->m_counterCycle == 0 && this->m_timeCycle == 0) { | |
| 67 | ✗ | return true; | |
| 68 | } | ||
| 69 | |||
| 70 | // evaluate trigger criteria | ||
| 71 | ✗ | bool shouldTrigger = false; | |
| 72 | ✗ | if (this->m_counterCycle > 0) { | |
| 73 | ✗ | shouldTrigger = shouldTrigger || this->shouldCounterTrigger(); | |
| 74 | } | ||
| 75 | ✗ | if (this->m_timeCycle > 0) { | |
| 76 | ✗ | shouldTrigger = shouldTrigger || this->shouldTimeTrigger(time); | |
| 77 | } | ||
| 78 | |||
| 79 | // update states | ||
| 80 | ✗ | if (this->m_counterCycle > 0) { | |
| 81 | ✗ | this->updateCounter(shouldTrigger); | |
| 82 | } | ||
| 83 | ✗ | if (this->m_timeCycle > 0) { | |
| 84 | ✗ | this->updateTime(shouldTrigger, time); | |
| 85 | } | ||
| 86 | |||
| 87 | ✗ | return shouldTrigger; | |
| 88 | } | ||
| 89 | |||
| 90 | ✗ | bool RateLimiter ::trigger() { | |
| 91 | ✗ | FW_ASSERT(this->m_timeCycle == 0); | |
| 92 | ✗ | return trigger(Fw::Time::zero()); | |
| 93 | } | ||
| 94 | |||
| 95 | ✗ | bool RateLimiter ::shouldCounterTrigger() { | |
| 96 | ✗ | FW_ASSERT(this->m_counterCycle > 0); | |
| 97 | |||
| 98 | // trigger at 0 | ||
| 99 | ✗ | bool shouldTrigger = (this->m_counter == 0); | |
| 100 | |||
| 101 | ✗ | return shouldTrigger; | |
| 102 | } | ||
| 103 | |||
| 104 | ✗ | bool RateLimiter ::shouldTimeTrigger(Fw::Time time) { | |
| 105 | ✗ | FW_ASSERT(this->m_timeCycle > 0); | |
| 106 | |||
| 107 | // trigger at prev trigger time + time cycle seconds OR when time is at negative infinity | ||
| 108 | ✗ | Fw::Time timeCycle = Fw::Time(this->m_timeCycle, 0); | |
| 109 | ✗ | Fw::Time nextTrigger = Fw::Time::add(this->m_time, timeCycle); | |
| 110 | ✗ | bool shouldTrigger = (time >= nextTrigger) || this->m_timeAtNegativeInfinity; | |
| 111 | |||
| 112 | ✗ | return shouldTrigger; | |
| 113 | ✗ | } | |
| 114 | |||
| 115 | ✗ | void RateLimiter ::updateCounter(bool triggered) { | |
| 116 | ✗ | FW_ASSERT(this->m_counterCycle > 0); | |
| 117 | |||
| 118 | ✗ | if (triggered) { | |
| 119 | // triggered, set to next state and maybe wrap (handles counterCycle == 1) | ||
| 120 | ✗ | this->m_counter = 1; | |
| 121 | ✗ | if (this->m_counter >= this->m_counterCycle) { | |
| 122 | ✗ | this->m_counter = 0; | |
| 123 | } | ||
| 124 | |||
| 125 | } else { | ||
| 126 | // otherwise, just increment and maybe wrap | ||
| 127 | ✗ | this->m_counter++; | |
| 128 | ✗ | if (this->m_counter >= this->m_counterCycle) { | |
| 129 | ✗ | this->m_counter = 0; | |
| 130 | } | ||
| 131 | } | ||
| 132 | ✗ | } | |
| 133 | |||
| 134 | ✗ | void RateLimiter ::updateTime(bool triggered, Fw::Time time) { | |
| 135 | ✗ | FW_ASSERT(this->m_timeCycle > 0); | |
| 136 | |||
| 137 | ✗ | if (triggered) { | |
| 138 | // mark time of trigger | ||
| 139 | ✗ | this->m_time = time; | |
| 140 | } | ||
| 141 | ✗ | this->m_timeAtNegativeInfinity = false; | |
| 142 | ✗ | } | |
| 143 | |||
| 144 | } // end namespace Utils | ||
| 145 |