1644 lines
44 KiB
C++
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
|