implement a more robust method of determining dominant member
This commit is contained in:
parent
3672b1d49f
commit
4a52f7302d
|
@ -23,6 +23,7 @@
|
|||
*/
|
||||
|
||||
#include "arbitratinguniverse.h"
|
||||
#include <map>
|
||||
|
||||
namespace sACN {
|
||||
|
||||
|
@ -282,45 +283,69 @@ double ArbitratingUniverse::rxRate()
|
|||
*/
|
||||
std::shared_ptr<Universe> ArbitratingUniverse::dominant_()
|
||||
{
|
||||
bool sourceListChanged = false;
|
||||
std::shared_ptr<Universe> ret = nullptr;
|
||||
if (sources_.empty())
|
||||
return nullptr;
|
||||
|
||||
for(auto it = sources_.begin(); it != sources_.end(); )
|
||||
// cache the age of each universe
|
||||
std::unordered_map<DATA::data_header,uint> ages;
|
||||
for (const auto& [header, universe] : sources_)
|
||||
ages.insert({header, universe->rxAge()});
|
||||
|
||||
purge_stale_sources_(&ages);
|
||||
|
||||
// order universe into a two dimentional container; priority then age
|
||||
std::map<uint,std::multimap<uint,std::shared_ptr<Universe>>> by_priority;
|
||||
for (const auto& [header, universe] : sources_)
|
||||
{
|
||||
auto universe = it->second;
|
||||
|
||||
if (!ret && HoldLastLook)
|
||||
{ // anything is better than nothing
|
||||
++it;
|
||||
ret = universe;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto age = universe->rxAge();
|
||||
|
||||
if (age > E131_NETWORK_DATA_LOSS_TIMEOUT)
|
||||
{ // clean up zombie universes
|
||||
it = sources_.erase(it);
|
||||
sourceListChanged = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (age > DMX::E111_DATA_LOSS_TIMEOUT)
|
||||
{
|
||||
++it;
|
||||
continue; // stale universes cannot be dominant
|
||||
}
|
||||
|
||||
if (universe->provenance()->priority > ret->provenance()->priority)
|
||||
ret = universe; // rank by provenance
|
||||
|
||||
++it;
|
||||
auto age = ages.at(header);
|
||||
auto priority = header.priority;
|
||||
if (!by_priority.count(priority))
|
||||
by_priority.emplace(priority, std::multimap<uint,std::shared_ptr<Universe>>({{age,universe}}));
|
||||
else
|
||||
by_priority.at(priority).insert({age, universe});
|
||||
}
|
||||
|
||||
if (sourceListChanged)
|
||||
doListChangeCallbacks();
|
||||
// freshest universe at the hightest priority
|
||||
return by_priority.rbegin()->second.begin()->second;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
/**
|
||||
* @brief ArbitratingUniverse::purge_stale_sources_
|
||||
* @param age_cache
|
||||
*/
|
||||
void ArbitratingUniverse::purge_stale_sources_(std::unordered_map<DATA::data_header,uint> *age_cache)
|
||||
{
|
||||
std::unordered_map<DATA::data_header,uint> ages;
|
||||
if (age_cache)
|
||||
ages = *age_cache;
|
||||
else
|
||||
for (const auto& [header, universe] : sources_)
|
||||
ages.insert({header, universe->rxAge()});
|
||||
|
||||
// order the member universes by age
|
||||
std::multimap<uint,DATA::data_header> by_age;
|
||||
for (const auto& [header, _] : sources_)
|
||||
by_age.insert({ages.at(header), header});
|
||||
|
||||
// clean up stale universes, oldest first
|
||||
for(auto it = by_age.crbegin(); it != by_age.crend(); it++)
|
||||
{
|
||||
auto age = it->first;
|
||||
auto key = it->second;
|
||||
// the lastest source universe is never purged if holding the last look
|
||||
if (sources_.size() == 1 && HoldLastLook)
|
||||
break;
|
||||
// it's a sorted container. If this universe is live, so are the remainder.
|
||||
if (age < E131_NETWORK_DATA_LOSS_TIMEOUT)
|
||||
break;
|
||||
// erase the zombie universe
|
||||
sources_.erase(key);
|
||||
}
|
||||
|
||||
// if zombies were erased, notify about the changes
|
||||
if (sources_.size() != by_age.size())
|
||||
doListChangeCallbacks();
|
||||
}
|
||||
|
||||
} // namespace SACN
|
||||
|
|
|
@ -102,6 +102,7 @@ private:
|
|||
bool HoldLastLook;
|
||||
|
||||
std::shared_ptr<Universe> dominant_();
|
||||
void purge_stale_sources_(std::unordered_map<DATA::data_header, uint> * = nullptr);
|
||||
bool hasSourceUniverse(const DATA::data_header&) const;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue