Files
unison/src/visualizer/model/pyviz.cc
2023-09-17 22:10:09 +00:00

1644 lines
44 KiB
C++

/*
* Copyright (c) 2008 INESC Porto
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Gustavo Carneiro <gjc@inescporto.pt>
*/
#include "pyviz.h"
#include "visual-simulator-impl.h"
#include "ns3/abort.h"
#include "ns3/config.h"
#include "ns3/ethernet-header.h"
#include "ns3/log.h"
#include "ns3/node-list.h"
#include "ns3/ppp-header.h"
#include "ns3/simulator.h"
#include "ns3/wifi-mac-header.h"
#include "ns3/wifi-net-device.h"
#include <cstdlib>
#include <sstream>
NS_LOG_COMPONENT_DEFINE("PyViz");
#define NUM_LAST_PACKETS 10
static std::vector<std::string>
PathSplit(std::string str)
{
std::vector<std::string> results;
size_t cutAt;
while ((cutAt = str.find_first_of('/')) != std::string::npos)
{
if (cutAt > 0)
{
results.push_back(str.substr(0, cutAt));
}
str = str.substr(cutAt + 1);
}
if (!str.empty())
{
results.push_back(str);
}
return results;
}
namespace ns3
{
static PyViz* g_visualizer = nullptr; ///< the visualizer
/**
* PyVizPacketTag structure
*/
struct PyVizPacketTag : public Tag
{
static TypeId GetTypeId();
TypeId GetInstanceTypeId() const override;
uint32_t GetSerializedSize() const override;
void Serialize(TagBuffer buf) const override;
void Deserialize(TagBuffer buf) override;
void Print(std::ostream& os) const override;
PyVizPacketTag();
uint32_t m_packetId; ///< packet id
};
/**
* \brief Get the type ID.
* \return the object TypeId
*/
TypeId
PyVizPacketTag::GetTypeId()
{
static TypeId tid = TypeId("ns3::PyVizPacketTag")
.SetParent<Tag>()
.SetGroupName("Visualizer")
.AddConstructor<PyVizPacketTag>();
return tid;
}
TypeId
PyVizPacketTag::GetInstanceTypeId() const
{
return GetTypeId();
}
uint32_t
PyVizPacketTag::GetSerializedSize() const
{
return 4;
}
void
PyVizPacketTag::Serialize(TagBuffer buf) const
{
buf.WriteU32(m_packetId);
}
void
PyVizPacketTag::Deserialize(TagBuffer buf)
{
m_packetId = buf.ReadU32();
}
void
PyVizPacketTag::Print(std::ostream& os) const
{
os << "PacketId=" << m_packetId;
}
PyVizPacketTag::PyVizPacketTag()
: Tag()
{
}
PyViz::PyViz()
{
NS_LOG_FUNCTION_NOARGS();
NS_ASSERT(g_visualizer == nullptr);
g_visualizer = this;
// WiFi
Config::ConnectFailSafe("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Mac/MacTx",
MakeCallback(&PyViz::TraceNetDevTxWifi, this));
Config::ConnectFailSafe("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Mac/MacRx",
MakeCallback(&PyViz::TraceNetDevRxWifi, this));
// CSMA
Config::ConnectFailSafe("/NodeList/*/DeviceList/*/$ns3::CsmaNetDevice/MacTx",
MakeCallback(&PyViz::TraceNetDevTxCsma, this));
Config::ConnectFailSafe("/NodeList/*/DeviceList/*/$ns3::CsmaNetDevice/MacRx",
MakeCallback(&PyViz::TraceNetDevRxCsma, this));
Config::ConnectFailSafe("/NodeList/*/DeviceList/*/$ns3::CsmaNetDevice/MacPromiscRx",
MakeCallback(&PyViz::TraceNetDevPromiscRxCsma, this));
// Generic queue drop
Config::ConnectFailSafe("/NodeList/*/DeviceList/*/TxQueue/Drop",
MakeCallback(&PyViz::TraceDevQueueDrop, this));
// IPv4 drop
Config::ConnectFailSafe("/NodeList/*/$ns3::Ipv4L3Protocol/Drop",
MakeCallback(&PyViz::TraceIpv4Drop, this));
// Point-to-Point
Config::ConnectFailSafe("/NodeList/*/DeviceList/*/$ns3::PointToPointNetDevice/MacTx",
MakeCallback(&PyViz::TraceNetDevTxPointToPoint, this));
Config::ConnectFailSafe("/NodeList/*/DeviceList/*/$ns3::PointToPointNetDevice/MacRx",
MakeCallback(&PyViz::TraceNetDevRxPointToPoint, this));
// WiMax
Config::ConnectFailSafe("/NodeList/*/DeviceList/*/$ns3::WimaxNetDevice/Tx",
MakeCallback(&PyViz::TraceNetDevTxWimax, this));
Config::ConnectFailSafe("/NodeList/*/DeviceList/*/$ns3::WimaxNetDevice/Rx",
MakeCallback(&PyViz::TraceNetDevRxWimax, this));
// LTE
Config::ConnectFailSafe("/NodeList/*/DeviceList/*/$ns3::LteNetDevice/Tx",
MakeCallback(&PyViz::TraceNetDevTxLte, this));
Config::ConnectFailSafe("/NodeList/*/DeviceList/*/$ns3::LteNetDevice/Rx",
MakeCallback(&PyViz::TraceNetDevRxLte, this));
}
void
PyViz::RegisterCsmaLikeDevice(const std::string& deviceTypeName)
{
TypeId::LookupByName(deviceTypeName); // this will assert if the type name is invalid
std::ostringstream sstream;
sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/MacTx";
Config::Connect(sstream.str(), MakeCallback(&PyViz::TraceNetDevTxCsma, this));
sstream.str("");
sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/Rx";
Config::Connect(sstream.str(), MakeCallback(&PyViz::TraceNetDevRxCsma, this));
sstream.str("");
sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/PromiscRx";
Config::Connect(sstream.str(), MakeCallback(&PyViz::TraceNetDevPromiscRxCsma, this));
}
void
PyViz::RegisterWifiLikeDevice(const std::string& deviceTypeName)
{
TypeId::LookupByName(deviceTypeName); // this will assert if the type name is invalid
std::ostringstream sstream;
sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/Tx";
Config::Connect(sstream.str(), MakeCallback(&PyViz::TraceNetDevTxWifi, this));
sstream.str("");
sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/Rx";
Config::Connect(sstream.str(), MakeCallback(&PyViz::TraceNetDevRxWifi, this));
}
void
PyViz::RegisterPointToPointLikeDevice(const std::string& deviceTypeName)
{
TypeId::LookupByName(deviceTypeName); // this will assert if the type name is invalid
std::ostringstream sstream;
sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/TxQueue/Dequeue";
Config::Connect(sstream.str(), MakeCallback(&PyViz::TraceNetDevTxPointToPoint, this));
sstream.str("");
sstream << "/NodeList/*/DeviceList/*/$" << deviceTypeName << "/Rx";
Config::Connect(sstream.str(), MakeCallback(&PyViz::TraceNetDevRxPointToPoint, this));
}
void
PyViz::SetPacketCaptureOptions(uint32_t nodeId, PacketCaptureOptions options)
{
NS_LOG_DEBUG(" SetPacketCaptureOptions "
<< nodeId << " PacketCaptureOptions (headers size = " << options.headers.size()
<< " mode = " << options.mode << " numLastPackets = " << options.numLastPackets
<< ")");
m_packetCaptureOptions[nodeId] = options;
}
void
PyViz::RegisterDropTracePath(const std::string& tracePath)
{
Config::Connect(tracePath, MakeCallback(&PyViz::TraceDevQueueDrop, this));
}
PyViz::~PyViz()
{
NS_LOG_FUNCTION_NOARGS();
NS_ASSERT(g_visualizer == this);
g_visualizer = nullptr;
}
void
PyViz::DoPause(const std::string& message)
{
m_pauseMessages.push_back(message);
m_stop = true;
NS_LOG_LOGIC(Simulator::Now().As(Time::S)
<< ": Have " << g_visualizer->m_pauseMessages.size() << " pause messages");
}
void
PyViz::Pause(const std::string& message)
{
NS_ASSERT(g_visualizer);
g_visualizer->DoPause(message);
}
std::vector<std::string>
PyViz::GetPauseMessages() const
{
NS_LOG_LOGIC(Simulator::Now().As(Time::S)
<< ": GetPauseMessages: have " << g_visualizer->m_pauseMessages.size()
<< " pause messages");
return m_pauseMessages;
}
void
PyViz::CallbackStopSimulation()
{
NS_LOG_FUNCTION_NOARGS();
if (m_runUntil <= Simulator::Now())
{
Simulator::Stop(Seconds(0)); // Stop right now
m_stop = true;
}
}
void
PyViz::SimulatorRunUntil(Time time)
{
NS_LOG_LOGIC("SimulatorRunUntil " << time << " (now is " << Simulator::Now() << ")");
m_pauseMessages.clear();
m_transmissionSamples.clear();
m_packetDrops.clear();
Time expirationTime = Simulator::Now() - Seconds(10);
// Clear very old transmission records
for (std::map<TxRecordKey, TxRecordValue>::iterator iter = m_txRecords.begin();
iter != m_txRecords.end();)
{
if (iter->second.time < expirationTime)
{
m_txRecords.erase(iter++);
}
else
{
iter++;
}
}
// Clear very old packets of interest
for (std::map<uint32_t, Time>::iterator iter = m_packetsOfInterest.begin();
iter != m_packetsOfInterest.end();)
{
if (iter->second < expirationTime)
{
m_packetsOfInterest.erase(iter++);
}
else
{
iter++;
}
}
if (Simulator::Now() >= time)
{
return;
}
// Schedule a dummy callback function for the target time, to make
// sure we stop at the right time. Otherwise, simulations with few
// events just appear to "jump" big chunks of time.
NS_LOG_LOGIC("Schedule dummy callback to be called in " << (time - Simulator::Now()));
m_runUntil = time;
m_stop = false;
Simulator::ScheduleWithContext(Simulator::NO_CONTEXT,
time - Simulator::Now(),
&PyViz::CallbackStopSimulation,
this);
Ptr<SimulatorImpl> impl = Simulator::GetImplementation();
Ptr<VisualSimulatorImpl> visualImpl = DynamicCast<VisualSimulatorImpl>(impl);
if (visualImpl)
{
visualImpl->RunRealSimulator();
}
else
{
impl->Run();
}
}
bool
PyViz::TransmissionSampleKey::operator<(const PyViz::TransmissionSampleKey& other) const
{
if (this->transmitter < other.transmitter)
{
return true;
}
if (this->transmitter != other.transmitter)
{
return false;
}
if (this->receiver < other.receiver)
{
return true;
}
if (this->receiver != other.receiver)
{
return false;
}
return this->channel < other.channel;
}
bool
PyViz::TransmissionSampleKey::operator==(const PyViz::TransmissionSampleKey& other) const
{
bool retval = (transmitter == other.transmitter) && (receiver == other.receiver) &&
(channel == other.channel);
return retval;
}
PyViz::NetDeviceStatistics&
PyViz::FindNetDeviceStatistics(int node, int interface)
{
std::map<uint32_t, std::vector<NetDeviceStatistics>>::iterator nodeStatsIter =
m_nodesStatistics.find(node);
std::vector<NetDeviceStatistics>* stats;
if (nodeStatsIter == m_nodesStatistics.end())
{
stats = &m_nodesStatistics[node];
stats->resize(NodeList::GetNode(node)->GetNDevices());
}
else
{
stats = &(nodeStatsIter->second);
}
NetDeviceStatistics& devStats = (*stats)[interface];
return devStats;
}
bool
PyViz::GetPacketCaptureOptions(uint32_t nodeId, const PacketCaptureOptions** outOptions) const
{
std::map<uint32_t, PacketCaptureOptions>::const_iterator iter =
m_packetCaptureOptions.find(nodeId);
if (iter == m_packetCaptureOptions.end())
{
return false;
}
else
{
*outOptions = &iter->second;
return true;
}
}
bool
PyViz::FilterPacket(Ptr<const Packet> packet, const PacketCaptureOptions& options)
{
switch (options.mode)
{
case PACKET_CAPTURE_DISABLED:
return false;
case PACKET_CAPTURE_FILTER_HEADERS_OR: {
PacketMetadata::ItemIterator metadataIterator = packet->BeginItem();
while (metadataIterator.HasNext())
{
PacketMetadata::Item item = metadataIterator.Next();
if (options.headers.find(item.tid) != options.headers.end())
{
return true;
}
}
return false;
}
case PACKET_CAPTURE_FILTER_HEADERS_AND: {
std::set<TypeId> missingHeaders(options.headers);
PacketMetadata::ItemIterator metadataIterator = packet->BeginItem();
while (metadataIterator.HasNext())
{
PacketMetadata::Item item = metadataIterator.Next();
std::set<TypeId>::iterator missingIter = missingHeaders.find(item.tid);
if (missingIter != missingHeaders.end())
{
missingHeaders.erase(missingIter);
}
}
return missingHeaders.empty();
}
default:
NS_FATAL_ERROR("should not be reached");
return false;
}
}
void
PyViz::TraceDevQueueDrop(std::string context, Ptr<const Packet> packet)
{
NS_LOG_FUNCTION(context << packet->GetUid());
std::vector<std::string> splitPath = PathSplit(context);
int nodeIndex = std::stoi(splitPath[1]);
Ptr<Node> node = NodeList::GetNode(nodeIndex);
if (m_nodesOfInterest.find(nodeIndex) == m_nodesOfInterest.end())
{
// if the transmitting node is not "of interest", we still
// record the transmission if it is a packet of interest.
if (m_packetsOfInterest.find(packet->GetUid()) == m_packetsOfInterest.end())
{
NS_LOG_DEBUG("Packet " << packet->GetUid() << " is not of interest");
return;
}
}
// ---- "last packets"
const PacketCaptureOptions* captureOptions;
if (GetPacketCaptureOptions(nodeIndex, &captureOptions) &&
FilterPacket(packet, *captureOptions))
{
LastPacketsSample& last = m_lastPackets[nodeIndex];
PacketSample lastPacket;
lastPacket.time = Simulator::Now();
lastPacket.packet = packet->Copy();
lastPacket.device = nullptr;
last.lastDroppedPackets.push_back(lastPacket);
while (last.lastDroppedPackets.size() > captureOptions->numLastPackets)
{
last.lastDroppedPackets.erase(last.lastDroppedPackets.begin());
}
}
std::map<Ptr<Node>, uint32_t>::iterator iter = m_packetDrops.find(node);
if (iter == m_packetDrops.end())
{
m_packetDrops[node] = packet->GetSize();
}
else
{
iter->second += packet->GetSize();
}
}
void
PyViz::TraceIpv4Drop(std::string context,
const ns3::Ipv4Header& hdr,
Ptr<const Packet> packet,
ns3::Ipv4L3Protocol::DropReason reason,
Ptr<Ipv4> dummy_ipv4,
uint32_t interface)
{
Ptr<Packet> packetCopy = packet->Copy();
packetCopy->AddHeader(hdr);
TraceDevQueueDrop(context, packetCopy);
}
// --------- TX device tracing -------------------
void
PyViz::TraceNetDevTxCommon(const std::string& context,
Ptr<const Packet> packet,
const Mac48Address& destinationAddress)
{
NS_LOG_FUNCTION(context << packet->GetUid() << *packet);
std::vector<std::string> splitPath = PathSplit(context);
int nodeIndex = std::stoi(splitPath[1]);
int devIndex = std::stoi(splitPath[3]);
Ptr<Node> node = NodeList::GetNode(nodeIndex);
Ptr<NetDevice> device = node->GetDevice(devIndex);
// ---- statistics
NetDeviceStatistics& stats = FindNetDeviceStatistics(nodeIndex, devIndex);
++stats.transmittedPackets;
stats.transmittedBytes += packet->GetSize();
// ---- "last packets"
const PacketCaptureOptions* captureOptions;
if (GetPacketCaptureOptions(nodeIndex, &captureOptions) &&
FilterPacket(packet, *captureOptions))
{
LastPacketsSample& last = m_lastPackets[nodeIndex];
TxPacketSample lastPacket;
lastPacket.time = Simulator::Now();
lastPacket.packet = packet->Copy();
lastPacket.device = device;
lastPacket.to = destinationAddress;
last.lastTransmittedPackets.push_back(lastPacket);
while (last.lastTransmittedPackets.size() > captureOptions->numLastPackets)
{
last.lastTransmittedPackets.erase(last.lastTransmittedPackets.begin());
}
}
// ---- transmissions records
if (m_nodesOfInterest.find(nodeIndex) == m_nodesOfInterest.end())
{
// if the transmitting node is not "of interest", we still
// record the transmission if it is a packet of interest.
if (m_packetsOfInterest.find(packet->GetUid()) == m_packetsOfInterest.end())
{
NS_LOG_DEBUG("Packet " << packet->GetUid() << " is not of interest");
return;
}
}
else
{
// We will follow this packet throughout the network.
m_packetsOfInterest[packet->GetUid()] = Simulator::Now();
}
TxRecordValue record = {Simulator::Now(), node, false};
if (destinationAddress == device->GetBroadcast())
{
record.isBroadcast = true;
}
m_txRecords[TxRecordKey(device->GetChannel(), packet->GetUid())] = record;
PyVizPacketTag tag;
// packet->RemovePacketTag (tag);
tag.m_packetId = packet->GetUid();
packet->AddByteTag(tag);
}
void
PyViz::TraceNetDevTxWifi(std::string context, Ptr<const Packet> packet)
{
NS_LOG_FUNCTION(context << packet->GetUid() << *packet);
/*
* To DS From DS Address 1 Address 2 Address 3 Address 4
*----------------------------------------------------------------------
* 0 0 Destination Source BSSID N/A
* 0 1 Destination BSSID Source N/A
* 1 0 BSSID Source Destination N/A
* 1 1 Receiver Transmitter Destination Source
*/
WifiMacHeader hdr;
NS_ABORT_IF(packet->PeekHeader(hdr) == 0);
Mac48Address destinationAddress;
if (hdr.IsToDs() && !hdr.IsFromDs())
{
destinationAddress = hdr.GetAddr3();
}
else if (!hdr.IsToDs() && hdr.IsFromDs())
{
destinationAddress = hdr.GetAddr1();
}
else if (!hdr.IsToDs() && !hdr.IsFromDs())
{
destinationAddress = hdr.GetAddr1();
}
else
{
destinationAddress = hdr.GetAddr3();
}
TraceNetDevTxCommon(context, packet, destinationAddress);
}
void
PyViz::TraceNetDevTxCsma(std::string context, Ptr<const Packet> packet)
{
EthernetHeader ethernetHeader;
NS_ABORT_IF(packet->PeekHeader(ethernetHeader) == 0);
TraceNetDevTxCommon(context, packet, ethernetHeader.GetDestination());
}
void
PyViz::TraceNetDevTxPointToPoint(std::string context, Ptr<const Packet> packet)
{
TraceNetDevTxCommon(context, packet, Mac48Address());
}
// --------- RX device tracing -------------------
void
PyViz::TraceNetDevRxCommon(const std::string& context,
Ptr<const Packet> packet,
const Mac48Address& from)
{
uint32_t uid;
PyVizPacketTag tag;
if (packet->FindFirstMatchingByteTag(tag))
{
uid = tag.m_packetId;
}
else
{
// NS_ASSERT (0);
NS_LOG_WARN("Packet has no byte tag; wimax link?");
uid = packet->GetUid();
}
NS_LOG_FUNCTION(context << uid);
std::vector<std::string> splitPath = PathSplit(context);
int nodeIndex = std::stoi(splitPath[1]);
int devIndex = std::stoi(splitPath[3]);
// ---- statistics
NetDeviceStatistics& stats = FindNetDeviceStatistics(nodeIndex, devIndex);
++stats.receivedPackets;
stats.receivedBytes += packet->GetSize();
Ptr<Node> node = NodeList::GetNode(nodeIndex);
Ptr<NetDevice> device = node->GetDevice(devIndex);
// ---- "last packets"
const PacketCaptureOptions* captureOptions;
if (GetPacketCaptureOptions(nodeIndex, &captureOptions) &&
FilterPacket(packet, *captureOptions))
{
LastPacketsSample& last = m_lastPackets[nodeIndex];
RxPacketSample lastPacket;
lastPacket.time = Simulator::Now();
lastPacket.packet = packet->Copy();
lastPacket.device = device;
lastPacket.from = from;
last.lastReceivedPackets.push_back(lastPacket);
while (last.lastReceivedPackets.size() > captureOptions->numLastPackets)
{
last.lastReceivedPackets.erase(last.lastReceivedPackets.begin());
}
}
// ---- transmissions
if (m_packetsOfInterest.find(uid) == m_packetsOfInterest.end())
{
NS_LOG_DEBUG("RX Packet " << uid << " is not of interest");
return;
}
Ptr<Channel> channel = device->GetChannel();
std::map<TxRecordKey, TxRecordValue>::iterator recordIter =
m_txRecords.find(TxRecordKey(channel, uid));
if (recordIter == m_txRecords.end())
{
NS_LOG_DEBUG("RX Packet " << uid << " was not transmitted?!");
return;
}
TxRecordValue& record = recordIter->second;
if (record.srcNode == node)
{
NS_LOG_WARN("Node " << node->GetId() << " receiving back the same packet (UID=" << uid
<< ") it had previously transmitted, on the same channel!");
return;
}
TransmissionSampleKey key = {record.srcNode, node, channel};
#ifdef NS3_LOG_ENABLE
NS_LOG_DEBUG("m_transmissionSamples begin:");
if (g_log.IsEnabled(ns3::LOG_DEBUG))
{
for (std::map<TransmissionSampleKey, TransmissionSampleValue>::const_iterator iter =
m_transmissionSamples.begin();
iter != m_transmissionSamples.end();
iter++)
{
NS_LOG_DEBUG(iter->first.transmitter
<< "/" << iter->first.transmitter->GetId() << ", " << iter->first.receiver
<< "/" << iter->first.receiver->GetId() << ", " << iter->first.channel
<< " => " << iter->second.bytes << " (@ " << &iter->second << ")");
}
}
NS_LOG_DEBUG("m_transmissionSamples end.");
#endif
std::map<TransmissionSampleKey, TransmissionSampleValue>::iterator iter =
m_transmissionSamples.find(key);
if (iter == m_transmissionSamples.end())
{
TransmissionSampleValue sample = {packet->GetSize()};
NS_LOG_DEBUG("RX: from " << key.transmitter << "/" << key.transmitter->GetId() << " to "
<< key.receiver << "/" << key.receiver->GetId() << " channel "
<< channel << ": " << packet->GetSize()
<< " bytes more. => new sample with " << packet->GetSize()
<< " bytes.");
m_transmissionSamples[key] = sample;
}
else
{
TransmissionSampleValue& sample = iter->second;
NS_LOG_DEBUG("RX: from " << key.transmitter << "/" << key.transmitter->GetId() << " to "
<< key.receiver << "/" << key.receiver->GetId() << " channel "
<< channel << ": " << packet->GetSize()
<< " bytes more. => sample " << &sample << " with bytes "
<< sample.bytes);
sample.bytes += packet->GetSize();
}
}
void
PyViz::TraceNetDevRxWifi(std::string context, Ptr<const Packet> packet)
{
NS_LOG_FUNCTION(context << packet->GetUid());
/*
* To DS From DS Address 1 Address 2 Address 3 Address 4
*----------------------------------------------------------------------
* 0 0 Destination Source BSSID N/A
* 0 1 Destination BSSID Source N/A
* 1 0 BSSID Source Destination N/A
* 1 1 Receiver Transmitter Destination Source
*/
WifiMacHeader hdr;
NS_ABORT_IF(packet->PeekHeader(hdr) == 0);
Mac48Address sourceAddress;
if (hdr.IsToDs() && !hdr.IsFromDs())
{
sourceAddress = hdr.GetAddr2();
}
else if (!hdr.IsToDs() && hdr.IsFromDs())
{
sourceAddress = hdr.GetAddr3();
}
else if (!hdr.IsToDs() && !hdr.IsFromDs())
{
sourceAddress = hdr.GetAddr2();
}
else
{
sourceAddress = hdr.GetAddr4();
}
TraceNetDevRxCommon(context, packet, sourceAddress);
}
void
PyViz::TraceNetDevRxCsma(std::string context, Ptr<const Packet> packet)
{
EthernetHeader ethernetHeader;
NS_ABORT_IF(packet->PeekHeader(ethernetHeader) == 0);
TraceNetDevRxCommon(context, packet, ethernetHeader.GetSource());
}
void
PyViz::TraceNetDevRxPointToPoint(std::string context, Ptr<const Packet> packet)
{
TraceNetDevRxCommon(context, packet, Mac48Address());
}
void
PyViz::TraceNetDevPromiscRxCsma(std::string context, Ptr<const Packet> packet)
{
EthernetHeader ethernetHeader;
NS_ABORT_IF(packet->PeekHeader(ethernetHeader) == 0);
NetDevice::PacketType packetType = NetDevice::PACKET_OTHERHOST; // FIXME
// Other packet types are already being received by
// TraceNetDevRxCsma; we don't want to receive them twice.
if (packetType == NetDevice::PACKET_OTHERHOST)
{
TraceNetDevRxCommon(context, packet, ethernetHeader.GetDestination());
}
}
void
PyViz::TraceNetDevTxWimax(std::string context,
Ptr<const Packet> packet,
const Mac48Address& destination)
{
NS_LOG_FUNCTION(context);
TraceNetDevTxCommon(context, packet, destination);
}
void
PyViz::TraceNetDevRxWimax(std::string context, Ptr<const Packet> packet, const Mac48Address& source)
{
NS_LOG_FUNCTION(context);
TraceNetDevRxCommon(context, packet, source);
}
void
PyViz::TraceNetDevTxLte(std::string context,
Ptr<const Packet> packet,
const Mac48Address& destination)
{
NS_LOG_FUNCTION(context);
TraceNetDevTxCommon(context, packet, destination);
}
void
PyViz::TraceNetDevRxLte(std::string context, Ptr<const Packet> packet, const Mac48Address& source)
{
NS_LOG_FUNCTION(context);
TraceNetDevRxCommon(context, packet, source);
}
// ---------------------
PyViz::TransmissionSampleList
PyViz::GetTransmissionSamples() const
{
NS_LOG_DEBUG("GetTransmissionSamples BEGIN");
TransmissionSampleList list;
for (std::map<TransmissionSampleKey, TransmissionSampleValue>::const_iterator iter =
m_transmissionSamples.begin();
iter != m_transmissionSamples.end();
iter++)
{
TransmissionSample sample;
sample.transmitter = iter->first.transmitter;
sample.receiver = iter->first.receiver;
sample.channel = iter->first.channel;
sample.bytes = iter->second.bytes;
NS_LOG_DEBUG("from " << sample.transmitter->GetId() << " to " << sample.receiver->GetId()
<< ": " << sample.bytes << " bytes.");
list.push_back(sample);
}
NS_LOG_DEBUG("GetTransmissionSamples END");
return list;
}
PyViz::PacketDropSampleList
PyViz::GetPacketDropSamples() const
{
NS_LOG_DEBUG("GetPacketDropSamples BEGIN");
PacketDropSampleList list;
for (std::map<Ptr<Node>, uint32_t>::const_iterator iter = m_packetDrops.begin();
iter != m_packetDrops.end();
iter++)
{
PacketDropSample sample;
sample.transmitter = iter->first;
sample.bytes = iter->second;
NS_LOG_DEBUG("in " << sample.transmitter->GetId() << ": " << sample.bytes
<< " bytes dropped.");
list.push_back(sample);
}
NS_LOG_DEBUG("GetPacketDropSamples END");
return list;
}
void
PyViz::SetNodesOfInterest(std::set<uint32_t> nodes)
{
m_nodesOfInterest = nodes;
}
std::vector<PyViz::NodeStatistics>
PyViz::GetNodesStatistics() const
{
std::vector<PyViz::NodeStatistics> retval;
for (std::map<uint32_t, std::vector<NetDeviceStatistics>>::const_iterator iter =
m_nodesStatistics.begin();
iter != m_nodesStatistics.end();
iter++)
{
NodeStatistics stats = {iter->first, iter->second};
retval.push_back(stats);
}
return retval;
}
PyViz::LastPacketsSample
PyViz::GetLastPackets(uint32_t nodeId) const
{
NS_LOG_DEBUG("GetLastPackets: " << nodeId);
std::map<uint32_t, LastPacketsSample>::const_iterator iter = m_lastPackets.find(nodeId);
if (iter != m_lastPackets.end())
{
return iter->second;
}
else
{
return LastPacketsSample();
}
}
namespace
{
/// Adapted from http://en.wikipedia.org/w/index.php?title=Line_clipping&oldid=248609574
class FastClipping
{
public:
/// Vector2 structure
struct Vector2
{
double x; ///< X
double y; ///< Y
};
Vector2 m_clipMin; ///< clip minimum
Vector2 m_clipMax; ///< clip maximum
/// Line structure
struct Line
{
Vector2 start; ///< start
Vector2 end; ///< end
double dx; ///< dX
double dy; ///< dY
};
private:
/**
* Clip start top function
* \param line the clip line
*/
void ClipStartTop(Line& line) const
{
line.start.x += line.dx * (m_clipMin.y - line.start.y) / line.dy;
line.start.y = m_clipMin.y;
}
/**
* Clip start bottom function
* \param line the clip line
*/
void ClipStartBottom(Line& line) const
{
line.start.x += line.dx * (m_clipMax.y - line.start.y) / line.dy;
line.start.y = m_clipMax.y;
}
/**
* Clip start right function
* \param line the clip line
*/
void ClipStartRight(Line& line) const
{
line.start.y += line.dy * (m_clipMax.x - line.start.x) / line.dx;
line.start.x = m_clipMax.x;
}
/**
* Clip start left function
* \param line the clip line
*/
void ClipStartLeft(Line& line) const
{
line.start.y += line.dy * (m_clipMin.x - line.start.x) / line.dx;
line.start.x = m_clipMin.x;
}
/**
* Clip end top function
* \param line the clip line
*/
void ClipEndTop(Line& line) const
{
line.end.x += line.dx * (m_clipMin.y - line.end.y) / line.dy;
line.end.y = m_clipMin.y;
}
/**
* Clip end bottom function
* \param line the clip line
*/
void ClipEndBottom(Line& line) const
{
line.end.x += line.dx * (m_clipMax.y - line.end.y) / line.dy;
line.end.y = m_clipMax.y;
}
/**
* Clip end right function
* \param line the clip line
*/
void ClipEndRight(Line& line) const
{
line.end.y += line.dy * (m_clipMax.x - line.end.x) / line.dx;
line.end.x = m_clipMax.x;
}
/**
* Clip end left function
* \param line the clip line
*/
void ClipEndLeft(Line& line) const
{
line.end.y += line.dy * (m_clipMin.x - line.end.x) / line.dx;
line.end.x = m_clipMin.x;
}
public:
/**
* Constructor
*
* \param clipMin minimum clipping vector
* \param clipMax maximum clipping vector
*/
FastClipping(Vector2 clipMin, Vector2 clipMax)
: m_clipMin(clipMin),
m_clipMax(clipMax)
{
}
/**
* Clip line function
* \param line the clip line
* \returns true if clipped
*/
bool ClipLine(Line& line)
{
uint8_t lineCode = 0;
if (line.end.y < m_clipMin.y)
{
lineCode |= 8;
}
else if (line.end.y > m_clipMax.y)
{
lineCode |= 4;
}
if (line.end.x > m_clipMax.x)
{
lineCode |= 2;
}
else if (line.end.x < m_clipMin.x)
{
lineCode |= 1;
}
if (line.start.y < m_clipMin.y)
{
lineCode |= 128;
}
else if (line.start.y > m_clipMax.y)
{
lineCode |= 64;
}
if (line.start.x > m_clipMax.x)
{
lineCode |= 32;
}
else if (line.start.x < m_clipMin.x)
{
lineCode |= 16;
}
// 9 - 8 - A
// | | |
// 1 - 0 - 2
// | | |
// 5 - 4 - 6
switch (lineCode)
{
// center
case 0x00:
return true;
case 0x01:
ClipEndLeft(line);
return true;
case 0x02:
ClipEndRight(line);
return true;
case 0x04:
ClipEndBottom(line);
return true;
case 0x05:
ClipEndLeft(line);
if (line.end.y > m_clipMax.y)
{
ClipEndBottom(line);
}
return true;
case 0x06:
ClipEndRight(line);
if (line.end.y > m_clipMax.y)
{
ClipEndBottom(line);
}
return true;
case 0x08:
ClipEndTop(line);
return true;
case 0x09:
ClipEndLeft(line);
if (line.end.y < m_clipMin.y)
{
ClipEndTop(line);
}
return true;
case 0x0A:
ClipEndRight(line);
if (line.end.y < m_clipMin.y)
{
ClipEndTop(line);
}
return true;
// left
case 0x10:
ClipStartLeft(line);
return true;
case 0x12:
ClipStartLeft(line);
ClipEndRight(line);
return true;
case 0x14:
ClipStartLeft(line);
if (line.start.y > m_clipMax.y)
{
return false;
}
ClipEndBottom(line);
return true;
case 0x16:
ClipStartLeft(line);
if (line.start.y > m_clipMax.y)
{
return false;
}
ClipEndBottom(line);
if (line.end.x > m_clipMax.x)
{
ClipEndRight(line);
}
return true;
case 0x18:
ClipStartLeft(line);
if (line.start.y < m_clipMin.y)
{
return false;
}
ClipEndTop(line);
return true;
case 0x1A:
ClipStartLeft(line);
if (line.start.y < m_clipMin.y)
{
return false;
}
ClipEndTop(line);
if (line.end.x > m_clipMax.x)
{
ClipEndRight(line);
}
return true;
// right
case 0x20:
ClipStartRight(line);
return true;
case 0x21:
ClipStartRight(line);
ClipEndLeft(line);
return true;
case 0x24:
ClipStartRight(line);
if (line.start.y > m_clipMax.y)
{
return false;
}
ClipEndBottom(line);
return true;
case 0x25:
ClipStartRight(line);
if (line.start.y > m_clipMax.y)
{
return false;
}
ClipEndBottom(line);
if (line.end.x < m_clipMin.x)
{
ClipEndLeft(line);
}
return true;
case 0x28:
ClipStartRight(line);
if (line.start.y < m_clipMin.y)
{
return false;
}
ClipEndTop(line);
return true;
case 0x29:
ClipStartRight(line);
if (line.start.y < m_clipMin.y)
{
return false;
}
ClipEndTop(line);
if (line.end.x < m_clipMin.x)
{
ClipEndLeft(line);
}
return true;
// bottom
case 0x40:
ClipStartBottom(line);
return true;
case 0x41:
ClipStartBottom(line);
if (line.start.x < m_clipMin.x)
{
return false;
}
ClipEndLeft(line);
if (line.end.y > m_clipMax.y)
{
ClipEndBottom(line);
}
return true;
case 0x42:
ClipStartBottom(line);
if (line.start.x > m_clipMax.x)
{
return false;
}
ClipEndRight(line);
return true;
case 0x48:
ClipStartBottom(line);
ClipEndTop(line);
return true;
case 0x49:
ClipStartBottom(line);
if (line.start.x < m_clipMin.x)
{
return false;
}
ClipEndLeft(line);
if (line.end.y < m_clipMin.y)
{
ClipEndTop(line);
}
return true;
case 0x4A:
ClipStartBottom(line);
if (line.start.x > m_clipMax.x)
{
return false;
}
ClipEndRight(line);
if (line.end.y < m_clipMin.y)
{
ClipEndTop(line);
}
return true;
// bottom-left
case 0x50:
ClipStartLeft(line);
if (line.start.y > m_clipMax.y)
{
ClipStartBottom(line);
}
return true;
case 0x52:
ClipEndRight(line);
if (line.end.y > m_clipMax.y)
{
return false;
}
ClipStartBottom(line);
if (line.start.x < m_clipMin.x)
{
ClipStartLeft(line);
}
return true;
case 0x58:
ClipEndTop(line);
if (line.end.x < m_clipMin.x)
{
return false;
}
ClipStartBottom(line);
if (line.start.x < m_clipMin.x)
{
ClipStartLeft(line);
}
return true;
case 0x5A:
ClipStartLeft(line);
if (line.start.y < m_clipMin.y)
{
return false;
}
ClipEndRight(line);
if (line.end.y > m_clipMax.y)
{
return false;
}
if (line.start.y > m_clipMax.y)
{
ClipStartBottom(line);
}
if (line.end.y < m_clipMin.y)
{
ClipEndTop(line);
}
return true;
// bottom-right
case 0x60:
ClipStartRight(line);
if (line.start.y > m_clipMax.y)
{
ClipStartBottom(line);
}
return true;
case 0x61:
ClipEndLeft(line);
if (line.end.y > m_clipMax.y)
{
return false;
}
ClipStartBottom(line);
if (line.start.x > m_clipMax.x)
{
ClipStartRight(line);
}
return true;
case 0x68:
ClipEndTop(line);
if (line.end.x > m_clipMax.x)
{
return false;
}
ClipStartRight(line);
if (line.start.y > m_clipMax.y)
{
ClipStartBottom(line);
}
return true;
case 0x69:
ClipEndLeft(line);
if (line.end.y > m_clipMax.y)
{
return false;
}
ClipStartRight(line);
if (line.start.y < m_clipMin.y)
{
return false;
}
if (line.end.y < m_clipMin.y)
{
ClipEndTop(line);
}
if (line.start.y > m_clipMax.y)
{
ClipStartBottom(line);
}
return true;
// top
case 0x80:
ClipStartTop(line);
return true;
case 0x81:
ClipStartTop(line);
if (line.start.x < m_clipMin.x)
{
return false;
}
ClipEndLeft(line);
return true;
case 0x82:
ClipStartTop(line);
if (line.start.x > m_clipMax.x)
{
return false;
}
ClipEndRight(line);
return true;
case 0x84:
ClipStartTop(line);
ClipEndBottom(line);
return true;
case 0x85:
ClipStartTop(line);
if (line.start.x < m_clipMin.x)
{
return false;
}
ClipEndLeft(line);
if (line.end.y > m_clipMax.y)
{
ClipEndBottom(line);
}
return true;
case 0x86:
ClipStartTop(line);
if (line.start.x > m_clipMax.x)
{
return false;
}
ClipEndRight(line);
if (line.end.y > m_clipMax.y)
{
ClipEndBottom(line);
}
return true;
// top-left
case 0x90:
ClipStartLeft(line);
if (line.start.y < m_clipMin.y)
{
ClipStartTop(line);
}
return true;
case 0x92:
ClipEndRight(line);
if (line.end.y < m_clipMin.y)
{
return false;
}
ClipStartTop(line);
if (line.start.x < m_clipMin.x)
{
ClipStartLeft(line);
}
return true;
case 0x94:
ClipEndBottom(line);
if (line.end.x < m_clipMin.x)
{
return false;
}
ClipStartLeft(line);
if (line.start.y < m_clipMin.y)
{
ClipStartTop(line);
}
return true;
case 0x96:
ClipStartLeft(line);
if (line.start.y > m_clipMax.y)
{
return false;
}
ClipEndRight(line);
if (line.end.y < m_clipMin.y)
{
return false;
}
if (line.start.y < m_clipMin.y)
{
ClipStartTop(line);
}
if (line.end.y > m_clipMax.y)
{
ClipEndBottom(line);
}
return true;
// top-right
case 0xA0:
ClipStartRight(line);
if (line.start.y < m_clipMin.y)
{
ClipStartTop(line);
}
return true;
case 0xA1:
ClipEndLeft(line);
if (line.end.y < m_clipMin.y)
{
return false;
}
ClipStartTop(line);
if (line.start.x > m_clipMax.x)
{
ClipStartRight(line);
}
return true;
case 0xA4:
ClipEndBottom(line);
if (line.end.x > m_clipMax.x)
{
return false;
}
ClipStartRight(line);
if (line.start.y < m_clipMin.y)
{
ClipStartTop(line);
}
return true;
case 0xA5:
ClipEndLeft(line);
if (line.end.y < m_clipMin.y)
{
return false;
}
ClipStartRight(line);
if (line.start.y > m_clipMax.y)
{
return false;
}
if (line.end.y > m_clipMax.y)
{
ClipEndBottom(line);
}
if (line.start.y < m_clipMin.y)
{
ClipStartTop(line);
}
return true;
}
return false;
}
};
} // namespace
void
PyViz::LineClipping(double boundsX1,
double boundsY1,
double boundsX2,
double boundsY2,
double& lineX1,
double& lineY1,
double& lineX2,
double& lineY2)
{
FastClipping::Vector2 clipMin = {boundsX1, boundsY1};
FastClipping::Vector2 clipMax = {boundsX2, boundsY2};
FastClipping::Line line = {{lineX1, lineY1},
{lineX2, lineY2},
(lineX2 - lineX1),
(lineY2 - lineY1)};
FastClipping clipper(clipMin, clipMax);
clipper.ClipLine(line);
lineX1 = line.start.x;
lineX2 = line.end.x;
lineY1 = line.start.y;
lineY2 = line.end.y;
}
} // namespace ns3