68 class EMANE::NeighborMetricManager::Implementation
73 lastNeighborStatusUpdateTime_{}
82 neighborDeleteAgeMicroseconds_ = ageMicroseconds;
86 void handleTxActivity(
NEMId dst, std::uint64_t u64DataRatebps,
const TimePoint & txTime)
91 handleR2RITxActivity(dst, u64DataRatebps, txTime);
94 handleNeighborTxActivity(dst, u64DataRatebps, txTime);
99 std::uint64_t u64SeqNum,
103 handleR2RIRxActivity(src, u64SeqNum, uuid, rxTime);
105 updateNeighborRxActivity(src, u64SeqNum, uuid, rxTime);
111 std::uint64_t u64SeqNum,
117 std::uint64_t u64DataRatebps)
119 handleR2RIRxActivity(src,
125 durationMicroseconds,
128 updateNeighborRxActivity(src,
134 durationMicroseconds,
140 void handleNeighborStatusUpdate()
144 StatisticTable<NEMId> * pTable = pStatisticNeighborStatusTable_;
146 for(
auto iter : neighborDataTable_)
148 NeighborData * pNeighborMetric = iter.second.first;
150 float fBWUtilzationRatio = (lastNeighborStatusUpdateTime_ ==
EMANE::TimePoint{}) ? 0.0f :
151 std::chrono::duration_cast<EMANE::DoubleSeconds>(pNeighborMetric->rxUtilizationMicroseconds_).count() /
152 std::chrono::duration_cast<
EMANE::DoubleSeconds>(currentTime - lastNeighborStatusUpdateTime_).count();
154 float fRxAge = (! pNeighborMetric->bHaveEverHadRxActivity_) ? 0.0f :
155 std::chrono::duration_cast<DoubleSeconds>(
156 currentTime - pNeighborMetric->lastRxTime_).count();
158 float fSINRAvg = getAvg(pNeighborMetric->fSINRSum_, pNeighborMetric->u64NumRxFrames_);
160 float fNoiseFloorAvg = getAvg(pNeighborMetric->fNoiseFloorSum_, pNeighborMetric->u64NumRxFrames_);
162 pTable->setCell(iter.first,
163 NBR_STATUS_TX_FRAMES,
164 Any{pNeighborMetric->u64NumTxFrames_});
166 pTable->setCell(iter.first,
167 NBR_STATUS_RX_FRAMES,
168 Any{pNeighborMetric->u64NumRxFrames_});
170 pTable->setCell(iter.first,
171 NBR_STATUS_MISSED_FRAMES,
172 Any{pNeighborMetric->u64NumRxMissedFrames_});
174 pTable->setCell(iter.first,
175 NBR_STATUS_BW_CONSUMPTION,
176 Any{fBWUtilzationRatio});
178 pTable->setCell(iter.first,
182 pTable->setCell(iter.first,
183 NBR_STATUS_NOISE_FLOOR_AVG,
184 Any{fNoiseFloorAvg});
186 pTable->setCell(iter.first,
190 clearData_i(pNeighborMetric);
193 lastNeighborStatusUpdateTime_ = currentTime;
204 for(
auto iter = r2riMetricTable_.begin(); iter != r2riMetricTable_.end(); )
208 std::chrono::duration_cast<
Microseconds>(currentTime - iter->second->lastRxTime_)};
211 if(ageMicroseconds > neighborDeleteAgeMicroseconds_)
213 delete (iter->second);
216 r2riMetricTable_.erase(iter++);
220 float fSINRAvg{}, fSINRStd{}, fNoiseFloorAvg{}, fNoiseFloorStdv{};
223 getAvgAndStd(iter->second->fSINRSum_,
224 iter->second->fSINRSum2_,
225 iter->second->u64NumRxFrames_,
230 getAvgAndStd(iter->second->fNoiseFloorSum_,
231 iter->second->fNoiseFloorSum2_,
232 iter->second->u64NumRxFrames_,
237 neighborMetric{iter->first,
238 iter->second->u64NumRxFrames_,
239 iter->second->u64NumTxFrames_,
240 iter->second->u64NumRxMissedFrames_,
241 iter->second->rxUtilizationMicroseconds_,
246 iter->second->u64RxDataRateAvg_,
247 iter->second->u64TxDataRateAvg_};
249 neighborMetrics.push_back(neighborMetric);
251 clearData_i(iter->second);
258 return neighborMetrics;
263 pStatisticNeighborMetricTable_ =
264 statisticRegistrar.registerTable<
NEMId>(
"NeighborMetricTable",
265 sNeighborMetricLables,
267 "Neighbor Metric Table");
269 pStatisticNeighborStatusTable_ =
270 statisticRegistrar.registerTable<
NEMId>(
"NeighborStatusTable",
271 sNeighborStatusLables,
273 "Neighbor Status Table");
279 enum NeighborMetricLables { NBR_METRIC_RX_FRAMES = 1,
280 NBR_METRIC_TX_FRAMES = 2,
281 NBR_METRIC_MISSED_FRAMES = 3,
282 NBR_METRIC_BW_CONSUMPTION = 4,
283 NBR_METRIC_LAST_RX_TIME = 5,
284 NBR_METRIC_LAST_TX_TIME = 6,
285 NBR_METRIC_SINR_AVG = 7,
286 NBR_METRIC_SINR_STDV = 8,
287 NBR_METRIC_NOISE_FLOOR_AVG = 9,
288 NBR_METRIC_NOISE_FLOOR_STDV = 10,
289 NBR_METRIC_RX_DATARATE_AVG = 11,
290 NBR_METRIC_TX_DATARATE_AVG = 12};
293 enum NeighborStatusLables { NBR_STATUS_RX_FRAMES = 1,
294 NBR_STATUS_TX_FRAMES = 2,
295 NBR_STATUS_MISSED_FRAMES = 3,
296 NBR_STATUS_BW_CONSUMPTION = 4,
297 NBR_STATUS_SINR_AVG = 5,
298 NBR_STATUS_NOISE_FLOOR_AVG = 6,
299 NBR_STATUS_RX_AGE = 7};
303 struct NeighborData {
306 std::uint64_t u64LastRxSeqNum_;
308 bool bHaveEverHadRxActivity_;
313 std::uint64_t u64NumRxFrames_;
314 std::uint64_t u64NumRxMissedFrames_;
315 std::uint64_t u64NumTxFrames_;
321 float fNoiseFloorSum_;
322 float fNoiseFloorSum2_;
324 std::uint64_t u64RxDataRateMin_;
325 std::uint64_t u64RxDataRateMax_;
326 std::uint64_t u64RxDataRateAvg_;
328 std::uint64_t u64TxDataRateMin_;
329 std::uint64_t u64TxDataRateMax_;
330 std::uint64_t u64TxDataRateAvg_;
334 NeighborData(
NEMId nemId) :
337 bHaveEverHadRxActivity_{},
341 u64NumRxMissedFrames_{},
343 rxUtilizationMicroseconds_{},
360 using NeighborDataMap = std::map<EMANE::NEMId, NeighborData *>;
363 using NeighborDataPair = std::pair<NeighborData *, NeighborData *>;
365 using NeighborDataPairMap = std::map<EMANE::NEMId, NeighborDataPair>;
367 using Counter = std::uint64_t;
372 NeighborDataMap r2riMetricTable_;
375 NeighborDataPairMap neighborDataTable_;
380 StatisticTable<NEMId> * pStatisticNeighborStatusTable_;
383 StatisticTable<NEMId> * pStatisticNeighborMetricTable_;
389 NeighborData * lookupR2RIMetric(
NEMId nemId)
391 auto iter = r2riMetricTable_.find(nemId);
393 if(iter == r2riMetricTable_.end())
396 iter = r2riMetricTable_.insert(std::make_pair(nemId,
new NeighborData{nemId})).first;
403 NeighborDataPair lookupNeighborData(
NEMId nemId)
405 auto iter = neighborDataTable_.find(nemId);
407 if(iter == neighborDataTable_.end())
410 iter = neighborDataTable_.insert(std::make_pair(nemId,
411 NeighborDataPair{
new NeighborData{nemId},
new NeighborData{nemId}})).first;
415 std::vector<Any> v1{
Any{nemId}};
418 v1.insert(v1.end(), 4,
Any{Counter{}});
421 v1.insert(v1.end(), sNeighborMetricLables.size() - 5,
Any{
float{}});
424 pStatisticNeighborMetricTable_->addRow(nemId, v1);
429 std::vector<Any> v2{
Any{nemId}};
432 v2.insert(v2.end(), 3,
Any{Counter{}});
435 v2.insert(v2.end(), sNeighborStatusLables.size() - 4,
Any{
float{}});
438 pStatisticNeighborStatusTable_->addRow(nemId, v2);
445 void handleR2RITxActivity(
NEMId dst, std::uint64_t u64DataRatebps,
const TimePoint & txTime)
447 updateTxActivityData_i(lookupR2RIMetric(dst), u64DataRatebps, txTime);
451 void handleNeighborTxActivity(
NEMId dst, std::uint64_t u64DataRatebps,
const TimePoint & txTime)
453 auto neighborDataPair = lookupNeighborData(dst);
456 updateTxActivityData_i(neighborDataPair.first, u64DataRatebps, txTime);
458 updateTxActivityData_i(neighborDataPair.second, u64DataRatebps, txTime);
461 updateTxActivityNeighborMetricStatistics_i(neighborDataPair.second);
465 void handleR2RIRxActivity(
NEMId src,
466 std::uint64_t u64SeqNum,
470 updateRxActivityData_i(lookupR2RIMetric(src), u64SeqNum, uuid, rxTime);
474 void handleR2RIRxActivity(
NEMId src,
475 std::uint64_t u64SeqNum,
481 std::uint64_t u64DataRatebps)
483 NeighborData * pNeighborMetric = lookupR2RIMetric(src);
485 updateRxActivityData_i(pNeighborMetric, u64SeqNum, uuid, rxTime);
487 updateRxActivityChannelData_i(pNeighborMetric, fSINR, fNoiseFloor, durationMicroseconds, u64DataRatebps);
491 void updateNeighborRxActivity(
NEMId src,
492 std::uint64_t u64SeqNum,
496 auto neighborDataPair = lookupNeighborData(src);
499 updateRxActivityData_i(neighborDataPair.first, u64SeqNum, uuid, rxTime);
501 updateRxActivityData_i(neighborDataPair.second, u64SeqNum, uuid, rxTime);
504 updateRxActivityNeighborMetricStatistics_i(neighborDataPair.second);
509 void updateNeighborRxActivity(
NEMId src,
510 std::uint64_t u64SeqNum,
516 std::uint64_t u64DataRatebps)
518 auto neighborDataPair = lookupNeighborData(src);
521 updateRxActivityData_i(neighborDataPair.first, u64SeqNum, uuid, rxTime);
523 updateRxActivityData_i(neighborDataPair.second, u64SeqNum, uuid, rxTime);
525 updateRxActivityChannelData_i(neighborDataPair.first, fSINR, fNoiseFloor, durationMicroseconds, u64DataRatebps);
527 updateRxActivityChannelData_i(neighborDataPair.second, fSINR, fNoiseFloor, durationMicroseconds, u64DataRatebps);
530 updateRxActivityNeighborMetricStatistics_i(neighborDataPair.second);
532 updateRxActivityChannelStatistics_i(neighborDataPair.second);
537 void updateTxActivityData_i(NeighborData * pNeighborMetric, std::uint64_t u64DataRatebps,
const TimePoint & txTime)
539 pNeighborMetric->u64NumTxFrames_ += 1;
541 pNeighborMetric->lastTxTime_ = txTime;
543 updateRunningAverage(pNeighborMetric->u64TxDataRateAvg_,
544 pNeighborMetric->u64NumTxFrames_,
547 updateMinMax(pNeighborMetric->u64TxDataRateMin_, pNeighborMetric->u64TxDataRateMax_, u64DataRatebps);
554 void updateTxActivityNeighborMetricStatistics_i(NeighborData * pNeighborMetric)
556 StatisticTable<NEMId> * pTable = pStatisticNeighborMetricTable_;
559 pTable->setCell(pNeighborMetric->nemId_,
560 NBR_METRIC_TX_FRAMES,
561 Any{pNeighborMetric->u64NumTxFrames_});
563 pTable->setCell(pNeighborMetric->nemId_,
564 NBR_METRIC_TX_DATARATE_AVG,
565 Any{pNeighborMetric->u64TxDataRateAvg_});
567 pTable->setCell(pNeighborMetric->nemId_,
568 NBR_METRIC_LAST_TX_TIME,
569 Any{std::chrono::duration_cast<DoubleSeconds>(
570 pNeighborMetric->lastTxTime_.time_since_epoch()).count()});
574 void updateRxActivityData_i(NeighborData * pNeighborMetric,
575 std::uint64_t u64SeqNum,
579 if(!uuid_compare(uuid,pNeighborMetric->uuid_))
581 if(u64SeqNum > pNeighborMetric->u64LastRxSeqNum_)
583 pNeighborMetric->u64NumRxMissedFrames_ +=
584 u64SeqNum - pNeighborMetric->u64LastRxSeqNum_ - 1;
586 pNeighborMetric->u64LastRxSeqNum_ = u64SeqNum;
591 if(pNeighborMetric->u64NumRxMissedFrames_)
593 pNeighborMetric->u64NumRxMissedFrames_ -= 1;
600 uuid_copy(pNeighborMetric->uuid_,uuid);
602 pNeighborMetric->u64LastRxSeqNum_ = u64SeqNum;
605 pNeighborMetric->u64NumRxFrames_ += 1;
607 pNeighborMetric->lastRxTime_ = rxTime;
609 pNeighborMetric->bHaveEverHadRxActivity_ =
true;
613 void updateRxActivityNeighborMetricStatistics_i(NeighborData * pNeighborMetric)
615 StatisticTable<NEMId> * pTable = pStatisticNeighborMetricTable_;
618 pTable->setCell(pNeighborMetric->nemId_,
619 NBR_METRIC_RX_FRAMES,
620 Any{pNeighborMetric->u64NumRxFrames_});
622 pTable->setCell(pNeighborMetric->nemId_,
623 NBR_METRIC_MISSED_FRAMES,
624 Any{pNeighborMetric->u64NumRxMissedFrames_});
628 void updateRxActivityChannelData_i(NeighborData * pNeighborMetric,
632 std::uint64_t u64DataRatebps)
634 pNeighborMetric->rxUtilizationMicroseconds_ += durationMicroseconds;
636 pNeighborMetric->fSINRSum_ += fSINR;
637 pNeighborMetric->fSINRSum2_ += (fSINR * fSINR);
639 pNeighborMetric->fNoiseFloorSum_ += fNoiseFloor;
640 pNeighborMetric->fNoiseFloorSum2_ += (fNoiseFloor * fNoiseFloor);
642 updateMinMax(pNeighborMetric->u64RxDataRateMin_, pNeighborMetric->u64RxDataRateMax_, u64DataRatebps);
644 updateRunningAverage(pNeighborMetric->u64RxDataRateAvg_, pNeighborMetric->u64NumRxFrames_, u64DataRatebps);
648 void updateRxActivityChannelStatistics_i(NeighborData * pNeighborMetric)
650 float fSINRAvg{}, fSINRStdv{}, fNoiseFloorAvg{}, fNoiseFloorStdv{};
653 getAvgAndStd(pNeighborMetric->fSINRSum_,
654 pNeighborMetric->fSINRSum2_,
655 pNeighborMetric->u64NumRxFrames_,
660 getAvgAndStd(pNeighborMetric->fNoiseFloorSum_,
661 pNeighborMetric->fNoiseFloorSum2_,
662 pNeighborMetric->u64NumRxFrames_,
666 StatisticTable<NEMId> * pTable = pStatisticNeighborMetricTable_;
669 pTable->setCell(pNeighborMetric->nemId_,
670 NBR_METRIC_BW_CONSUMPTION,
671 Any{pNeighborMetric->rxUtilizationMicroseconds_.count()});
673 pTable->setCell(pNeighborMetric->nemId_,
674 NBR_METRIC_LAST_RX_TIME,
675 Any{std::chrono::duration_cast<DoubleSeconds>(
676 pNeighborMetric->lastRxTime_.time_since_epoch()).count()});
678 pTable->setCell(pNeighborMetric->nemId_,
682 pTable->setCell(pNeighborMetric->nemId_,
683 NBR_METRIC_SINR_STDV,
686 pTable->setCell(pNeighborMetric->nemId_,
687 NBR_METRIC_NOISE_FLOOR_AVG,
688 Any{fNoiseFloorAvg});
690 pTable->setCell(pNeighborMetric->nemId_,
691 NBR_METRIC_NOISE_FLOOR_STDV,
692 Any{fNoiseFloorStdv});
694 pTable->setCell(pNeighborMetric->nemId_,
695 NBR_METRIC_RX_DATARATE_AVG,
696 Any{pNeighborMetric->u64RxDataRateAvg_});
700 void clearData_i(NeighborData * pNeighborMetric)
705 pNeighborMetric->u64NumRxMissedFrames_ = 0;
708 pNeighborMetric->u64NumRxFrames_ = 0;
709 pNeighborMetric->u64NumTxFrames_ = 0;
712 pNeighborMetric->rxUtilizationMicroseconds_ = EMANE::Microseconds::zero();
715 pNeighborMetric->fSINRSum_ = 0.0f;
716 pNeighborMetric->fSINRSum2_ = 0.0f;
719 pNeighborMetric->fNoiseFloorSum_ = 0.0f;
720 pNeighborMetric->fNoiseFloorSum2_ = 0.0f;
723 pNeighborMetric->u64RxDataRateMin_ = 0;
724 pNeighborMetric->u64RxDataRateMax_ = 0;
725 pNeighborMetric->u64RxDataRateAvg_ = 0;
727 pNeighborMetric->u64TxDataRateMin_ = 0;
728 pNeighborMetric->u64TxDataRateMax_ = 0;
729 pNeighborMetric->u64TxDataRateAvg_ = 0;
733 float getAvg(
float fSum,
size_t numSamples)
737 return fSum / numSamples;
739 else if(numSamples == 1)
750 void getAvgAndStd(
float fSum,
float fSumSquared,
size_t numSamples,
float & fAvg,
float & fStd)
752 fAvg = getAvg(fSum, numSamples);
756 float fDelta{fSumSquared - (fSum * fSum)};
760 fStd = sqrt(fDelta / numSamples) / (numSamples - 1.0f);
765 inline void updateMinMax(std::uint64_t & min, std::uint64_t & max, std::uint64_t value)
767 if(min > value || min == 0)
772 if(max < value || max == 0)
779 inline void updateRunningAverage(std::uint64_t &avg, std::uint64_t count, std::uint64_t val)
781 avg = (count == 1) ? val : ((avg * count) + val) / (count + 1);
788 pImpl_{
new Implementation{nemId}}
799 pImpl_->setNeighborDeleteTimeMicroseconds(ageMicroseconds);
807 pImpl_->handleTxActivity(dst, u64DataRatebps, txTime);
813 std::uint64_t u64SeqNum,
818 pImpl_->handleRxActivity(src, u64SeqNum, uuid, rxTime);
823 std::uint64_t u64SeqNum,
829 std::uint64_t u64DataRatebps)
832 pImpl_->handleRxActivity(src,
838 durationMicroseconds,
847 pImpl_->handleNeighborStatusUpdate();
849 return pImpl_->getNeighborMetrics();
855 pImpl_->handleNeighborStatusUpdate();
861 pImpl_->registerStatistics(statisticRegistrar);
void updateNeighborStatus()
void updateNeighborRxMetric(NEMId src, std::uint64_t u64SeqNum, const uuid_t &uuid, const TimePoint &rxTime)
Controls::R2RINeighborMetrics getNeighborMetrics()
constexpr NEMId NEM_BROADCAST_MAC_ADDRESS
std::list< R2RINeighborMetric > R2RINeighborMetrics
void updateNeighborTxMetric(NEMId dst, std::uint64_t u64DataRatebps, const TimePoint &txTime)
The StatisticRegistrar allows NEM layers to register statistics and statistic tables. Statistics and Statistic tables are owned by the emulator framework and a borrowed reference is returned to the registering NEM layer.
std::vector< std::string > StatisticTableLabels
NeighborMetricManager(NEMId nemId)
std::chrono::microseconds Microseconds
std::chrono::duration< double > DoubleSeconds
R2RI neighbor metrics are used in conjunction with the R2RINeighborMetricControlMessage to inform an ...
void registerStatistics(StatisticRegistrar &statisticRegistrar)
void setNeighborDeleteTimeMicroseconds(const Microseconds &ageMicroseconds)
Clock::time_point TimePoint