wifi: (merges !2009) Add WifiTxStatsHelper

This commit is contained in:
Muyuan Shen
2024-09-13 17:13:44 -07:00
committed by Gabriel Ferreira
parent 4b393292eb
commit a704cc950c
4 changed files with 2065 additions and 0 deletions

View File

@@ -13,6 +13,7 @@ set(source_files
helper/wifi-radio-energy-model-helper.cc
helper/yans-wifi-helper.cc
helper/wifi-phy-rx-trace-helper.cc
helper/wifi-tx-stats-helper.cc
model/addba-extension.cc
model/adhoc-wifi-mac.cc
model/ampdu-subframe-header.cc
@@ -182,6 +183,7 @@ set(header_files
helper/wifi-radio-energy-model-helper.h
helper/yans-wifi-helper.h
helper/wifi-phy-rx-trace-helper.h
helper/wifi-tx-stats-helper.h
model/addba-extension.h
model/adhoc-wifi-mac.h
model/ampdu-subframe-header.h
@@ -395,4 +397,5 @@ build_lib(
test/wifi-non-ht-dup-test.cc
test/wifi-phy-mu-mimo-test.cc
test/wifi-operating-channel-test.cc
test/wifi-tx-stats-helper-test.cc
)

View File

@@ -0,0 +1,488 @@
/*
* Copyright (c) 2024 Huazhong University of Science and Technology
* Copyright (c) 2024 University of Washington
*
* SPDX-License-Identifier: GPL-2.0-only
*
* Authors: Muyuan Shen <muyuan@uw.edu>
* Hao Yin <haoyin@uw.edu>
*/
#include "wifi-tx-stats-helper.h"
#include "ns3/frame-exchange-manager.h"
#include "ns3/log.h"
#include "ns3/net-device-container.h"
#include "ns3/node-container.h"
#include "ns3/wifi-mac-queue.h"
#include "ns3/wifi-net-device.h"
#include <functional>
namespace ns3
{
NS_LOG_COMPONENT_DEFINE("WifiTxStatsHelper");
WifiTxStatsHelper::WifiTxStatsHelper()
{
NS_LOG_FUNCTION(this);
}
WifiTxStatsHelper::WifiTxStatsHelper(Time startTime, Time stopTime)
: m_startTime(startTime),
m_stopTime(stopTime)
{
NS_LOG_FUNCTION(this << startTime.As(Time::S) << stopTime.As(Time::S));
NS_ASSERT_MSG(startTime <= stopTime,
"Invalid Start: " << startTime << " and Stop: " << stopTime << " Time");
}
void
WifiTxStatsHelper::Enable(const NodeContainer& nodes)
{
NS_LOG_FUNCTION(this << nodes.GetN());
NetDeviceContainer netDevices;
for (uint32_t i = 0; i < nodes.GetN(); ++i)
{
for (uint32_t j = 0; j < nodes.Get(i)->GetNDevices(); ++j)
{
netDevices.Add(nodes.Get(i)->GetDevice(j));
}
}
Enable(netDevices);
}
void
WifiTxStatsHelper::Enable(const NetDeviceContainer& devices)
{
NS_LOG_FUNCTION(this << devices.GetN());
for (uint32_t j = 0; j < devices.GetN(); ++j)
{
auto device = DynamicCast<WifiNetDevice>(devices.Get(j));
if (!device)
{
NS_LOG_DEBUG("Ignoring deviceId: " << devices.Get(j)->GetIfIndex() << " on nodeId: "
<< devices.Get(j)->GetNode()->GetId()
<< " because it is not of type WifiNetDevice");
continue;
}
NS_LOG_INFO("Adding deviceId: " << devices.Get(j)->GetIfIndex()
<< " on nodeId: " << devices.Get(j)->GetNode()->GetId());
const auto nodeId = devices.Get(j)->GetNode()->GetId();
const auto deviceId = devices.Get(j)->GetIfIndex();
if (device->GetMac()->GetQosSupported())
{
for (const auto& [ac, wifiAc] : wifiAcList)
{
// Trace enqueue for available ACs
device->GetMac()->GetTxopQueue(ac)->TraceConnectWithoutContext(
"Enqueue",
MakeCallback(&WifiTxStatsHelper::NotifyMacEnqueue, this)
.Bind(nodeId, deviceId));
}
}
else
{
// Trace enqueue for Non-QoS AC
device->GetMac()
->GetTxopQueue(AC_BE_NQOS)
->TraceConnectWithoutContext(
"Enqueue",
MakeCallback(&WifiTxStatsHelper::NotifyMacEnqueue, this)
.Bind(nodeId, deviceId));
}
device->GetMac()->TraceConnectWithoutContext(
"AckedMpdu",
MakeCallback(&WifiTxStatsHelper::NotifyAcked, this).Bind(nodeId, deviceId));
device->GetMac()->TraceConnectWithoutContext(
"DroppedMpdu",
MakeCallback(&WifiTxStatsHelper::NotifyMacDropped, this).Bind(nodeId, deviceId));
for (int i = 0; i < device->GetNPhys(); ++i)
{
device->GetPhy(i)->TraceConnectWithoutContext(
"PhyTxBegin",
MakeCallback(&WifiTxStatsHelper::NotifyTxStart, this).Bind(nodeId, deviceId));
}
}
}
void
WifiTxStatsHelper::Start(Time startTime)
{
NS_LOG_FUNCTION(this << startTime.As(Time::S));
NS_ASSERT_MSG(startTime >= Now(), "Invalid Start: " << startTime << " less than current time");
NS_ASSERT_MSG(startTime <= m_stopTime,
"Invalid Start: " << startTime << " and Stop: " << m_stopTime << " Time");
m_startTime = startTime;
}
void
WifiTxStatsHelper::Stop(Time stopTime)
{
NS_LOG_FUNCTION(this << stopTime.As(Time::S));
NS_ASSERT_MSG(stopTime >= Now(), "Invalid Stop: " << stopTime << " less than current time");
NS_ASSERT_MSG(m_startTime <= stopTime,
"Invalid Start: " << m_startTime << " and Stop: " << stopTime << " Time");
m_stopTime = stopTime;
}
void
WifiTxStatsHelper::Reset()
{
NS_LOG_FUNCTION(this);
m_successMap.clear();
m_failureMap.clear();
m_startTime = Now();
}
WifiTxStatsHelper::CountPerNodeDevice_t
WifiTxStatsHelper::GetSuccessesByNodeDevice() const
{
WifiTxStatsHelper::CountPerNodeDevice_t results;
for (const auto& [nodeDevTuple, records] : m_successMap)
{
results[{std::get<0>(nodeDevTuple), std::get<1>(nodeDevTuple)}] = records.size();
}
return results;
}
WifiTxStatsHelper::CountPerNodeDeviceLink_t
WifiTxStatsHelper::GetSuccessesByNodeDeviceLink(WifiTxStatsHelper::MultiLinkSuccessType type) const
{
WifiTxStatsHelper::CountPerNodeDeviceLink_t results;
for (const auto& [nodeDevTuple, records] : m_successMap)
{
for (const auto& record : records)
{
NS_ASSERT_MSG(!record.m_successLinkIdSet.empty(), "No LinkId set on MPDU");
if (type == FIRST_LINK_IN_SET)
{
const auto firstLinkId = *record.m_successLinkIdSet.begin();
const auto nodeDevLinkTuple =
std::tuple<uint32_t, uint32_t, uint8_t>(std::get<0>(nodeDevTuple),
std::get<1>(nodeDevTuple),
firstLinkId);
if (auto countIt = results.find(nodeDevLinkTuple); countIt != results.end())
{
countIt->second++;
}
else
{
results[nodeDevLinkTuple] = 1;
}
}
else
{
for (const auto linkId : record.m_successLinkIdSet)
{
const auto nodeDevLinkTuple =
std::tuple<uint32_t, uint32_t, uint8_t>(std::get<0>(nodeDevTuple),
std::get<1>(nodeDevTuple),
linkId);
if (auto countIt = results.find(nodeDevLinkTuple); countIt != results.end())
{
countIt->second++;
}
else
{
results[nodeDevLinkTuple] = 1;
}
}
}
}
}
return results;
}
WifiTxStatsHelper::CountPerNodeDevice_t
WifiTxStatsHelper::GetFailuresByNodeDevice() const
{
WifiTxStatsHelper::CountPerNodeDevice_t results;
for (const auto& [nodeDevTuple, records] : m_failureMap)
{
results[{std::get<0>(nodeDevTuple), std::get<1>(nodeDevTuple)}] = records.size();
}
return results;
}
WifiTxStatsHelper::CountPerNodeDevice_t
WifiTxStatsHelper::GetFailuresByNodeDevice(WifiMacDropReason reason) const
{
WifiTxStatsHelper::CountPerNodeDevice_t results;
for (const auto& [nodeDevTuple, records] : m_failureMap)
{
for (const auto& record : records)
{
NS_ASSERT_MSG(record.m_dropTime.has_value() && record.m_dropReason.has_value(),
"Incomplete dropped MPDU record");
if (record.m_dropReason == reason)
{
if (auto countIt =
results.find({std::get<0>(nodeDevTuple), std::get<1>(nodeDevTuple)});
countIt != results.end())
{
countIt->second++;
}
else
{
results[{std::get<0>(nodeDevTuple), std::get<1>(nodeDevTuple)}] = 1;
}
}
}
}
return results;
}
WifiTxStatsHelper::CountPerNodeDevice_t
WifiTxStatsHelper::GetRetransmissionsByNodeDevice() const
{
WifiTxStatsHelper::CountPerNodeDevice_t results;
for (const auto& [nodeDevLinkTuple, records] : m_successMap)
{
for (const auto& record : records)
{
if (auto countIt =
results.find({std::get<0>(nodeDevLinkTuple), std::get<1>(nodeDevLinkTuple)});
countIt != results.end())
{
countIt->second += record.m_retransmissions;
}
else
{
results[{std::get<0>(nodeDevLinkTuple), std::get<1>(nodeDevLinkTuple)}] =
record.m_retransmissions;
}
}
}
return results;
}
uint64_t
WifiTxStatsHelper::GetSuccesses() const
{
uint64_t count{0};
for (const auto& [nodeDevLinkTuple, records] : m_successMap)
{
count += records.size();
}
return count;
}
uint64_t
WifiTxStatsHelper::GetFailures() const
{
uint64_t count{0};
for (const auto& [nodeDevTuple, records] : m_failureMap)
{
count += records.size();
}
return count;
}
uint64_t
WifiTxStatsHelper::GetFailures(WifiMacDropReason reason) const
{
uint64_t count{0};
for (const auto& [nodeDevTuple, records] : m_failureMap)
{
for (const auto& record : records)
{
NS_ASSERT_MSG(record.m_dropTime.has_value() && record.m_dropReason.has_value(),
"Incomplete dropped MPDU record");
if (record.m_dropReason == reason)
{
count++;
}
}
}
return count;
}
uint64_t
WifiTxStatsHelper::GetRetransmissions() const
{
uint64_t count{0};
for (const auto& [nodeDevTuple, records] : m_successMap)
{
for (const auto& record : records)
{
count += record.m_retransmissions;
}
}
return count;
}
Time
WifiTxStatsHelper::GetDuration() const
{
return Now() - m_startTime;
}
const WifiTxStatsHelper::MpduRecordsPerNodeDeviceLink_t
WifiTxStatsHelper::GetSuccessRecords(WifiTxStatsHelper::MultiLinkSuccessType type) const
{
WifiTxStatsHelper::MpduRecordsPerNodeDeviceLink_t results;
for (const auto& [nodeDevTuple, records] : m_successMap)
{
for (const auto& record : records)
{
NS_ASSERT_MSG(!record.m_successLinkIdSet.empty(), "No LinkId set on MPDU");
if (type == FIRST_LINK_IN_SET)
{
const auto firstLinkId = *record.m_successLinkIdSet.begin();
const auto nodeDevLinkTuple =
std::tuple<uint32_t, uint32_t, uint8_t>(std::get<0>(nodeDevTuple),
std::get<1>(nodeDevTuple),
firstLinkId);
results[nodeDevLinkTuple].emplace_back(record);
}
else
{
for (const auto linkId : record.m_successLinkIdSet)
{
const auto nodeDevLinkTuple =
std::tuple<uint32_t, uint32_t, uint8_t>(std::get<0>(nodeDevTuple),
std::get<1>(nodeDevTuple),
linkId);
results[nodeDevLinkTuple].emplace_back(record);
}
}
}
}
return results;
}
const WifiTxStatsHelper::MpduRecordsPerNodeDevice_t&
WifiTxStatsHelper::GetFailureRecords() const
{
return m_failureMap;
}
void
WifiTxStatsHelper::NotifyMacEnqueue(uint32_t nodeId, uint32_t deviceId, Ptr<const WifiMpdu> mpdu)
{
NS_LOG_FUNCTION(this << nodeId << deviceId << mpdu);
const auto now = Simulator::Now();
if (now > m_stopTime)
{
NS_LOG_DEBUG("Ignoring enqueue because helper is stopped");
return;
}
if (mpdu->GetHeader().IsData())
{
if (!mpdu->GetHeader().HasData())
{
// exclude Null frames
return;
}
MpduRecord record{.m_nodeId = nodeId, .m_deviceId = deviceId, .m_enqueueTime = now};
if (mpdu->GetHeader().IsQosData())
{
record.m_tid = mpdu->GetHeader().GetQosTid();
}
NS_LOG_INFO("Creating inflight record for packet UID "
<< mpdu->GetPacket()->GetUid() << " node " << nodeId << " device " << deviceId
<< " tid " << +record.m_tid);
m_inflightMap[mpdu->GetPacket()->GetUid()] = record;
}
}
void
WifiTxStatsHelper::NotifyTxStart(uint32_t nodeId, uint32_t deviceId, Ptr<const Packet> pkt, Watt_u)
{
NS_LOG_FUNCTION(this << nodeId << deviceId << pkt);
const auto now = Now();
if (now > m_stopTime)
{
NS_LOG_DEBUG("Ignoring TxStart because helper is stopped");
return;
}
if (auto pktRecord = m_inflightMap.find(pkt->GetUid()); pktRecord != m_inflightMap.end())
{
auto& [uid, record] = *pktRecord;
NS_LOG_INFO("Packet UID " << uid << " started");
if (record.m_txStartTime.IsZero())
{
NS_LOG_INFO("TxStart called (first transmission) for inflight packet UID "
<< pkt->GetUid());
record.m_txStartTime = now;
}
else
{
NS_LOG_INFO("TxStart called (retransmission) for inflight packet UID "
<< pkt->GetUid());
record.m_retransmissions++;
}
}
}
void
WifiTxStatsHelper::NotifyAcked(uint32_t nodeId, uint32_t deviceId, Ptr<const WifiMpdu> mpdu)
{
NS_LOG_FUNCTION(this << nodeId << deviceId << mpdu);
NS_ASSERT_MSG(!mpdu->GetInFlightLinkIds().empty(), "No LinkId set on MPDU");
const auto now = Now();
if (now <= m_startTime || now > m_stopTime)
{
if (auto pktRecord = m_inflightMap.find(mpdu->GetPacket()->GetUid());
pktRecord != m_inflightMap.end())
{
m_inflightMap.erase(pktRecord);
}
NS_LOG_DEBUG("Ignoring acknowledgement because the time is out of range");
return;
}
// Get the set of in-flight link IDs
const auto linkIds = mpdu->GetInFlightLinkIds();
if (auto pktRecord = m_inflightMap.find(mpdu->GetPacket()->GetUid());
pktRecord != m_inflightMap.end())
{
auto& [uid, record] = *pktRecord;
record.m_ackTime = now;
record.m_successLinkIdSet = linkIds;
record.m_mpduSeqNum = mpdu->GetHeader().GetSequenceNumber();
// Store record in success map and remove it below from inflight map
NS_LOG_INFO("Successful transmission logged of packet UID " << uid);
m_successMap[{nodeId, deviceId}].push_back(std::move(record));
NS_LOG_INFO("Erasing packet UID " << uid << " from inflight map due to success");
m_inflightMap.erase(pktRecord);
}
}
void
WifiTxStatsHelper::NotifyMacDropped(uint32_t nodeId,
uint32_t deviceId,
WifiMacDropReason reason,
Ptr<const WifiMpdu> mpdu)
{
NS_LOG_FUNCTION(this << nodeId << deviceId << +reason << mpdu);
const auto now = Now();
if (now <= m_startTime || now > m_stopTime)
{
if (auto pktRecord = m_inflightMap.find(mpdu->GetPacket()->GetUid());
pktRecord != m_inflightMap.end())
{
m_inflightMap.erase(pktRecord);
}
NS_LOG_DEBUG("Ignoring drop because the time is out of range");
return;
}
if (auto pktRecord = m_inflightMap.find(mpdu->GetPacket()->GetUid());
pktRecord != m_inflightMap.end())
{
auto& [uid, record] = *pktRecord;
NS_LOG_INFO("Packet UID " << uid << " dropped");
record.m_dropTime = now;
record.m_dropReason = reason;
record.m_mpduSeqNum = mpdu->GetHeader().GetSequenceNumber();
NS_LOG_INFO("Failed transmission logged of packet UID " << uid);
// Store record in failure map and remove it below from inflight map
m_failureMap[{nodeId, deviceId}].push_back(std::move(record));
NS_LOG_INFO("Erasing packet UID " << uid << " from inflight map due to failure");
m_inflightMap.erase(pktRecord);
}
}
} // namespace ns3

View File

@@ -0,0 +1,389 @@
/*
* Copyright (c) 2024 Huazhong University of Science and Technology
* Copyright (c) 2024 University of Washington
*
* SPDX-License-Identifier: GPL-2.0-only
*
* Authors: Muyuan Shen <muyuan@uw.edu>
* Hao Yin <haoyin@uw.edu>
*/
#ifndef WIFI_TX_STATS_HELPER_H
#define WIFI_TX_STATS_HELPER_H
#include "ns3/nstime.h"
#include "ns3/object.h"
#include "ns3/qos-utils.h"
#include "ns3/type-id.h"
#include "ns3/wifi-mac.h"
#include "ns3/wifi-types.h"
#include "ns3/wifi-utils.h"
#include <cstdint>
#include <functional>
#include <map>
#include <string>
#include <tuple>
#include <vector>
namespace ns3
{
class NetDeviceContainer;
class NodeContainer;
class Time;
class Packet;
class WifiMpdu;
/**
* @brief Statistics helper for tracking outcomes of data MPDU transmissions.
*
* This helper may be used to track statistics of all data MPDU transmissions on a given Node,
* WifiNetDevice, or even link granularity (for MLO operation), including counts of the
* number of successfully acknowledged MPDUs, the number of retransmissions (if any) of
* those successfully acknowledged MPDUs, and the number of failed MPDUs (by drop reason).
* The helper can also be used to access timestamped data about how long the MPDU was enqueued
* and how long it took to complete acknowledgement (or failure) once the first transmission
* of it was attempted. For failed MPDUs, their WifiMacDropReason can be accessed.
*/
class WifiTxStatsHelper
{
public:
/**
* When Multi-Link Operation (MLO) is used, it is possible for MPDUs to be sent on
* multiple links concurrently. When such an MPDU is acked, a question arises as to how
* to count the number of successes (only one success, or success on each link). The
* ACK does not convey which transmission on which link triggered the event. Therefore,
* two policies are defined for how to count a successful event.
* 1) FIRST_LINK_IN_SET: Count the success on the first link of the set of in-flight links
* 2) ALL_LINKS: Count the success on all links in the in-flight link set
*/
enum MultiLinkSuccessType : uint8_t
{
FIRST_LINK_IN_SET,
ALL_LINKS
};
/**
* Structure for recording information about an individual data MPDU transmission.
*/
struct MpduRecord
{
uint32_t m_nodeId{std::numeric_limits<uint32_t>::max()}; //!< The sending node ID
uint32_t m_deviceId{std::numeric_limits<uint32_t>::max()}; //!< The sending device ID
Time m_enqueueTime; //!< The enqueue time (time that the packet was added to a WifiMacQueue)
Time m_txStartTime; //!< The time at which the MPDU was first transmitted
std::optional<Time> m_dropTime; //!< The time of removal from the WifiMacQueue, if failed
Time m_ackTime; //!< The time at which the MPDU was acknowledged
uint64_t m_mpduSeqNum{0}; //!< The MPDU sequence number
uint32_t m_retransmissions{0}; //!< A count of the number of retransmissions of the MPDU
uint8_t m_tid{WIFI_TID_UNDEFINED}; //!< The TID for the MPDU
std::set<uint8_t> m_successLinkIdSet; //!< If acked, the set of in-flight link ID(s)
std::optional<WifiMacDropReason> m_dropReason; //!< If failed, the drop reason
};
/**
* Default constructor; start time initialized to zero and stop time to Time::Max()
*/
WifiTxStatsHelper();
/**
* Construct a helper with start and stop times. Only those MPDUs
* enqueued between the start and stop times will be counted.
*
* @param startTime The measurement start time
* @param stopTime The measurement stop time
*/
WifiTxStatsHelper(Time startTime, Time stopTime);
/**
* Enables trace collection for all nodes and WifiNetDevices in the specified NodeContainer.
* @param nodes The NodeContainer to which traces are to be connected.
*/
void Enable(const NodeContainer& nodes);
/**
* Enables trace collection for all WifiNetDevices in the specified NetDeviceContainer.
* @param devices The NetDeviceContainer to which traces are to be connected.
*/
void Enable(const NetDeviceContainer& devices);
/**
* @brief Set the start time for statistics collection
*
* No MPDUs enqueued before startTime will be included
* in the statistics.
*
* @param startTime The measurement start time
*/
void Start(Time startTime);
/**
* @brief Set the stop time for statistics collection
*
* No MPDUs enqueued after stopTime elapses will be included in
* statistics. However, MPDUs that were enqueued before stopTime
* (and after startTime) will be included in the statistics if
* they are dequeued before the statistics are queried by the user.
*
* @param stopTime The measurement stop time
*/
void Stop(Time stopTime);
/**
* @brief Clear the data structures for all completed successful and failed MpduRecords
*/
void Reset();
//
// Hash function and unordered maps
//
/**
* Hash function for the tuples used in unordered_maps
*/
struct TupleHash
{
/**
* Combines two hash values into one (similar to boost::hash_combine)
* @param seed the starting hash point
* @param value the value to combine to the hash
*/
template <typename T>
void hash_combine(size_t& seed, const T& value) const
{
seed ^= std::hash<T>{}(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
/**
* Hash function for tuples of arbitrary size
* @param tuple the input Tuple
* @return the hash of the tuple elements
*/
template <typename Tuple, size_t Index = 0>
size_t tuple_hash(const Tuple& tuple) const
{
if constexpr (Index < std::tuple_size<Tuple>::value)
{
size_t seed = tuple_hash<Tuple, Index + 1>(tuple);
hash_combine(seed, std::get<Index>(tuple));
return seed;
}
else
{
return 0; // Base case
}
}
/**
* Operator overload for calculating hash of tuple
* @param tuple the input Tuple
* @return the hash of the tuple elements
*/
template <typename... Args>
size_t operator()(const std::tuple<Args...>& tuple) const
{
return tuple_hash(tuple);
}
};
using CountPerNodeDeviceLink_t =
std::unordered_map<std::tuple<uint32_t, uint32_t, uint8_t>,
uint64_t,
TupleHash>; //!< std::unordered_map of {nodeId, deviceId, linkId} tuple
//!< to counter value
using CountPerNodeDevice_t =
std::unordered_map<std::tuple<uint32_t, uint32_t>,
uint64_t,
TupleHash>; //!< std::unordered_map of {nodeId, deviceId} tuple to
//!< counter value
using MpduRecordsPerNodeDeviceLink_t =
std::unordered_map<std::tuple<uint32_t, uint32_t, uint8_t>,
std::list<MpduRecord>,
TupleHash>; //!< std::unordered_map of {nodeId, deviceId, linkId} tuple
//!< to a list of MPDU records
using MpduRecordsPerNodeDevice_t =
std::unordered_map<std::tuple<uint32_t, uint32_t>,
std::list<MpduRecord>,
TupleHash>; //!< std::unordered_map of {nodeId, deviceId} tuple to a list
//!< of MPDU records
//
// Methods to retrieve the counts tabulated by this helper
//
/**
* @brief Return the counts of successful MPDU transmissions in a hash map
*
* The return type is an std::unordered_map, and the keys are tuples of {node ID,
* device ID}. The value returned by indexing the tuple is the count of
* successful MPDU transmissions.
*
* If Multi-Link Operation (MLO) is configured, a variant of this method is available
* that returns an unordered_map indexed by a tuple that includes the Link ID, for
* more granular statistics reporting.
* @see GetSuccessesByNodeDeviceLink
*
* If Multi-Link Operation (MLO) is configured, and MPDUs are sent on multiple
* links in parallel, this method will count one success for any such acknowledged
* MPDU (even if it was successfully received on more than one link).
*
* @return A hash map with counts of MPDU successful transmissions
*/
CountPerNodeDevice_t GetSuccessesByNodeDevice() const;
/**
* @brief Return the counts of successful MPDU transmissions in a hash map
* @param type how the statistics handle a successful MPDU transmitted on multiple links
*
* The return type is an std::unordered_map, and the keys are tuples of {node ID,
* device ID, link ID}. The value returned by indexing the tuple is the count of
* successful MPDU transmissions.
*
* The link ID corresponds to the link for which the MPDU was successfully
* acknowledged, regardless of the links that may have been used in
* earlier transmissions of the MPDU.
*
* For a similar method that does not use Link IDs in the key,
* @see GetSuccessesByNodeDevice
*
* @return A hash map with counts of MPDU successful transmissions
*/
CountPerNodeDeviceLink_t GetSuccessesByNodeDeviceLink(
WifiTxStatsHelper::MultiLinkSuccessType type = FIRST_LINK_IN_SET) const;
/**
* @brief Return the counts of failed MPDU transmissions in a hash map
*
* The return type is an std::unordered_map, and the keys are tuples of {node ID,
* device ID}. The value returned by indexing the tuple is the count of
* failed MPDU transmissions.
*
* The number of failures are only stored with the granularity
* of per-device, since in a multi-link scenario, some retransmissions
* (that lead to eventual failure) may occur on different links.
*
* @return A hash map with counts of MPDU failed transmissions
*/
CountPerNodeDevice_t GetFailuresByNodeDevice() const;
/**
* @brief Return the counts of failed MPDU transmissions due to given drop reason in a hash map
* @param reason Reason for dropping the MPDU
*
* The return type is an std::unordered_map, and the keys are tuples of {node ID,
* device ID}. The value returned by indexing the tuple is the count of
* failed MPDU transmissions due to the reason given.
*
* The number of failures are only stored with the granularity
* of per-device, since in a multi-link scenario, some retransmissions
* (that lead to eventual failure) may occur on different links.
*
* @return A hash map with counts of MPDU failed transmissions
*/
CountPerNodeDevice_t GetFailuresByNodeDevice(WifiMacDropReason reason) const;
/**
* @brief Return the counts of MPDU retransmissions in a hash map
*
* The return type is an std::unordered_map, and the keys are tuples of {node ID,
* device ID}. The value returned by indexing the tuple is the count of
* MPDU retransmissions of those MPDUs that were successfully acked.
*
* The number of retransmissions are only stored with the granularity
* of per-device, since in a multi-link scenario, some retransmissions
* may be sent on different links.
*
* @return A hash map with counts of MPDU retransmissions for successful MPDUs
*/
CountPerNodeDevice_t GetRetransmissionsByNodeDevice() const;
/**
* @brief Return the count of successful MPDU transmissions across all enabled devices
*
* @return The count of all successful MPDU transmissions
*/
uint64_t GetSuccesses() const;
/**
* @brief Return the count of failed MPDU transmissions across all enabled devices
*
* @return The count of all failed MPDU transmissions
*/
uint64_t GetFailures() const;
/**
* @brief Return the count of failed MPDU transmissions due to given drop reason across
* all enabled devices
* @param reason Reason for dropping the MPDU
*
* @return The count of all failed MPDU transmissions for the specified reason
*/
uint64_t GetFailures(WifiMacDropReason reason) const;
/**
* @brief Return the count of MPDU retransmissions across all enabled devices
*
* @return The count of all MPDU retransmissions for acked MPDUs
*/
uint64_t GetRetransmissions() const;
/**
* @brief Return the duration since the helper was started or reset
*
* @return The duration since the helper was started or reset
*/
Time GetDuration() const;
/**
* @brief Return a hash map of successful MPDU records
* @param type how the statistics handle a successful MPDU transmitted on multiple links
*
* The return type is an std::unordered_map, and the keys are tuples of {node ID,
* device ID, link ID}. The value returned by indexing the tuple is the list of MPDU
* records of the MPDUs that were successfully acked.
*
* @return A const reference to the hash map of successful MPDU records
*/
const MpduRecordsPerNodeDeviceLink_t GetSuccessRecords(
WifiTxStatsHelper::MultiLinkSuccessType type = FIRST_LINK_IN_SET) const;
/**
* @brief Return a hash map of MPDU records for failed transmissions
*
* The return type is an std::unordered_map, and the keys are tuples of {node ID,
* device ID}. The value returned by indexing the tuple is the list of MPDU
* records of the MPDUs that failed.
*
* @return A const reference to the hash map of MPDU records corresponding to failure
*/
const MpduRecordsPerNodeDevice_t& GetFailureRecords() const;
private:
/**
* @brief Callback for the WifiMacQueue::Enqueue trace
* @param nodeId the Node ID triggering the trace
* @param deviceId the Node ID triggering the trace
* @param mpdu The MPDU enqueued
*/
void NotifyMacEnqueue(uint32_t nodeId, uint32_t deviceId, Ptr<const WifiMpdu> mpdu);
/**
* @brief Callback for the WifiPhy::PhyTxBegin trace
* @param nodeId the Node ID triggering the trace
* @param deviceId the Node ID triggering the trace
* @param pkt The frame being sent
*/
void NotifyTxStart(uint32_t nodeId, uint32_t deviceId, Ptr<const Packet> pkt, Watt_u);
/**
* @brief Callback for the WifiMac::AckedMpdu trace
* @param nodeId the Node ID triggering the trace
* @param deviceId the Node ID triggering the trace
* @param mpdu The MPDU acked
*/
void NotifyAcked(uint32_t nodeId, uint32_t deviceId, Ptr<const WifiMpdu> mpdu);
/**
* @brief Callback for the WifiMac::DroppedMpdu trace
* @param nodeId the Node ID triggering the trace
* @param deviceId the Node ID triggering the trace
* @param reason Reason for dropping the MPDU
* @param mpdu The MPDU dropped
*/
void NotifyMacDropped(uint32_t nodeId,
uint32_t deviceId,
WifiMacDropReason reason,
Ptr<const WifiMpdu> mpdu);
MpduRecordsPerNodeDevice_t m_successMap; //!< The nested map of successful MPDUs
MpduRecordsPerNodeDevice_t m_failureMap; //!< The nested map of failed MPDUs
std::map<uint64_t, MpduRecord> m_inflightMap; //!< In-flight MPDUs; key is a Packet UID
Time m_startTime; //!< The start time for recording statistics
Time m_stopTime{Time::Max()}; //!< The stop time for recording statistics
};
} // namespace ns3
#endif // WIFI_TX_STATS_HELPER_H

File diff suppressed because it is too large Load Diff