diff --git a/.hgignore b/.hgignore index c2e599571..8389372fd 100644 --- a/.hgignore +++ b/.hgignore @@ -36,6 +36,10 @@ massif.* \.patch$ \.diff$ \.tr$ +\.txt$ +\.sh$ +\.dat$ +\.plt$ \#[^\#/]+\#$ ^coverity syntax: glob diff --git a/examples/tutorial/examples-to-run.py b/examples/tutorial/examples-to-run.py index 2e2e5fdbe..d6fa3a232 100644 --- a/examples/tutorial/examples-to-run.py +++ b/examples/tutorial/examples-to-run.py @@ -15,6 +15,7 @@ cpp_examples = [ ("fourth", "True", "True"), ("fifth", "True", "True"), ("sixth", "True", "True"), + ("seventh", "True", "True"), ] # A list of Python examples to run in order to ensure that they remain diff --git a/examples/tutorial/seventh.cc b/examples/tutorial/seventh.cc new file mode 100644 index 000000000..caa6cce59 --- /dev/null +++ b/examples/tutorial/seventh.cc @@ -0,0 +1,267 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * 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 + */ + +#include +#include "ns3/core-module.h" +#include "ns3/network-module.h" +#include "ns3/internet-module.h" +#include "ns3/point-to-point-module.h" +#include "ns3/applications-module.h" +#include "ns3/stats-module.h" + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE ("SeventhScriptExample"); + +// =========================================================================== +// +// node 0 node 1 +// +----------------+ +----------------+ +// | ns-3 TCP | | ns-3 TCP | +// +----------------+ +----------------+ +// | 10.1.1.1 | | 10.1.1.2 | +// +----------------+ +----------------+ +// | point-to-point | | point-to-point | +// +----------------+ +----------------+ +// | | +// +---------------------+ +// 5 Mbps, 2 ms +// +// +// We want to look at changes in the ns-3 TCP congestion window. We need +// to crank up a flow and hook the CongestionWindow attribute on the socket +// of the sender. Normally one would use an on-off application to generate a +// flow, but this has a couple of problems. First, the socket of the on-off +// application is not created until Application Start time, so we wouldn't be +// able to hook the socket (now) at configuration time. Second, even if we +// could arrange a call after start time, the socket is not public so we +// couldn't get at it. +// +// So, we can cook up a simple version of the on-off application that does what +// we want. On the plus side we don't need all of the complexity of the on-off +// application. On the minus side, we don't have a helper, so we have to get +// a little more involved in the details, but this is trivial. +// +// So first, we create a socket and do the trace connect on it; then we pass +// this socket into the constructor of our simple application which we then +// install in the source node. +// =========================================================================== +// +class MyApp : public Application +{ +public: + MyApp (); + virtual ~MyApp (); + + void Setup (Ptr socket, Address address, uint32_t packetSize, uint32_t nPackets, DataRate dataRate); + +private: + virtual void StartApplication (void); + virtual void StopApplication (void); + + void ScheduleTx (void); + void SendPacket (void); + + Ptr m_socket; + Address m_peer; + uint32_t m_packetSize; + uint32_t m_nPackets; + DataRate m_dataRate; + EventId m_sendEvent; + bool m_running; + uint32_t m_packetsSent; +}; + +MyApp::MyApp () + : m_socket (0), + m_peer (), + m_packetSize (0), + m_nPackets (0), + m_dataRate (0), + m_sendEvent (), + m_running (false), + m_packetsSent (0) +{ +} + +MyApp::~MyApp () +{ + m_socket = 0; +} + +void +MyApp::Setup (Ptr socket, Address address, uint32_t packetSize, uint32_t nPackets, DataRate dataRate) +{ + m_socket = socket; + m_peer = address; + m_packetSize = packetSize; + m_nPackets = nPackets; + m_dataRate = dataRate; +} + +void +MyApp::StartApplication (void) +{ + m_running = true; + m_packetsSent = 0; + m_socket->Bind (); + m_socket->Connect (m_peer); + SendPacket (); +} + +void +MyApp::StopApplication (void) +{ + m_running = false; + + if (m_sendEvent.IsRunning ()) + { + Simulator::Cancel (m_sendEvent); + } + + if (m_socket) + { + m_socket->Close (); + } +} + +void +MyApp::SendPacket (void) +{ + Ptr packet = Create (m_packetSize); + m_socket->Send (packet); + + if (++m_packetsSent < m_nPackets) + { + ScheduleTx (); + } +} + +void +MyApp::ScheduleTx (void) +{ + if (m_running) + { + Time tNext (Seconds (m_packetSize * 8 / static_cast (m_dataRate.GetBitRate ()))); + m_sendEvent = Simulator::Schedule (tNext, &MyApp::SendPacket, this); + } +} + +static void +CwndChange (Ptr stream, uint32_t oldCwnd, uint32_t newCwnd) +{ + NS_LOG_UNCOND (Simulator::Now ().GetSeconds () << "\t" << newCwnd); + *stream->GetStream () << Simulator::Now ().GetSeconds () << "\t" << oldCwnd << "\t" << newCwnd << std::endl; +} + +static void +RxDrop (Ptr file, Ptr p) +{ + NS_LOG_UNCOND ("RxDrop at " << Simulator::Now ().GetSeconds ()); + file->Write (Simulator::Now (), p); +} + +int +main (int argc, char *argv[]) +{ + NodeContainer nodes; + nodes.Create (2); + + PointToPointHelper pointToPoint; + pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps")); + pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms")); + + NetDeviceContainer devices; + devices = pointToPoint.Install (nodes); + + Ptr em = CreateObject (); + em->SetAttribute ("ErrorRate", DoubleValue (0.00001)); + devices.Get (1)->SetAttribute ("ReceiveErrorModel", PointerValue (em)); + + InternetStackHelper stack; + stack.Install (nodes); + + Ipv4AddressHelper address; + address.SetBase ("10.1.1.0", "255.255.255.252"); + Ipv4InterfaceContainer interfaces = address.Assign (devices); + + uint16_t sinkPort = 8080; + Address sinkAddress (InetSocketAddress (interfaces.GetAddress (1), sinkPort)); + PacketSinkHelper packetSinkHelper ("ns3::TcpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), sinkPort)); + ApplicationContainer sinkApps = packetSinkHelper.Install (nodes.Get (1)); + sinkApps.Start (Seconds (0.)); + sinkApps.Stop (Seconds (20.)); + + Ptr ns3TcpSocket = Socket::CreateSocket (nodes.Get (0), TcpSocketFactory::GetTypeId ()); + + Ptr app = CreateObject (); + app->Setup (ns3TcpSocket, sinkAddress, 1040, 1000, DataRate ("1Mbps")); + nodes.Get (0)->AddApplication (app); + app->SetStartTime (Seconds (1.)); + app->SetStopTime (Seconds (20.)); + + AsciiTraceHelper asciiTraceHelper; + Ptr stream = asciiTraceHelper.CreateFileStream ("seventh.cwnd"); + ns3TcpSocket->TraceConnectWithoutContext ("CongestionWindow", MakeBoundCallback (&CwndChange, stream)); + + PcapHelper pcapHelper; + Ptr file = pcapHelper.CreateFile ("seventh.pcap", std::ios::out, PcapHelper::DLT_PPP); + devices.Get (1)->TraceConnectWithoutContext ("PhyRxDrop", MakeBoundCallback (&RxDrop, file)); + + // Use GnuplotHelper to plot the packet byte count over time + GnuplotHelper plotHelper; + + // Configure the plot. The first argument is the file name prefix + // for the output files generated. The second, third, and fourth + // arguments are, respectively, the plot title, x-axis, and y-axis labels + plotHelper.ConfigurePlot ("seventh-packet-byte-count", + "Packet Byte Count vs. Time", + "Time (Seconds)", + "Packet Byte Count"); + + // Specify the probe type, probe path (in configuration namespace), and + // probe output trace source ("OutputBytes") to plot. The fourth argument + // specifies the name of the data series label on the plot. The last + // argument formats the plot by specifying where the key should be placed. + plotHelper.PlotProbe ("ns3::Ipv4PacketProbe", + "/NodeList/*/$ns3::Ipv4L3Protocol/Tx", + "OutputBytes", + "Packet Byte Count", + GnuplotAggregator::KEY_BELOW); + + // Use FileHelper to write out the packet byte count over time + FileHelper fileHelper; + + // Configure the file to be written, and the formatting of output data. + fileHelper.ConfigureFile ("seventh-packet-byte-count", + FileAggregator::FORMATTED); + + // Set the labels for this formatted output file. + fileHelper.Set2dFormat ("Time (Seconds) = %.3e\tPacket Byte Count = %.0f"); + + // Specify the probe type, probe path (in configuration namespace), and + // probe output trace source ("OutputBytes") to write. + fileHelper.WriteProbe ("ns3::Ipv4PacketProbe", + "/NodeList/*/$ns3::Ipv4L3Protocol/Tx", + "OutputBytes"); + + Simulator::Stop (Seconds (20)); + Simulator::Run (); + Simulator::Destroy (); + + return 0; +} + diff --git a/examples/tutorial/wscript b/examples/tutorial/wscript index 0aca20c3a..63ee409b3 100644 --- a/examples/tutorial/wscript +++ b/examples/tutorial/wscript @@ -23,3 +23,6 @@ def build(bld): obj = bld.create_ns3_program('sixth', ['core', 'point-to-point', 'internet', 'applications']) obj.source = 'sixth.cc' + + obj = bld.create_ns3_program('seventh', ['core', 'stats', 'point-to-point', 'internet', 'applications']) + obj.source = 'seventh.cc' diff --git a/src/applications/model/application-packet-probe.cc b/src/applications/model/application-packet-probe.cc new file mode 100644 index 000000000..6e1613799 --- /dev/null +++ b/src/applications/model/application-packet-probe.cc @@ -0,0 +1,119 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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 + * + * Authors: L. Felipe Perrone (perrone@bucknell.edu) + * Tiago G. Rodrigues (tgr002@bucknell.edu) + * + * Modified by: Mitch Watrous (watrous@u.washington.edu) + */ + +#include "ns3/application-packet-probe.h" +#include "ns3/object.h" +#include "ns3/log.h" +#include "ns3/names.h" +#include "ns3/config.h" +#include "ns3/trace-source-accessor.h" + +NS_LOG_COMPONENT_DEFINE ("ApplicationPacketProbe"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (ApplicationPacketProbe); + +TypeId +ApplicationPacketProbe::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::ApplicationPacketProbe") + .SetParent () + .AddConstructor () + .AddTraceSource ( "Output", + "The packet plus its socket address that serve as the output for this probe", + MakeTraceSourceAccessor (&ApplicationPacketProbe::m_output)) + .AddTraceSource ( "OutputBytes", + "The number of bytes in the packet", + MakeTraceSourceAccessor (&ApplicationPacketProbe::m_outputBytes)) + ; + return tid; +} + +ApplicationPacketProbe::ApplicationPacketProbe () +{ + NS_LOG_FUNCTION (this); + m_packet = 0; +} + +ApplicationPacketProbe::~ApplicationPacketProbe () +{ + NS_LOG_FUNCTION (this); +} + +void +ApplicationPacketProbe::SetValue (Ptr packet, const Address& address) +{ + NS_LOG_FUNCTION (this << packet << address); + m_packet = packet; + m_address = address; + m_output (packet, address); + + uint32_t packetSizeNew = packet->GetSize (); + m_outputBytes (m_packetSizeOld, packetSizeNew); + m_packetSizeOld = packetSizeNew; +} + +void +ApplicationPacketProbe::SetValueByPath (std::string path, Ptr packet, const Address& address) +{ + NS_LOG_FUNCTION (path << packet << address); + Ptr probe = Names::Find (path); + NS_ASSERT_MSG (probe, "Error: Can't find probe for path " << path); + probe->SetValue (packet, address); +} + +bool +ApplicationPacketProbe::ConnectByObject (std::string traceSource, Ptr obj) +{ + NS_LOG_FUNCTION (this << traceSource << obj); + NS_LOG_DEBUG ("Name of probe (if any) in names database: " << Names::FindPath (obj)); + bool connected = obj->TraceConnectWithoutContext (traceSource, MakeCallback (&ns3::ApplicationPacketProbe::TraceSink, this)); + return connected; +} + +void +ApplicationPacketProbe::ConnectByPath (std::string path) +{ + NS_LOG_FUNCTION (this << path); + NS_LOG_DEBUG ("Name of probe to search for in config database: " << path); + Config::ConnectWithoutContext (path, MakeCallback (&ns3::ApplicationPacketProbe::TraceSink, this)); +} + +void +ApplicationPacketProbe::TraceSink (Ptr packet, const Address& address) +{ + NS_LOG_FUNCTION (this << packet << address); + if (IsEnabled ()) + { + m_packet = packet; + m_address = address; + m_output (packet, address); + + uint32_t packetSizeNew = packet->GetSize (); + m_outputBytes (m_packetSizeOld, packetSizeNew); + m_packetSizeOld = packetSizeNew; + } +} + +} // namespace ns3 diff --git a/src/applications/model/application-packet-probe.h b/src/applications/model/application-packet-probe.h new file mode 100644 index 000000000..bc1db3475 --- /dev/null +++ b/src/applications/model/application-packet-probe.h @@ -0,0 +1,120 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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 + * + * Authors: L. Felipe Perrone (perrone@bucknell.edu) + * Tiago G. Rodrigues (tgr002@bucknell.edu) + * + * Modified by: Mitch Watrous (watrous@u.washington.edu) + */ + +#ifndef APPLICATION_PACKET_PROBE_H +#define APPLICATION_PACKET_PROBE_H + +#include "ns3/object.h" +#include "ns3/callback.h" +#include "ns3/boolean.h" +#include "ns3/nstime.h" +#include "ns3/packet.h" +#include "ns3/application.h" +#include "ns3/traced-value.h" +#include "ns3/simulator.h" +#include "ns3/probe.h" + +namespace ns3 { + +/** + * This class is designed to probe an underlying ns3 TraceSource + * exporting a packet and a socket address. This probe exports a + * trace source "Output" with arguments of type Ptr and + * const Address&. This probe exports another trace source + * "OutputBytes" with arguments of type uint32_t, which is the number + * of bytes in the packet. The trace sources emit values when either + * the probed trace source emits a new value, or when SetValue () is + * called. + */ +class ApplicationPacketProbe : public Probe +{ +public: + static TypeId GetTypeId (); + ApplicationPacketProbe (); + virtual ~ApplicationPacketProbe (); + + /** + * \brief Set a probe value + * + * \param packet set the traced packet equal to this + * \param address set the socket address for the traced packet equal to this + */ + void SetValue (Ptr packet, const Address& address); + + /** + * \brief Set a probe value by its name in the Config system + * + * \param path config path to access the probe + * \param packet set the traced packet equal to this + * \param address set the socket address for the traced packet equal to this + */ + static void SetValueByPath (std::string path, Ptr packet, const Address& address); + + /** + * \brief connect to a trace source attribute provided by a given object + * + * \param traceSource the name of the attribute TraceSource to connect to + * \param obj ns3::Object to connect to + * \return true if the trace source was successfully connected + */ + virtual bool ConnectByObject (std::string traceSource, Ptr obj); + + /** + * \brief connect to a trace source provided by a config path + * + * \param path Config path to bind to + * + * Note, if an invalid path is provided, the probe will not be connected + * to anything. + */ + virtual void ConnectByPath (std::string path); + +private: + /** + * \brief Method to connect to an underlying ns3::TraceSource with + * arguments of type Ptr and const Address& + * + * \param packet the traced packet + * \param address the socket address for the traced packet + * + * \internal + */ + void TraceSink (Ptr packet, const Address& address); + + TracedCallback, const Address&> m_output; + TracedCallback m_outputBytes; + + /// The traced packet. + Ptr m_packet; + + /// The socket address for the traced packet. + Address m_address; + + /// The size of the traced packet. + uint32_t m_packetSizeOld; +}; + + +} // namespace ns3 + +#endif // APPLICATION_PACKET_PROBE_H diff --git a/src/applications/wscript b/src/applications/wscript index e792da477..f71907da8 100644 --- a/src/applications/wscript +++ b/src/applications/wscript @@ -18,6 +18,7 @@ def build(bld): 'model/udp-echo-client.cc', 'model/udp-echo-server.cc', 'model/v4ping.cc', + 'model/application-packet-probe.cc', 'helper/bulk-send-helper.cc', 'helper/on-off-helper.cc', 'helper/packet-sink-helper.cc', @@ -50,6 +51,7 @@ def build(bld): 'model/udp-echo-client.h', 'model/udp-echo-server.h', 'model/v4ping.h', + 'model/application-packet-probe.h', 'helper/bulk-send-helper.h', 'helper/on-off-helper.h', 'helper/packet-sink-helper.h', diff --git a/src/internet/model/ipv4-packet-probe.cc b/src/internet/model/ipv4-packet-probe.cc new file mode 100644 index 000000000..596af25f8 --- /dev/null +++ b/src/internet/model/ipv4-packet-probe.cc @@ -0,0 +1,123 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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 + * + * Authors: L. Felipe Perrone (perrone@bucknell.edu) + * Tiago G. Rodrigues (tgr002@bucknell.edu) + * + * Modified by: Mitch Watrous (watrous@u.washington.edu) + */ + +#include "ns3/ipv4-packet-probe.h" +#include "ns3/object.h" +#include "ns3/log.h" +#include "ns3/names.h" +#include "ns3/config.h" +#include "ns3/trace-source-accessor.h" + +NS_LOG_COMPONENT_DEFINE ("Ipv4PacketProbe"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (Ipv4PacketProbe); + +TypeId +Ipv4PacketProbe::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::Ipv4PacketProbe") + .SetParent () + .AddConstructor () + .AddTraceSource ( "Output", + "The packet plus its IPv4 object and interface that serve as the output for this probe", + MakeTraceSourceAccessor (&Ipv4PacketProbe::m_output)) + .AddTraceSource ( "OutputBytes", + "The number of bytes in the packet", + MakeTraceSourceAccessor (&Ipv4PacketProbe::m_outputBytes)) + ; + return tid; +} + +Ipv4PacketProbe::Ipv4PacketProbe () +{ + NS_LOG_FUNCTION (this); + m_packet = 0; + m_ipv4 = 0; + m_interface = 0; +} + +Ipv4PacketProbe::~Ipv4PacketProbe () +{ + NS_LOG_FUNCTION (this); +} + +void +Ipv4PacketProbe::SetValue (Ptr packet, Ptr ipv4, uint32_t interface) +{ + NS_LOG_FUNCTION (this << packet << ipv4 << interface); + m_packet = packet; + m_ipv4 = ipv4; + m_interface = interface; + m_output (packet, ipv4, interface); + + uint32_t packetSizeNew = packet->GetSize (); + m_outputBytes (m_packetSizeOld, packetSizeNew); + m_packetSizeOld = packetSizeNew; +} + +void +Ipv4PacketProbe::SetValueByPath (std::string path, Ptr packet, Ptr ipv4, uint32_t interface) +{ + NS_LOG_FUNCTION (path << packet << ipv4 << interface); + Ptr probe = Names::Find (path); + NS_ASSERT_MSG (probe, "Error: Can't find probe for path " << path); + probe->SetValue (packet, ipv4, interface); +} + +bool +Ipv4PacketProbe::ConnectByObject (std::string traceSource, Ptr obj) +{ + NS_LOG_FUNCTION (this << traceSource << obj); + NS_LOG_DEBUG ("Name of probe (if any) in names database: " << Names::FindPath (obj)); + bool connected = obj->TraceConnectWithoutContext (traceSource, MakeCallback (&ns3::Ipv4PacketProbe::TraceSink, this)); + return connected; +} + +void +Ipv4PacketProbe::ConnectByPath (std::string path) +{ + NS_LOG_FUNCTION (this << path); + NS_LOG_DEBUG ("Name of probe to search for in config database: " << path); + Config::ConnectWithoutContext (path, MakeCallback (&ns3::Ipv4PacketProbe::TraceSink, this)); +} + +void +Ipv4PacketProbe::TraceSink (Ptr packet, Ptr ipv4, uint32_t interface) +{ + NS_LOG_FUNCTION (this << packet << ipv4 << interface); + if (IsEnabled ()) + { + m_packet = packet; + m_ipv4 = ipv4; + m_interface = interface; + m_output (packet, ipv4, interface); + + uint32_t packetSizeNew = packet->GetSize (); + m_outputBytes (m_packetSizeOld, packetSizeNew); + m_packetSizeOld = packetSizeNew; + } +} + +} // namespace ns3 diff --git a/src/internet/model/ipv4-packet-probe.h b/src/internet/model/ipv4-packet-probe.h new file mode 100644 index 000000000..f827a3c5b --- /dev/null +++ b/src/internet/model/ipv4-packet-probe.h @@ -0,0 +1,124 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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 + * + * Authors: L. Felipe Perrone (perrone@bucknell.edu) + * Tiago G. Rodrigues (tgr002@bucknell.edu) + * + * Modified by: Mitch Watrous (watrous@u.washington.edu) + */ + +#ifndef IPV4_PACKET_PROBE_H +#define IPV4_PACKET_PROBE_H + +#include "ns3/object.h" +#include "ns3/callback.h" +#include "ns3/boolean.h" +#include "ns3/nstime.h" +#include "ns3/packet.h" +#include "ns3/ipv4.h" +#include "ns3/traced-value.h" +#include "ns3/simulator.h" +#include "ns3/probe.h" + +namespace ns3 { + +/** + * This class is designed to probe an underlying ns3 TraceSource + * exporting a packet, an IPv4 object, and an interface. This probe + * exports a trace source "Output" with arguments of type Ptr, + * Ptr, and uint32_t. The Output trace source emits a value + * when either the trace source emits a new value, or when SetValue () + * is called. + */ +class Ipv4PacketProbe : public Probe +{ +public: + static TypeId GetTypeId (); + Ipv4PacketProbe (); + virtual ~Ipv4PacketProbe (); + + /** + * \brief Set a probe value + * + * \param packet set the traced packet equal to this + * \param ipv4 set the IPv4 object for the traced packet equal to this + * \param interface set the IPv4 interface for the traced packet equal to this + */ + void SetValue (Ptr packet, Ptr ipv4, uint32_t interface); + + /** + * \brief Set a probe value by its name in the Config system + * + * \param path config path to access the probe + * \param packet set the traced packet equal to this + * \param ipv4 set the IPv4 object for the traced packet equal to this + * \param interface set the IPv4 interface for the traced packet equal to this + */ + static void SetValueByPath (std::string path, Ptr packet, Ptr ipv4, uint32_t interface); + + /** + * \brief connect to a trace source attribute provided by a given object + * + * \param traceSource the name of the attribute TraceSource to connect to + * \param obj ns3::Object to connect to + * \return true if the trace source was successfully connected + */ + virtual bool ConnectByObject (std::string traceSource, Ptr obj); + + /** + * \brief connect to a trace source provided by a config path + * + * \param path Config path to bind to + * + * Note, if an invalid path is provided, the probe will not be connected + * to anything. + */ + virtual void ConnectByPath (std::string path); + +private: + /** + * \brief Method to connect to an underlying ns3::TraceSource with + * arguments of type Ptr, Ptr, and uint32_t + * + * \param packet the traced packet + * \param ipv4 the IPv4 object for the traced packet + * \param interface the IPv4 interface for the traced packet + * + * \internal + */ + void TraceSink (Ptr packet, Ptr ipv4, uint32_t interface); + + TracedCallback, Ptr, uint32_t> m_output; + TracedCallback m_outputBytes; + + /// The traced packet. + Ptr m_packet; + + /// The IPv4 object for the traced packet. + Ptr m_ipv4; + + /// The IPv4 interface for the traced packet. + uint32_t m_interface; + + /// The size of the traced packet. + uint32_t m_packetSizeOld; +}; + + +} // namespace ns3 + +#endif // IPV4_PACKET_PROBE_H diff --git a/src/internet/wscript b/src/internet/wscript index 932075fc2..477a95bfd 100644 --- a/src/internet/wscript +++ b/src/internet/wscript @@ -189,6 +189,7 @@ def build(bld): 'helper/ipv6-interface-container.cc', 'helper/ipv6-routing-helper.cc', 'model/ipv6-address-generator.cc', + 'model/ipv4-packet-probe.cc', ] internet_test = bld.create_ns3_module_test_library('internet') @@ -300,6 +301,7 @@ def build(bld): 'model/tcp-tx-buffer.h', 'model/tcp-rx-buffer.h', 'model/rtt-estimator.h', + 'model/ipv4-packet-probe.h', ] if bld.env['NSC_ENABLED']: diff --git a/src/stats/model/packet-data-calculators.cc b/src/network/utils/packet-data-calculators.cc similarity index 98% rename from src/stats/model/packet-data-calculators.cc rename to src/network/utils/packet-data-calculators.cc index d30dbca52..1b936d686 100644 --- a/src/stats/model/packet-data-calculators.cc +++ b/src/network/utils/packet-data-calculators.cc @@ -22,7 +22,7 @@ #include "ns3/packet.h" #include "ns3/mac48-address.h" -#include "basic-data-calculators.h" +#include "ns3/basic-data-calculators.h" #include "packet-data-calculators.h" using namespace ns3; diff --git a/src/stats/model/packet-data-calculators.h b/src/network/utils/packet-data-calculators.h similarity index 96% rename from src/stats/model/packet-data-calculators.h rename to src/network/utils/packet-data-calculators.h index d6c72b167..51251f46e 100644 --- a/src/stats/model/packet-data-calculators.h +++ b/src/network/utils/packet-data-calculators.h @@ -23,8 +23,8 @@ #include "ns3/packet.h" #include "ns3/mac48-address.h" - -#include "data-calculator.h" +#include "ns3/data-calculator.h" +#include "ns3/basic-data-calculators.h" namespace ns3 { diff --git a/src/network/utils/packet-probe.cc b/src/network/utils/packet-probe.cc new file mode 100644 index 000000000..2d6de134d --- /dev/null +++ b/src/network/utils/packet-probe.cc @@ -0,0 +1,117 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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 + * + * Authors: L. Felipe Perrone (perrone@bucknell.edu) + * Tiago G. Rodrigues (tgr002@bucknell.edu) + * + * Modified by: Mitch Watrous (watrous@u.washington.edu) + */ + +#include "ns3/packet-probe.h" +#include "ns3/object.h" +#include "ns3/log.h" +#include "ns3/names.h" +#include "ns3/config.h" +#include "ns3/trace-source-accessor.h" + +NS_LOG_COMPONENT_DEFINE ("PacketProbe"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (PacketProbe); + +TypeId +PacketProbe::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::PacketProbe") + .SetParent () + .AddConstructor () + .AddTraceSource ( "Output", + "The packet that serve as the output for this probe", + MakeTraceSourceAccessor (&PacketProbe::m_output)) + .AddTraceSource ( "OutputBytes", + "The number of bytes in the packet", + MakeTraceSourceAccessor (&PacketProbe::m_outputBytes)) + ; + return tid; +} + +PacketProbe::PacketProbe () +{ + NS_LOG_FUNCTION (this); + m_packet = 0; +} + +PacketProbe::~PacketProbe () +{ + NS_LOG_FUNCTION (this); +} + +void +PacketProbe::SetValue (Ptr packet) +{ + NS_LOG_FUNCTION (this << packet); + m_packet = packet; + m_output (packet); + + uint32_t packetSizeNew = packet->GetSize (); + m_outputBytes (m_packetSizeOld, packetSizeNew); + m_packetSizeOld = packetSizeNew; +} + +void +PacketProbe::SetValueByPath (std::string path, Ptr packet) +{ + NS_LOG_FUNCTION (path << packet); + Ptr probe = Names::Find (path); + NS_ASSERT_MSG (probe, "Error: Can't find probe for path " << path); + probe->SetValue (packet); +} + +bool +PacketProbe::ConnectByObject (std::string traceSource, Ptr obj) +{ + NS_LOG_FUNCTION (this << traceSource << obj); + NS_LOG_DEBUG ("Name of probe (if any) in names database: " << Names::FindPath (obj)); + bool connected = obj->TraceConnectWithoutContext (traceSource, MakeCallback (&ns3::PacketProbe::TraceSink, this)); + return connected; +} + +void +PacketProbe::ConnectByPath (std::string path) +{ + NS_LOG_FUNCTION (this << path); + NS_LOG_DEBUG ("Name of probe to search for in config database: " << path); + Config::ConnectWithoutContext (path, MakeCallback (&ns3::PacketProbe::TraceSink, this)); +} + +void +PacketProbe::TraceSink (Ptr packet) +{ + NS_LOG_FUNCTION (this << packet); + if (IsEnabled ()) + { + m_packet = packet; + m_output (packet); + + uint32_t packetSizeNew = packet->GetSize (); + m_outputBytes (m_packetSizeOld, packetSizeNew); + m_packetSizeOld = packetSizeNew; + } +} + +} // namespace ns3 diff --git a/src/network/utils/packet-probe.h b/src/network/utils/packet-probe.h new file mode 100644 index 000000000..7d0a633a1 --- /dev/null +++ b/src/network/utils/packet-probe.h @@ -0,0 +1,112 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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 + * + * Authors: L. Felipe Perrone (perrone@bucknell.edu) + * Tiago G. Rodrigues (tgr002@bucknell.edu) + * + * Modified by: Mitch Watrous (watrous@u.washington.edu) + */ + +#ifndef PACKET_PROBE_H +#define PACKET_PROBE_H + +#include "ns3/object.h" +#include "ns3/callback.h" +#include "ns3/boolean.h" +#include "ns3/nstime.h" +#include "ns3/packet.h" +#include "ns3/traced-value.h" +#include "ns3/simulator.h" +#include "ns3/probe.h" + +namespace ns3 { + +/** + * This class is designed to probe an underlying ns3 TraceSource + * exporting a packet. This probe exports a trace source "Output" + * with argument of type Ptr. This probe exports + * another trace source "OutputBytes" with arguments of type uint32_t, + * which is the number of bytes in the packet. The trace sources emit + * values when either the probed trace source emits a new value, or + * when SetValue () is called. + */ +class PacketProbe : public Probe +{ +public: + static TypeId GetTypeId (); + PacketProbe (); + virtual ~PacketProbe (); + + /** + * \brief Set a probe value + * + * \param packet set the traced packet equal to this + */ + void SetValue (Ptr packet); + + /** + * \brief Set a probe value by its name in the Config system + * + * \param path config path to access the probe + * \param packet set the traced packet equal to this + */ + static void SetValueByPath (std::string path, Ptr packet); + + /** + * \brief connect to a trace source attribute provided by a given object + * + * \param traceSource the name of the attribute TraceSource to connect to + * \param obj ns3::Object to connect to + * \return true if the trace source was successfully connected + */ + virtual bool ConnectByObject (std::string traceSource, Ptr obj); + + /** + * \brief connect to a trace source provided by a config path + * + * \param path Config path to bind to + * + * Note, if an invalid path is provided, the probe will not be connected + * to anything. + */ + virtual void ConnectByPath (std::string path); + +private: + /** + * \brief Method to connect to an underlying ns3::TraceSource with + * arguments of type Ptr + * + * \param packet the traced packet + * + * \internal + */ + void TraceSink (Ptr packet); + + TracedCallback > m_output; + TracedCallback m_outputBytes; + + /// The traced packet. + Ptr m_packet; + + /// The size of the traced packet. + uint32_t m_packetSizeOld; +}; + + +} // namespace ns3 + +#endif // PACKET_PROBE_H diff --git a/src/network/wscript b/src/network/wscript index d2f96280e..f72fa29f8 100644 --- a/src/network/wscript +++ b/src/network/wscript @@ -1,7 +1,7 @@ ## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*- def build(bld): - network = bld.create_ns3_module('network', ['core']) + network = bld.create_ns3_module('network', ['core', 'stats']) network.source = [ 'model/address.cc', 'model/application.cc', @@ -52,6 +52,8 @@ def build(bld): 'utils/red-queue.cc', 'utils/simple-channel.cc', 'utils/simple-net-device.cc', + 'utils/packet-data-calculators.cc', + 'utils/packet-probe.cc', 'helper/application-container.cc', 'helper/net-device-container.cc', 'helper/node-container.cc', @@ -130,6 +132,8 @@ def build(bld): 'utils/simple-channel.h', 'utils/simple-net-device.h', 'utils/pcap-test.h', + 'utils/packet-data-calculators.h', + 'utils/packet-probe.h', 'helper/application-container.h', 'helper/net-device-container.h', 'helper/node-container.h', diff --git a/src/stats/examples/double-probe-example.cc b/src/stats/examples/double-probe-example.cc new file mode 100644 index 000000000..511286ac5 --- /dev/null +++ b/src/stats/examples/double-probe-example.cc @@ -0,0 +1,197 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2012 University of Washington + * + * 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 + * + */ + +/* + * This example is designed to show the main features of an + * ns3::DoubleProbe. + */ + +#include + +#include "ns3/core-module.h" +#include "ns3/double-probe.h" + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE ("DoubleProbeExample"); + +/* + * This is our test object, an object that increments counters at + * various times and emits one of them as a trace source. + */ +class Emitter : public Object +{ +public: + static TypeId GetTypeId (void); + Emitter (); +private: + void DoInitialize (void); + void Emit (void); + void Count (void); + + TracedValue m_counter; // normally this would be integer type + Ptr m_var; + +}; + +NS_OBJECT_ENSURE_REGISTERED (Emitter); + +TypeId +Emitter::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::Emitter") + .AddConstructor () + .SetParent () + .AddTraceSource ("Counter", + "sample counter", + MakeTraceSourceAccessor (&Emitter::m_counter)) + ; + return tid; +} + +Emitter::Emitter (void) +{ + NS_LOG_FUNCTION (this); + m_counter = 0; + m_var = CreateObject (); +} + +void +Emitter::DoInitialize (void) +{ + NS_LOG_FUNCTION (this); + Simulator::Schedule (Seconds (m_var->GetValue ()), &Emitter::Emit, this); + Simulator::Schedule (Seconds (m_var->GetValue ()), &Emitter::Count, this); +} + +void +Emitter::Emit (void) +{ + NS_LOG_FUNCTION (this); + NS_LOG_DEBUG ("Emitting at " << Simulator::Now ().GetSeconds ()); + Simulator::Schedule (Seconds (m_var->GetValue ()), &Emitter::Emit, this); +} + +void +Emitter::Count (void) +{ + NS_LOG_FUNCTION (this); + NS_LOG_DEBUG ("Counting at " << Simulator::Now ().GetSeconds ()); + m_counter += 1.0; + DoubleProbe::SetValueByPath ("/Names/StaticallyAccessedProbe", m_counter); + Simulator::Schedule (Seconds (m_var->GetValue ()), &Emitter::Count, this); +} + +// This is a function to test hooking a raw function to the trace source +void +NotifyViaTraceSource (std::string context, double oldVal, double newVal) +{ + NS_LOG_DEBUG ("context: " << context << " old " << oldVal << " new " << newVal); +} + +// This is a function to test hooking it to the probe output +void +NotifyViaProbe (std::string context, double oldVal, double newVal) +{ + NS_LOG_DEBUG ("context: " << context << " old " << oldVal << " new " << newVal); +} + +int main (int argc, char *argv[]) +{ + CommandLine cmd; + cmd.Parse (argc, argv); + bool connected; + + Ptr emitter = CreateObject (); + Names::Add ("/Names/Emitter", emitter); + + // + // The below shows typical functionality without a probe + // (connect a sink function to a trace source) + // + connected = emitter->TraceConnect ("Counter", "sample context", MakeCallback (&NotifyViaTraceSource)); + NS_ASSERT_MSG (connected, "Trace source not connected"); + + // + // Next, we'll show several use cases of using a Probe to access and + // filter the values of the underlying trace source + // + + // + // Probe1 will be hooked directly to the Emitter trace source object + // + + // probe1 will be hooked to the Emitter trace source + Ptr probe1 = CreateObject (); + // the probe's name can serve as its context in the tracing + probe1->SetName ("ObjectProbe"); + + // Connect the probe to the emitter's Counter + connected = probe1->ConnectByObject ("Counter", emitter); + NS_ASSERT_MSG (connected, "Trace source not connected to probe1"); + + // The probe itself should generate output. The context that we provide + // to this probe (in this case, the probe name) will help to disambiguate + // the source of the trace + connected = probe1->TraceConnect ("Output", probe1->GetName (), MakeCallback (&NotifyViaProbe)); + NS_ASSERT_MSG (connected, "Trace source not connected to probe1 Output"); + + // + // Probe2 will be hooked to the Emitter trace source object by + // accessing it by path name in the Config database + // + + // Create another similar probe; this will hook up via a Config path + Ptr probe2 = CreateObject (); + probe2->SetName ("PathProbe"); + + // Note, no return value is checked here + probe2->ConnectByPath ("/Names/Emitter/Counter"); + + // The probe itself should generate output. The context that we provide + // to this probe (in this case, the probe name) will help to disambiguate + // the source of the trace + connected = probe2->TraceConnect ("Output", "/Names/Probes/PathProbe/Output", MakeCallback (&NotifyViaProbe)); + NS_ASSERT_MSG (connected, "Trace source not connected to probe2 Output"); + + // + // Probe3 will be called by the emitter directly through the + // static method SetValueByPath(). + // + Ptr probe3 = CreateObject (); + probe3->SetName ("StaticallyAccessedProbe"); + // We must add it to the config database + Names::Add ("/Names/Probes", probe3->GetName (), probe3); + + // The probe itself should generate output. The context that we provide + // to this probe (in this case, the probe name) will help to disambiguate + // the source of the trace + connected = probe3->TraceConnect ("Output", "/Names/Probes/StaticallyAccessedProbe/Output", MakeCallback (&NotifyViaProbe)); + NS_ASSERT_MSG (connected, "Trace source not connected to probe3 Output"); + + // The Emitter object is not associated with an ns-3 node, so + // it won't get started automatically, so we need to do this ourselves + Simulator::Schedule (Seconds (0.0), &Emitter::Initialize, emitter); + + Simulator::Stop (Seconds (100.0)); + Simulator::Run (); + Simulator::Destroy (); + + return 0; +} diff --git a/src/stats/examples/file-aggregator-example.cc b/src/stats/examples/file-aggregator-example.cc new file mode 100644 index 000000000..8312d75f5 --- /dev/null +++ b/src/stats/examples/file-aggregator-example.cc @@ -0,0 +1,173 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2013 University of Washington + * + * 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: Mitch Watrous (watrous@u.washington.edu) + */ + +#include "ns3/core-module.h" +#include "ns3/stats-module.h" + +using namespace ns3; + +namespace { + +//=========================================================================== +// Function: CreateCommaSeparatedFile +// +// +// This function creates a file with 2 columns of values and separated +// by commas. +//=========================================================================== + +void CreateCommaSeparatedFile () +{ + using namespace std; + + string fileName = "file-aggregator-comma-separated.txt"; + string datasetContext = "Dataset/Context/String"; + + // Create an aggregator. + Ptr aggregator = + CreateObject (fileName, FileAggregator::COMMA_SEPARATED); + + // aggregator must be turned on + aggregator->Enable (); + + double time; + double value; + + // Create the 2-D dataset. + for (time = -5.0; time <= +5.0; time += 1.0) + { + // Calculate the 2-D curve + // + // 2 + // value = time . + // + value = time * time; + + // Add this point to the plot. + aggregator->Write2d (datasetContext, time, value); + } + + // Disable logging of data for the aggregator. + aggregator->Disable (); +} + + +//=========================================================================== +// Function: CreateSpaceSeparatedFile +// +// +// This function creates a file with 2 columns of values and separated +// by spaces. +//=========================================================================== + +void CreateSpaceSeparatedFile () +{ + using namespace std; + + string fileName = "file-aggregator-space-separated.txt"; + string datasetContext = "Dataset/Context/String"; + + // Create an aggregator. Note that the default type is space + // separated. + Ptr aggregator = + CreateObject (fileName); + + // aggregator must be turned on + aggregator->Enable (); + + double time; + double value; + + // Create the 2-D dataset. + for (time = -5.0; time <= +5.0; time += 1.0) + { + // Calculate the 2-D curve + // + // 2 + // value = time . + // + value = time * time; + + // Add this point to the plot. + aggregator->Write2d (datasetContext, time, value); + } + + // Disable logging of data for the aggregator. + aggregator->Disable (); +} + + +//=========================================================================== +// Function: CreateFormattedFile +// +// +// This function creates a file with formatted values. +//=========================================================================== + +void CreateFormattedFile () +{ + using namespace std; + + string fileName = "file-aggregator-formatted-values.txt"; + string datasetContext = "Dataset/Context/String"; + + // Create an aggregator that will have formatted values. + Ptr aggregator = + CreateObject (fileName, FileAggregator::FORMATTED); + + // Set the format for the values. + aggregator->Set2dFormat ("Time = %.3e\tValue = %.0f"); + + // aggregator must be turned on + aggregator->Enable (); + + double time; + double value; + + // Create the 2-D dataset. + for (time = -5.0; time < 5.5; time += 1.0) + { + // Calculate the 2-D curve + // + // 2 + // value = time . + // + value = time * time; + + // Add this point to the plot. + aggregator->Write2d (datasetContext, time, value); + } + + // Disable logging of data for the aggregator. + aggregator->Disable (); +} + + +} // anonymous namespace + + +int main (int argc, char *argv[]) +{ + CreateCommaSeparatedFile (); + CreateSpaceSeparatedFile (); + CreateFormattedFile (); + + return 0; +} diff --git a/src/stats/examples/file-helper-example.cc b/src/stats/examples/file-helper-example.cc new file mode 100644 index 000000000..1d4d4880e --- /dev/null +++ b/src/stats/examples/file-helper-example.cc @@ -0,0 +1,157 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2013 University of Washington + * + * 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 + * + * This is based on double-probe-example.cc. + * + * Author: Mitch Watrous (watrous@u.washington.edu) + */ + +/* + * This example is designed to show the main features of an + * ns3::FileHelper. + */ + +#include + +#include "ns3/core-module.h" +#include "ns3/stats-module.h" + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE ("FileHelperExample"); + +/* + * This is our test object, an object that increments counters at + * various times and emits one of them as a trace source. + */ +class Emitter : public Object +{ +public: + static TypeId GetTypeId (void); + Emitter (); +private: + void DoInitialize (void); + void Emit (void); + void Count (void); + + TracedValue m_counter; // normally this would be integer type + Ptr m_var; + +}; + +NS_OBJECT_ENSURE_REGISTERED (Emitter); + +TypeId +Emitter::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::Emitter") + .AddConstructor () + .SetParent () + .AddTraceSource ("Counter", + "sample counter", + MakeTraceSourceAccessor (&Emitter::m_counter)) + ; + return tid; +} + +Emitter::Emitter (void) +{ + NS_LOG_FUNCTION (this); + m_counter = 0; + m_var = CreateObject (); +} + +void +Emitter::DoInitialize (void) +{ + NS_LOG_FUNCTION (this); + Simulator::Schedule (Seconds (m_var->GetValue ()), &Emitter::Emit, this); + Simulator::Schedule (Seconds (m_var->GetValue ()), &Emitter::Count, this); +} + +void +Emitter::Emit (void) +{ + NS_LOG_FUNCTION (this); + NS_LOG_DEBUG ("Emitting at " << Simulator::Now ().GetSeconds ()); + Simulator::Schedule (Seconds (m_var->GetValue ()), &Emitter::Emit, this); +} + +void +Emitter::Count (void) +{ + NS_LOG_FUNCTION (this); + NS_LOG_DEBUG ("Counting at " << Simulator::Now ().GetSeconds ()); + m_counter += 1.0; + Simulator::Schedule (Seconds (m_var->GetValue ()), &Emitter::Count, this); +} + +int main (int argc, char *argv[]) +{ + CommandLine cmd; + cmd.Parse (argc, argv); + + // + // This Emitter has a trace source object that will emit values at + // random times. + // + + Ptr emitter = CreateObject (); + Names::Add ("/Names/Emitter", emitter); + + // + // This Probe will be hooked to the Emitter's trace source object by + // accessing it by path name in the Config database. + // + + Ptr probe = CreateObject (); + probe->SetName ("PathProbe"); + Names::Add ("/Names/Probe", probe); + + // Note, no return value is checked here. + probe->ConnectByPath ("/Names/Emitter/Counter"); + + // + // This file helper will be used to put data values into a file. + // + + // Create the file helper. + FileHelper fileHelper; + + // Configure the file to be written. + fileHelper.ConfigureFile ("file-helper-example", + FileAggregator::FORMATTED); + + // Set the labels for this formatted output file. + fileHelper.Set2dFormat ("Time (Seconds) = %.3e\tCount = %.0f"); + + // Write the values generated by the probe. The path that we + // provide helps to disambiguate the source of the trace. + fileHelper.WriteProbe ("ns3::DoubleProbe", + "/Names/Probe/Output", + "Output"); + + // The Emitter object is not associated with an ns-3 node, so + // it won't get started automatically, so we need to do this ourselves + Simulator::Schedule (Seconds (0.0), &Emitter::Initialize, emitter); + + Simulator::Stop (Seconds (100.0)); + Simulator::Run (); + Simulator::Destroy (); + + return 0; +} diff --git a/src/stats/examples/gnuplot-aggregator-example.cc b/src/stats/examples/gnuplot-aggregator-example.cc new file mode 100644 index 000000000..027bfbb2b --- /dev/null +++ b/src/stats/examples/gnuplot-aggregator-example.cc @@ -0,0 +1,91 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2013 University of Washington + * + * 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: Mitch Watrous (watrous@u.washington.edu) + */ + +#include "ns3/core-module.h" +#include "ns3/stats-module.h" + +using namespace ns3; + +namespace { + +//=========================================================================== +// Function: Create2dPlot +// +// +// This function creates a 2-Dimensional plot. +//=========================================================================== + +void Create2dPlot () +{ + using namespace std; + + string fileNameWithoutExtension = "gnuplot-aggregator"; + string plotTitle = "Gnuplot Aggregator Plot"; + string plotXAxisHeading = "Time (seconds)"; + string plotYAxisHeading = "Double Values"; + string plotDatasetLabel = "Data Values"; + string datasetContext = "Dataset/Context/String"; + + // Create an aggregator. + Ptr aggregator = + CreateObject (fileNameWithoutExtension); + + // Set the aggregator's properties. + aggregator->SetTerminal ("png"); + aggregator->SetTitle (plotTitle); + aggregator->SetLegend (plotXAxisHeading, plotYAxisHeading); + + // Add a data set to the aggregator. + aggregator->Add2dDataset (datasetContext, plotDatasetLabel); + + // aggregator must be turned on + aggregator->Enable (); + + double time; + double value; + + // Create the 2-D dataset. + for (time = -5.0; time <= +5.0; time += 1.0) + { + // Calculate the 2-D curve + // + // 2 + // value = time . + // + value = time * time; + + // Add this point to the plot. + aggregator->Write2d (datasetContext, time, value); + } + + // Disable logging of data for the aggregator. + aggregator->Disable (); +} + + +} // anonymous namespace + + +int main (int argc, char *argv[]) +{ + Create2dPlot (); + + return 0; +} diff --git a/src/stats/examples/gnuplot-example.cc b/src/stats/examples/gnuplot-example.cc index 75c141e6a..2bd0c71a8 100644 --- a/src/stats/examples/gnuplot-example.cc +++ b/src/stats/examples/gnuplot-example.cc @@ -201,9 +201,9 @@ void Create3DPlotFile () plot.AppendExtra ("set ticslevel 0"); // Set the labels for each axis. - plot.AppendExtra ("set xlabel 'X Values'"); - plot.AppendExtra ("set ylabel 'Y Values'"); - plot.AppendExtra ("set zlabel 'Z Values'"); + plot.AppendExtra ("set xlabel \"X Values\""); + plot.AppendExtra ("set ylabel \"Y Values\""); + plot.AppendExtra ("set zlabel \"Z Values\""); // Set the ranges for the x and y axis. plot.AppendExtra ("set xrange [-5:+5]"); diff --git a/src/stats/examples/gnuplot-helper-example.cc b/src/stats/examples/gnuplot-helper-example.cc new file mode 100644 index 000000000..397f9d2cf --- /dev/null +++ b/src/stats/examples/gnuplot-helper-example.cc @@ -0,0 +1,160 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2013 University of Washington + * + * 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 + * + * This is based on double-probe-example.cc. + * + * Author: Mitch Watrous (watrous@u.washington.edu) + */ + +/* + * This example is designed to show the main features of an + * ns3::GnuplotHelper. + */ + +#include + +#include "ns3/core-module.h" +#include "ns3/stats-module.h" + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE ("GnuplotHelperExample"); + +/* + * This is our test object, an object that increments counters at + * various times and emits one of them as a trace source. + */ +class Emitter : public Object +{ +public: + static TypeId GetTypeId (void); + Emitter (); +private: + void DoInitialize (void); + void Emit (void); + void Count (void); + + TracedValue m_counter; // normally this would be integer type + Ptr m_var; + +}; + +NS_OBJECT_ENSURE_REGISTERED (Emitter); + +TypeId +Emitter::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::Emitter") + .AddConstructor () + .SetParent () + .AddTraceSource ("Counter", + "sample counter", + MakeTraceSourceAccessor (&Emitter::m_counter)) + ; + return tid; +} + +Emitter::Emitter (void) +{ + NS_LOG_FUNCTION (this); + m_counter = 0; + m_var = CreateObject (); +} + +void +Emitter::DoInitialize (void) +{ + NS_LOG_FUNCTION (this); + Simulator::Schedule (Seconds (m_var->GetValue ()), &Emitter::Emit, this); + Simulator::Schedule (Seconds (m_var->GetValue ()), &Emitter::Count, this); +} + +void +Emitter::Emit (void) +{ + NS_LOG_FUNCTION (this); + NS_LOG_DEBUG ("Emitting at " << Simulator::Now ().GetSeconds ()); + Simulator::Schedule (Seconds (m_var->GetValue ()), &Emitter::Emit, this); +} + +void +Emitter::Count (void) +{ + NS_LOG_FUNCTION (this); + NS_LOG_DEBUG ("Counting at " << Simulator::Now ().GetSeconds ()); + m_counter += 1.0; + Simulator::Schedule (Seconds (m_var->GetValue ()), &Emitter::Count, this); +} + +int main (int argc, char *argv[]) +{ + CommandLine cmd; + cmd.Parse (argc, argv); + + // + // This Emitter has a trace source object that will emit values at + // random times. + // + + Ptr emitter = CreateObject (); + Names::Add ("/Names/Emitter", emitter); + + // + // This Probe will be hooked to the Emitter's trace source object by + // accessing it by path name in the Config database. + // + + Ptr probe = CreateObject (); + probe->SetName ("PathProbe"); + Names::Add ("/Names/Probe", probe); + + // Note, no return value is checked here. + probe->ConnectByPath ("/Names/Emitter/Counter"); + + // + // This gnuplot helper will be used to produce output used to make + // gnuplot plots. + // + + // Create the gnuplot helper. + GnuplotHelper plotHelper; + + // Configure the plot. + plotHelper.ConfigurePlot ("gnuplot-helper-example", + "Emitter Counts vs. Time", + "Time (Seconds)", + "Emitter Count", + "png"); + + // Plot the values generated by the probe. The path that we provide + // helps to disambiguate the source of the trace. + plotHelper.PlotProbe ("ns3::DoubleProbe", + "/Names/Probe/Output", + "Output", + "Emitter Count", + GnuplotAggregator::KEY_INSIDE); + + // The Emitter object is not associated with an ns-3 node, so + // it won't get started automatically, so we need to do this ourselves + Simulator::Schedule (Seconds (0.0), &Emitter::Initialize, emitter); + + Simulator::Stop (Seconds (100.0)); + Simulator::Run (); + Simulator::Destroy (); + + return 0; +} diff --git a/src/stats/examples/wscript b/src/stats/examples/wscript index bc1bfbf6e..013a19d6b 100644 --- a/src/stats/examples/wscript +++ b/src/stats/examples/wscript @@ -4,7 +4,22 @@ def build(bld): if not bld.env['ENABLE_EXAMPLES']: return; - obj = bld.create_ns3_program('gnuplot-example', ['stats']) + obj = bld.create_ns3_program('gnuplot-example', ['network', 'stats']) obj.source = 'gnuplot-example.cc' + program = bld.create_ns3_program('double-probe-example', ['network', 'stats']) + program.source = 'double-probe-example.cc' + + program = bld.create_ns3_program('gnuplot-aggregator-example', ['network', 'stats']) + program.source = 'gnuplot-aggregator-example.cc' + + program = bld.create_ns3_program('gnuplot-helper-example', ['network', 'stats']) + program.source = 'gnuplot-helper-example.cc' + + program = bld.create_ns3_program('file-aggregator-example', ['network', 'stats']) + program.source = 'file-aggregator-example.cc' + + program = bld.create_ns3_program('file-helper-example', ['network', 'stats']) + program.source = 'file-helper-example.cc' + diff --git a/src/stats/helper/file-helper.cc b/src/stats/helper/file-helper.cc new file mode 100644 index 000000000..38d09c9e3 --- /dev/null +++ b/src/stats/helper/file-helper.cc @@ -0,0 +1,559 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2013 University of Washington + * + * 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: Mitch Watrous (watrous@u.washington.edu) + */ + +#include +#include +#include + +#include "file-helper.h" +#include "ns3/abort.h" +#include "ns3/log.h" +#include "ns3/config.h" +#include "ns3/get-wildcard-matches.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("FileHelper"); + +FileHelper::FileHelper () + : m_aggregator (0), + m_fileProbeCount (0), + m_fileType (FileAggregator::SPACE_SEPARATED), + m_outputFileNameWithoutExtension ("file-helper"), + m_hasHeadingBeenSet (false) +{ + NS_LOG_FUNCTION (this); + + // Note that this does not construct an aggregator. It will be + // constructed later when needed. +} + +FileHelper::FileHelper (const std::string &outputFileNameWithoutExtension, + enum FileAggregator::FileType fileType) + : m_aggregator (0), + m_fileProbeCount (0), + m_fileType (fileType), + m_outputFileNameWithoutExtension (outputFileNameWithoutExtension), + m_hasHeadingBeenSet (false) +{ + NS_LOG_FUNCTION (this); + + // Note that this does not construct an aggregator. It will be + // constructed later when needed. +} + +FileHelper::~FileHelper () +{ + NS_LOG_FUNCTION (this); +} + +void +FileHelper::ConfigureFile (const std::string &outputFileNameWithoutExtension, + enum FileAggregator::FileType fileType) +{ + NS_LOG_FUNCTION (this << outputFileNameWithoutExtension << fileType); + + // See if an aggregator has already been constructed. + if (m_aggregator != 0) + { + NS_LOG_WARN ("An existing aggregator object " << m_aggregator << + " may be destroyed if no references remain."); + } + + // Store these so that they can be used to construct the aggregator. + m_fileType = fileType; + m_outputFileNameWithoutExtension = outputFileNameWithoutExtension; + m_hasHeadingBeenSet = false; + + // Note that this does not construct an aggregator. It will be + // constructed later when needed. +} + +void +FileHelper::WriteProbe (const std::string &typeId, + const std::string &path, + const std::string &probeTraceSource) +{ + NS_LOG_FUNCTION (this << typeId << path << probeTraceSource); + + std::string pathWithoutLastToken; + std::string lastToken; + + // See if the path has any wildcards. + bool pathHasNoWildcards = path.find ("*") == std::string::npos; + + // Remove the last token from the path. + unsigned lastSlash = path.find_last_of ("/"); + if (lastSlash == std::string::npos) + { + pathWithoutLastToken = path; + lastToken = ""; + } + else + { + // Chop off up to last token. + pathWithoutLastToken = path.substr (0, lastSlash); + + // Save the last token without the last slash. + lastToken = path.substr (lastSlash + 1, std::string::npos); + } + + // See if there are any matches for the probe's path with the last + // token removed. + Config::MatchContainer matches = Config::LookupMatches (pathWithoutLastToken); + uint32_t matchCount = matches.GetN (); + + // This is used to make the probe's context be unique. + std::string matchIdentifier; + + /// This is used to indicate if multiple aggregators are needed. + bool onlyOneAggregator; + + // Hook one or more probes and one or more aggregators together. + if (matchCount == 1 && pathHasNoWildcards) + { + // Connect the probe to the aggregator only once because there + // is only one matching config path. There is no need to find + // the wildcard matches because the passed in path has none. + matchIdentifier = "0"; + onlyOneAggregator = true; + ConnectProbeToAggregator (typeId, + matchIdentifier, + path, + probeTraceSource, + m_outputFileNameWithoutExtension, + onlyOneAggregator); + } + else if (matchCount > 0) + { + // Handle all of the matches if there are more than one. + for (uint32_t i = 0; i < matchCount; i++) + { + // Set the match identifier. + std::ostringstream matchIdentifierStream; + matchIdentifierStream << i; + matchIdentifier = matchIdentifierStream.str (); + onlyOneAggregator = false; + + // Construct the matched path and get the matches for each + // of the wildcards. + std::string wildcardSeparator = "-"; + std::string matchedPath = matches.GetMatchedPath (i) + lastToken; + std::string wildcardMatches = GetWildcardMatches (path, + matchedPath, + wildcardSeparator); + + // Connect the probe to the aggregator for this match. + ConnectProbeToAggregator (typeId, + matchIdentifier, + matchedPath, + probeTraceSource, + m_outputFileNameWithoutExtension + "-" + wildcardMatches, + onlyOneAggregator); + } + } + else + { + // There is a problem if there are no matching config paths. + NS_FATAL_ERROR ("Lookup of " << path << " got no matches"); + } +} + +void +FileHelper::AddProbe (const std::string &typeId, + const std::string &probeName, + const std::string &path) +{ + NS_LOG_FUNCTION (this << typeId << probeName << path); + + // See if this probe had already been added. + if (m_probeMap.count (probeName) > 0) + { + NS_ABORT_MSG ("That probe has already been added"); + } + + // Prepare the factory to create an object with the requested type. + m_factory.SetTypeId (typeId); + + // Create a base class object in order to validate the type. + Ptr probe = m_factory.Create ()->GetObject (); + if (probe == 0) + { + NS_ABORT_MSG ("The requested type is not a probe"); + } + + // Set the probe's name. + probe->SetName (probeName); + + // Set the path. Note that no return value is checked here. + probe->ConnectByPath (path); + + // Enable logging of data for the probe. + probe->Enable (); + + // Add this probe to the map so that its values can be used. + m_probeMap[probeName] = std::make_pair (probe, typeId); +} + +void +FileHelper::AddTimeSeriesAdaptor (const std::string &adaptorName) +{ + NS_LOG_FUNCTION (this << adaptorName); + + // See if this time series adaptor had already been added. + if (m_timeSeriesAdaptorMap.count (adaptorName) > 0) + { + NS_ABORT_MSG ("That time series adaptor has already been added"); + } + + // Create the time series adaptor. + Ptr timeSeriesAdaptor = CreateObject (); + + // Enable logging of data for the time series adaptor. + timeSeriesAdaptor->Enable (); + + // Add this time series adaptor to the map so that it can be used. + m_timeSeriesAdaptorMap[adaptorName] = timeSeriesAdaptor; +} + +void +FileHelper::AddAggregator (const std::string &aggregatorName, + const std::string &outputFileName, + bool onlyOneAggregator) +{ + NS_LOG_FUNCTION (this << aggregatorName << outputFileName << onlyOneAggregator); + + // See if this file aggregator had already been added. + if (m_aggregatorMap.count (aggregatorName) > 0) + { + NS_ABORT_MSG ("That file aggregator has already been added"); + } + + // If there is only going to be 1 file aggregator, then use the one + // already constructed in the map. + if (onlyOneAggregator) + { + // Get a pointer to the aggregator. + Ptr singleAggregator = GetAggregatorSingle (); + + m_aggregatorMap[aggregatorName] = singleAggregator; + return; + } + + // Create the file aggregator with the proper file type. + Ptr multipleAggregator = + CreateObject (outputFileName, m_fileType); + + // Set all of the format strings for the aggregator. + multipleAggregator->Set1dFormat (m_1dFormat); + multipleAggregator->Set2dFormat (m_2dFormat); + multipleAggregator->Set3dFormat (m_3dFormat); + multipleAggregator->Set4dFormat (m_4dFormat); + multipleAggregator->Set5dFormat (m_5dFormat); + multipleAggregator->Set6dFormat (m_6dFormat); + multipleAggregator->Set7dFormat (m_7dFormat); + multipleAggregator->Set8dFormat (m_8dFormat); + multipleAggregator->Set9dFormat (m_9dFormat); + multipleAggregator->Set10dFormat (m_10dFormat); + + // Set the heading + multipleAggregator->SetHeading (m_heading); + + // Enable logging of data for the file aggregator. + multipleAggregator->Enable (); + + // Add this file aggregator to the map so that it can be used. + m_aggregatorMap[aggregatorName] = multipleAggregator; +} + +Ptr +FileHelper::GetProbe (std::string probeName) const +{ + NS_LOG_FUNCTION (this << probeName); + + // Look for the probe. + std::map, std::string> >::const_iterator mapIterator = m_probeMap.find (probeName); + + // Return the probe if it has been added. + if (mapIterator != m_probeMap.end ()) + { + return mapIterator->second.first; + } + else + { + NS_ABORT_MSG ("That probe has not been added"); + } +} + +Ptr +FileHelper::GetAggregatorSingle () +{ + NS_LOG_FUNCTION (this); + + // Do a lazy construction of the single aggregator if it hasn't + // already been constructed. + if (!m_aggregator) + { + // Create the aggregator. + std::string outputFileName = m_outputFileNameWithoutExtension + ".txt"; + m_aggregator = CreateObject (outputFileName, m_fileType); + + // Set all of the format strings for the aggregator. + m_aggregator->Set1dFormat (m_1dFormat); + m_aggregator->Set2dFormat (m_2dFormat); + m_aggregator->Set3dFormat (m_3dFormat); + m_aggregator->Set4dFormat (m_4dFormat); + m_aggregator->Set5dFormat (m_5dFormat); + m_aggregator->Set6dFormat (m_6dFormat); + m_aggregator->Set7dFormat (m_7dFormat); + m_aggregator->Set8dFormat (m_8dFormat); + m_aggregator->Set9dFormat (m_9dFormat); + m_aggregator->Set10dFormat (m_10dFormat); + + // Set the heading + m_aggregator->SetHeading (m_heading); + + // Enable logging of data for the aggregator. + m_aggregator->Enable (); + } + return m_aggregator; +} + +Ptr +FileHelper::GetAggregatorMultiple (const std::string &aggregatorName, + const std::string &outputFileName) +{ + NS_LOG_FUNCTION (this); + + // See if this file aggregator had already been added. + if (m_aggregatorMap.count (aggregatorName) > 0) + { + return m_aggregatorMap[aggregatorName]; + } + + // Do a lazy construction of the aggregator if it hasn't already + // been constructed. + bool onlyOneAggregator = false; + AddAggregator (aggregatorName, + outputFileName, + onlyOneAggregator); + + return m_aggregatorMap[aggregatorName]; +} + +void +FileHelper::SetHeading (const std::string &heading) +{ + NS_LOG_FUNCTION (this << heading); + + m_hasHeadingBeenSet = true; + m_heading = heading; +} + +void +FileHelper::Set1dFormat (const std::string &format) +{ + NS_LOG_FUNCTION (this << format); + + m_1dFormat = format; +} + +void +FileHelper::Set2dFormat (const std::string &format) +{ + NS_LOG_FUNCTION (this << format); + + m_2dFormat = format; +} + +void +FileHelper::Set3dFormat (const std::string &format) +{ + NS_LOG_FUNCTION (this << format); + + m_3dFormat = format; +} + +void +FileHelper::Set4dFormat (const std::string &format) +{ + NS_LOG_FUNCTION (this << format); + + m_4dFormat = format; +} + +void +FileHelper::Set5dFormat (const std::string &format) +{ + NS_LOG_FUNCTION (this << format); + + m_5dFormat = format; +} + +void +FileHelper::Set6dFormat (const std::string &format) +{ + NS_LOG_FUNCTION (this << format); + + m_6dFormat = format; +} + +void +FileHelper::Set7dFormat (const std::string &format) +{ + NS_LOG_FUNCTION (this << format); + + m_7dFormat = format; +} + +void +FileHelper::Set8dFormat (const std::string &format) +{ + NS_LOG_FUNCTION (this << format); + + m_8dFormat = format; +} + +void +FileHelper::Set9dFormat (const std::string &format) +{ + NS_LOG_FUNCTION (this << format); + + m_9dFormat = format; +} + +void +FileHelper::Set10dFormat (const std::string &format) +{ + NS_LOG_FUNCTION (this << format); + + m_10dFormat = format; +} + +void +FileHelper::ConnectProbeToAggregator (const std::string &typeId, + const std::string &matchIdentifier, + const std::string &path, + const std::string &probeTraceSource, + const std::string &outputFileNameWithoutExtension, + bool onlyOneAggregator) +{ + NS_LOG_FUNCTION (this << typeId << matchIdentifier << path << probeTraceSource + << outputFileNameWithoutExtension << onlyOneAggregator); + + // Increment the total number of file probes that have been created. + m_fileProbeCount++; + + // Create a unique name for this probe. + std::ostringstream probeNameStream; + probeNameStream << "FileProbe-" << m_fileProbeCount; + std::string probeName = probeNameStream.str (); + + // Create a unique dataset context string for this probe. + std::string probeContext = probeName + + "/" + matchIdentifier + "/" + probeTraceSource; + + // Add the probe to the map of probes, which will keep the probe in + // memory after this function ends. + AddProbe (typeId, probeName, path); + + // Because the callbacks to the probes' trace sources don't use the + // probe's context, a unique adaptor needs to be created for each + // probe context so that information is not lost. + AddTimeSeriesAdaptor (probeContext); + + // Connect the probe to the adaptor. + if (m_probeMap[probeName].second == "ns3::DoubleProbe") + { + m_probeMap[probeName].first->TraceConnectWithoutContext + (probeTraceSource, + MakeCallback (&TimeSeriesAdaptor::TraceSinkDouble, + m_timeSeriesAdaptorMap[probeContext])); + } + else if (m_probeMap[probeName].second == "ns3::BooleanProbe") + { + m_probeMap[probeName].first->TraceConnectWithoutContext + (probeTraceSource, + MakeCallback (&TimeSeriesAdaptor::TraceSinkBoolean, + m_timeSeriesAdaptorMap[probeContext])); + } + else if (m_probeMap[probeName].second == "ns3::PacketProbe") + { + m_probeMap[probeName].first->TraceConnectWithoutContext + (probeTraceSource, + MakeCallback (&TimeSeriesAdaptor::TraceSinkUinteger32, + m_timeSeriesAdaptorMap[probeContext])); + } + else if (m_probeMap[probeName].second == "ns3::ApplicationPacketProbe") + { + m_probeMap[probeName].first->TraceConnectWithoutContext + (probeTraceSource, + MakeCallback (&TimeSeriesAdaptor::TraceSinkUinteger32, + m_timeSeriesAdaptorMap[probeContext])); + } + else if (m_probeMap[probeName].second == "ns3::Ipv4PacketProbe") + { + m_probeMap[probeName].first->TraceConnectWithoutContext + (probeTraceSource, + MakeCallback (&TimeSeriesAdaptor::TraceSinkUinteger32, + m_timeSeriesAdaptorMap[probeContext])); + } + else if (m_probeMap[probeName].second == "ns3::Uinteger8Probe") + { + m_probeMap[probeName].first->TraceConnectWithoutContext + (probeTraceSource, + MakeCallback (&TimeSeriesAdaptor::TraceSinkUinteger8, + m_timeSeriesAdaptorMap[probeContext])); + } + else if (m_probeMap[probeName].second == "ns3::Uinteger16Probe") + { + m_probeMap[probeName].first->TraceConnectWithoutContext + (probeTraceSource, + MakeCallback (&TimeSeriesAdaptor::TraceSinkUinteger16, + m_timeSeriesAdaptorMap[probeContext])); + } + else if (m_probeMap[probeName].second == "ns3::Uinteger32Probe") + { + m_probeMap[probeName].first->TraceConnectWithoutContext + (probeTraceSource, + MakeCallback (&TimeSeriesAdaptor::TraceSinkUinteger32, + m_timeSeriesAdaptorMap[probeContext])); + } + else + { + NS_FATAL_ERROR ("Unknown probe type " << m_probeMap[probeName].second << "; need to add support in the helper for this"); + } + + // Add the aggregator to the map of aggregators, which will keep the + // aggregator in memory after this function ends. + std::string outputFileName = outputFileNameWithoutExtension + ".txt"; + AddAggregator (probeContext, outputFileName, onlyOneAggregator); + + // Connect the adaptor to the aggregator. + std::string adaptorTraceSource = "Output"; + m_timeSeriesAdaptorMap[probeContext]->TraceConnect + (adaptorTraceSource, + probeContext, + MakeCallback (&FileAggregator::Write2d, + m_aggregatorMap[probeContext])); +} + +} // namespace ns3 + diff --git a/src/stats/helper/file-helper.h b/src/stats/helper/file-helper.h new file mode 100644 index 000000000..0abcc7aac --- /dev/null +++ b/src/stats/helper/file-helper.h @@ -0,0 +1,323 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2013 University of Washington + * + * 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: Mitch Watrous (watrous@u.washington.edu) + */ + +#ifndef FILE_HELPER_H +#define FILE_HELPER_H + +#include +#include +#include "ns3/object-factory.h" +#include "ns3/ptr.h" +#include "ns3/probe.h" +#include "ns3/file-aggregator.h" +#include "ns3/time-series-adaptor.h" + +namespace ns3 { + +/** + * \brief Helper class used to put data values into a file. + **/ +class FileHelper +{ +public: + /** + * Constructs a file helper that will create a space separated file + * named "file-helper.txt" unless it is later configured otherwise. + */ + FileHelper (); + + /** + * \param outputFileNameWithoutExtension name of output file to + * write with no extension + * \param fileType type of file to write. + * + * Constructs a file helper that will create a file named + * outputFileNameWithoutExtension plus possible extra information + * from wildcard matches plus ".txt" with values printed as + * specified by fileType. The default file type is space-separated. + */ + FileHelper (const std::string &outputFileNameWithoutExtension, + enum FileAggregator::FileType fileType = FileAggregator::SPACE_SEPARATED); + + virtual ~FileHelper (); + + /** + * \param outputFileNameWithoutExtension name of output file to + * write with no extension + * \param fileType type of file to write. + * + * Configures file related parameters for this file helper so that + * it will create a file named outputFileNameWithoutExtension plus + * possible extra information from wildcard matches plus ".txt" with + * values printed as specified by fileType. The default file type + * is space-separated. + */ + void ConfigureFile (const std::string &outputFileNameWithoutExtension, + enum FileAggregator::FileType fileType = FileAggregator::SPACE_SEPARATED); + + /** + * \param typeId the type ID for the probe used when it is created. + * \param path Config path to access the probe. + * \param probeTraceSource the probe trace source to access. + * + * Creates output files generated by hooking the ns-3 trace source + * with a probe, and then writing the values from the + * probeTraceSource. The output file names will have the text stored + * in m_outputFileNameWithoutExtension plus ".txt", and will consist + * of the 'newValue' at each timestamp. + * + * If the config path has more than one match in the system + * (e.g. there is a wildcard), then one output file for each match + * will be created. The output file names will contain the text in + * m_outputFileNameWithoutExtension plus the matched characters for + * each of the wildcards in the config path, separated by dashes, + * plus ".txt". For example, if the value in + * m_outputFileNameWithoutExtension is the string + * "packet-byte-count", and there are two wildcards in the path, + * then output file names like "packet-byte-count-0-0.txt" or + * "packet-byte-count-12-9.txt" will be possible as names for the + * files that will be created. + * + * A fatal error will result if an unknown probe type is used. + */ + void WriteProbe (const std::string &typeId, + const std::string &path, + const std::string &probeTraceSource); + + /** + * \param typeId the type ID for the probe used when it is created. + * \param probeName the probe's name. + * \param path Config path to access the probe + * + * \brief Adds a probe to be used to write values to files. + */ + void AddProbe (const std::string &typeId, + const std::string &probeName, + const std::string &path); + + /** + * \param adaptorName the timeSeriesAdaptor's name. + * + * \brief Adds a time series adaptor to be used to write the file. + */ + void AddTimeSeriesAdaptor (const std::string &adaptorName); + + /** + * \param aggregatorName the aggregator's name. + * \param outputFileName name of the file to write. + * \param onlyOneAggregator indicates if more than one aggregator + * should be created or not. + * + * \brief Adds an aggregator to be used to write values to files. + */ + void AddAggregator (const std::string &aggregatorName, + const std::string &outputFileName, + bool onlyOneAggregator); + + /** + * \param probeName the probe's name. + * + * \brief Gets the specified probe. + */ + Ptr GetProbe (std::string probeName) const; + + /** + * \brief Gets the single aggregator that is always constructed. + * + * This function is non-const because an aggregator may be lazily + * created by this method. + */ + Ptr GetAggregatorSingle (); + + /** + * \param aggregatorName name for aggregator. + * \param outputFileName name of output file to write. + * + * \brief Gets one of the multiple aggregators from the map. + * + * This function is non-const because an aggregator may be lazily + * created by this method. + */ + Ptr GetAggregatorMultiple (const std::string &aggregatorName, + const std::string &outputFileName); + + /** + * \param heading the heading string. + * + * \brief Sets the heading string that will be printed on the first + * line of the file. + * + * Note that the heading string will only be printed if it has been + * set by calling this function. + */ + void SetHeading (const std::string &heading); + + /** + * \param format the 1D format string. + * + * \brief Sets the 1D format string for the C-style sprintf() + * function. + */ + void Set1dFormat (const std::string &format); + + /** + * \param format the 2D format string. + * + * \brief Sets the 2D format string for the C-style sprintf() + * function. + */ + void Set2dFormat (const std::string &format); + + /** + * \param format the 3D format string. + * + * \brief Sets the 3D format string for the C-style sprintf() + * function. + */ + void Set3dFormat (const std::string &format); + + /** + * \param format the 4D format string. + * + * \brief Sets the 4D format string for the C-style sprintf() + * function. + */ + void Set4dFormat (const std::string &format); + + /** + * \param format the 5D format string. + * + * \brief Sets the 5D format string for the C-style sprintf() + * function. + */ + void Set5dFormat (const std::string &format); + + /** + * \param format the 6D format string. + * + * \brief Sets the 6D format string for the C-style sprintf() + * function. + */ + void Set6dFormat (const std::string &format); + + /** + * \param format the 7D format string. + * + * \brief Sets the 7D format string for the C-style sprintf() + * function. + */ + void Set7dFormat (const std::string &format); + + /** + * \param format the 8D format string. + * + * \brief Sets the 8D format string for the C-style sprintf() + * function. + */ + void Set8dFormat (const std::string &format); + + /** + * \param format the 9D format string. + * + * \brief Sets the 9D format string for the C-style sprintf() + * function. + */ + void Set9dFormat (const std::string &format); + + /** + * \param format the 10D format string. + * + * \brief Sets the 10D format string for the C-style sprintf() + * function. + */ + void Set10dFormat (const std::string &format); + +private: + /** + * \param typeId the type ID for the probe used when it is created. + * \param matchIdentifier this string is used to make the probe's + * context be unique. + * \param path Config path to access the probe. + * \param probeTraceSource the probe trace source to access. + * \param outputFileNameWithoutExtension name of output file to + * write with no extension + * \param onlyOneAggregator indicates if more than one aggregator + * should be created or not. + * + * \brief Connects the probe to the aggregator. + * + * A fatal error will result if an unknown probe type is used. + */ + void ConnectProbeToAggregator (const std::string &typeId, + const std::string &matchIdentifier, + const std::string &path, + const std::string &probeTraceSource, + const std::string &outputFileNameWithoutExtension, + bool onlyOneAggregator); + + /// Used to create the probes and collectors as they are added. + ObjectFactory m_factory; + + /// The single aggregator that is always created in the constructor. + Ptr m_aggregator; + + /// Maps aggregator names to aggregators when multiple aggregators + /// are needed. + std::map > m_aggregatorMap; + + /// Maps probe names to probes. + std::map, std::string> > m_probeMap; + + /// Maps time series adaptor names to time series adaptors. + std::map > m_timeSeriesAdaptorMap; + + /// Number of file probes that have been created. + uint32_t m_fileProbeCount; + + /// Determines the kind of file written by the aggregator. + enum FileAggregator::FileType m_fileType; + + /// The name of the output file to created without its extension. + std::string m_outputFileNameWithoutExtension; + + /// Indicates if the heading line for the file has been set. + bool m_hasHeadingBeenSet; + + /// Heading line for the outputfile. + std::string m_heading; + + /// Format strings for C-style sprintf() function. + std::string m_1dFormat; + std::string m_2dFormat; + std::string m_3dFormat; + std::string m_4dFormat; + std::string m_5dFormat; + std::string m_6dFormat; + std::string m_7dFormat; + std::string m_8dFormat; + std::string m_9dFormat; + std::string m_10dFormat; + +}; // class FileHelper + + +} // namespace ns3 + +#endif // FILE_HELPER_H diff --git a/src/stats/helper/gnuplot-helper.cc b/src/stats/helper/gnuplot-helper.cc new file mode 100644 index 000000000..9e20bd4ab --- /dev/null +++ b/src/stats/helper/gnuplot-helper.cc @@ -0,0 +1,413 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2013 University of Washington + * + * 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: Mitch Watrous (watrous@u.washington.edu) + */ + +#include +#include +#include +#include + +#include "gnuplot-helper.h" +#include "ns3/abort.h" +#include "ns3/assert.h" +#include "ns3/config.h" +#include "ns3/log.h" +#include "ns3/get-wildcard-matches.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("GnuplotHelper"); + +GnuplotHelper::GnuplotHelper () + : m_aggregator (0), + m_plotProbeCount (0), + m_outputFileNameWithoutExtension ("gnuplot-helper"), + m_title ("Gnuplot Helper Plot"), + m_xLegend ("X Values"), + m_yLegend ("Y Values"), + m_terminalType ("png") +{ + NS_LOG_FUNCTION (this); + + // Note that this does not construct an aggregator. It will be + // constructed later when needed. +} + +GnuplotHelper::GnuplotHelper (const std::string &outputFileNameWithoutExtension, + const std::string &title, + const std::string &xLegend, + const std::string &yLegend, + const std::string &terminalType) + : m_aggregator (0), + m_plotProbeCount (0), + m_outputFileNameWithoutExtension (outputFileNameWithoutExtension), + m_title (title), + m_xLegend (xLegend), + m_yLegend (yLegend), + m_terminalType (terminalType) +{ + NS_LOG_FUNCTION (this); + + // Construct the aggregator. + ConstructAggregator (); +} + +GnuplotHelper::~GnuplotHelper () +{ + NS_LOG_FUNCTION (this); +} + +void +GnuplotHelper::ConfigurePlot (const std::string &outputFileNameWithoutExtension, + const std::string &title, + const std::string &xLegend, + const std::string &yLegend, + const std::string &terminalType) +{ + NS_LOG_FUNCTION (this << outputFileNameWithoutExtension << title + << xLegend << yLegend << terminalType); + + // See if an aggregator has already been constructed. + if (m_aggregator != 0) + { + NS_LOG_WARN ("An existing aggregator object " << m_aggregator << + " may be destroyed if no references remain."); + } + + // Store these so that they can be used to construct the aggregator. + m_outputFileNameWithoutExtension = outputFileNameWithoutExtension; + m_title = title; + m_xLegend = xLegend; + m_yLegend = yLegend; + m_terminalType = terminalType; + + // Construct the aggregator. + ConstructAggregator (); +} + +void +GnuplotHelper::PlotProbe (const std::string &typeId, + const std::string &path, + const std::string &probeTraceSource, + const std::string &title, + enum GnuplotAggregator::KeyLocation keyLocation) +{ + NS_LOG_FUNCTION (this << typeId << path << probeTraceSource << title << keyLocation); + + // Get a pointer to the aggregator. + Ptr aggregator = GetAggregator (); + + // Add a subtitle to the title to show the probe's path. + aggregator->SetTitle ( m_title + " \\n\\nProbe Path: " + path); + + // Set the default dataset plotting style for the values. + aggregator->Set2dDatasetDefaultStyle (Gnuplot2dDataset::LINES_POINTS); + + // Set the location of the key in the plot. + aggregator->SetKeyLocation (keyLocation); + + std::string pathWithoutLastToken; + std::string lastToken; + + // See if the path has any wildcards. + bool pathHasNoWildcards = path.find ("*") == std::string::npos; + + // Remove the last token from the path. + unsigned lastSlash = path.find_last_of ("/"); + if (lastSlash == std::string::npos) + { + pathWithoutLastToken = path; + lastToken = ""; + } + else + { + // Chop off up to last token. + pathWithoutLastToken = path.substr (0, lastSlash); + + // Save the last token without the last slash. + lastToken = path.substr (lastSlash + 1, std::string::npos); + } + + // See if there are any matches for the probe's path with the last + // token removed. + Config::MatchContainer matches = Config::LookupMatches (pathWithoutLastToken); + uint32_t matchCount = matches.GetN (); + + // This is used to make the probe's context be unique. + std::string matchIdentifier; + + // Hook one or more probes and the aggregator together. + if (matchCount == 1 && pathHasNoWildcards) + { + // Connect the probe to the aggregator only once because there + // is only one matching config path. There is no need to find + // the wildcard matches because the passed in path has none. + matchIdentifier = "0"; + ConnectProbeToAggregator (typeId, + matchIdentifier, + path, + probeTraceSource, + title); + } + else if (matchCount > 0) + { + // Handle all of the matches if there are more than one. + for (uint32_t i = 0; i < matchCount; i++) + { + // Set the match identifier. + std::ostringstream matchIdentifierStream; + matchIdentifierStream << i; + matchIdentifier = matchIdentifierStream.str (); + + // Construct the matched path and get the matches for each + // of the wildcards. + std::string wildcardSeparator = " "; + std::string matchedPath = matches.GetMatchedPath (i) + lastToken; + std::string wildcardMatches = GetWildcardMatches (path, + matchedPath, + wildcardSeparator); + + // Connect the probe to the aggregator for this match. + ConnectProbeToAggregator (typeId, + matchIdentifier, + matchedPath, + probeTraceSource, + title + "-" + wildcardMatches); + } + } + else + { + // There is a problem if there are no matching config paths. + NS_FATAL_ERROR ("Lookup of " << path << " got no matches"); + } +} + +void +GnuplotHelper::AddProbe (const std::string &typeId, + const std::string &probeName, + const std::string &path) +{ + NS_LOG_FUNCTION (this << typeId << probeName << path); + + // See if this probe had already been added. + if (m_probeMap.count (probeName) > 0) + { + NS_ABORT_MSG ("That probe has already been added"); + } + + // Prepare the factory to create an object with the requested type. + m_factory.SetTypeId (typeId); + + // Create a base class object in order to validate the type. + Ptr probe = m_factory.Create ()->GetObject (); + if (probe == 0) + { + NS_ABORT_MSG ("The requested type is not a probe"); + } + + // Set the probe's name. + probe->SetName (probeName); + + // Set the path. Note that no return value is checked here. + probe->ConnectByPath (path); + + // Enable logging of data for the probe. + probe->Enable (); + + // Add this probe to the map so that its values can be used. + m_probeMap[probeName] = std::make_pair (probe, typeId); +} + +void +GnuplotHelper::AddTimeSeriesAdaptor (const std::string &adaptorName) +{ + NS_LOG_FUNCTION (this << adaptorName); + + // See if this time series adaptor had already been added. + if (m_timeSeriesAdaptorMap.count (adaptorName) > 0) + { + NS_ABORT_MSG ("That time series adaptor has already been added"); + } + + // Create the time series adaptor. + Ptr timeSeriesAdaptor = CreateObject (); + + // Enable logging of data for the time series adaptor. + timeSeriesAdaptor->Enable (); + + // Add this time series adaptor to the map so that can be used. + m_timeSeriesAdaptorMap[adaptorName] = timeSeriesAdaptor; +} + +Ptr +GnuplotHelper::GetProbe (std::string probeName) const +{ + // Look for the probe. + std::map, std::string> >::const_iterator mapIterator = m_probeMap.find (probeName); + + // Return the probe if it has been added. + if (mapIterator != m_probeMap.end ()) + { + return mapIterator->second.first; + } + else + { + NS_ABORT_MSG ("That probe has not been added"); + } +} + +Ptr +GnuplotHelper::GetAggregator () +{ + NS_LOG_FUNCTION (this); + + // Do a lazy construction of the aggregator if it hasn't already + // been constructed. + if (!m_aggregator) + { + ConstructAggregator (); + } + return m_aggregator; +} + +void +GnuplotHelper::ConstructAggregator () +{ + NS_LOG_FUNCTION (this); + + // Create the aggregator. + m_aggregator = CreateObject (m_outputFileNameWithoutExtension); + + // Set the aggregator's properties. + m_aggregator->SetTerminal (m_terminalType); + m_aggregator->SetTitle (m_title); + m_aggregator->SetLegend (m_xLegend, m_yLegend); + + // Enable logging of data for the aggregator. + m_aggregator->Enable (); +} + +void +GnuplotHelper::ConnectProbeToAggregator (const std::string &typeId, + const std::string &matchIdentifier, + const std::string &path, + const std::string &probeTraceSource, + const std::string &title) +{ + NS_LOG_FUNCTION (this << typeId << matchIdentifier << path << probeTraceSource + << title); + + Ptr aggregator = GetAggregator (); + + // Increment the total number of plot probes that have been created. + m_plotProbeCount++; + + // Create a unique name for this probe. + std::ostringstream probeNameStream; + probeNameStream << "PlotProbe-" << m_plotProbeCount; + std::string probeName = probeNameStream.str (); + + // Create a unique dataset context string for this probe. + std::string probeContext = probeName + + "/" + matchIdentifier + "/" + probeTraceSource; + + // Add the probe to the map of probes, which will keep the probe in + // memory after this function ends. + AddProbe (typeId, probeName, path); + + // Because the callbacks to the probes' trace sources don't use the + // probe's context, a unique adaptor needs to be created for each + // probe context so that information is not lost. + AddTimeSeriesAdaptor (probeContext); + + // Connect the probe to the adaptor. + if (m_probeMap[probeName].second == "ns3::DoubleProbe") + { + m_probeMap[probeName].first->TraceConnectWithoutContext + (probeTraceSource, + MakeCallback (&TimeSeriesAdaptor::TraceSinkDouble, + m_timeSeriesAdaptorMap[probeContext])); + } + else if (m_probeMap[probeName].second == "ns3::BooleanProbe") + { + m_probeMap[probeName].first->TraceConnectWithoutContext + (probeTraceSource, + MakeCallback (&TimeSeriesAdaptor::TraceSinkBoolean, + m_timeSeriesAdaptorMap[probeContext])); + } + else if (m_probeMap[probeName].second == "ns3::PacketProbe") + { + m_probeMap[probeName].first->TraceConnectWithoutContext + (probeTraceSource, + MakeCallback (&TimeSeriesAdaptor::TraceSinkUinteger32, + m_timeSeriesAdaptorMap[probeContext])); + } + else if (m_probeMap[probeName].second == "ns3::ApplicationPacketProbe") + { + m_probeMap[probeName].first->TraceConnectWithoutContext + (probeTraceSource, + MakeCallback (&TimeSeriesAdaptor::TraceSinkUinteger32, + m_timeSeriesAdaptorMap[probeContext])); + } + else if (m_probeMap[probeName].second == "ns3::Ipv4PacketProbe") + { + m_probeMap[probeName].first->TraceConnectWithoutContext + (probeTraceSource, + MakeCallback (&TimeSeriesAdaptor::TraceSinkUinteger32, + m_timeSeriesAdaptorMap[probeContext])); + } + else if (m_probeMap[probeName].second == "ns3::Uinteger8Probe") + { + m_probeMap[probeName].first->TraceConnectWithoutContext + (probeTraceSource, + MakeCallback (&TimeSeriesAdaptor::TraceSinkUinteger8, + m_timeSeriesAdaptorMap[probeContext])); + } + else if (m_probeMap[probeName].second == "ns3::Uinteger16Probe") + { + m_probeMap[probeName].first->TraceConnectWithoutContext + (probeTraceSource, + MakeCallback (&TimeSeriesAdaptor::TraceSinkUinteger16, + m_timeSeriesAdaptorMap[probeContext])); + } + else if (m_probeMap[probeName].second == "ns3::Uinteger32Probe") + { + m_probeMap[probeName].first->TraceConnectWithoutContext + (probeTraceSource, + MakeCallback (&TimeSeriesAdaptor::TraceSinkUinteger32, + m_timeSeriesAdaptorMap[probeContext])); + } + else + { + NS_FATAL_ERROR ("Unknown probe type " << m_probeMap[probeName].second << "; need to add support in the helper for this"); + } + + // Connect the adaptor to the aggregator. + std::string adaptorTraceSource = "Output"; + m_timeSeriesAdaptorMap[probeContext]->TraceConnect + (adaptorTraceSource, + probeContext, + MakeCallback (&GnuplotAggregator::Write2d, aggregator)); + + // Add the dataset to the plot. + aggregator->Add2dDataset (probeContext, title); +} + +} // namespace ns3 + diff --git a/src/stats/helper/gnuplot-helper.h b/src/stats/helper/gnuplot-helper.h new file mode 100644 index 000000000..c0a826bbd --- /dev/null +++ b/src/stats/helper/gnuplot-helper.h @@ -0,0 +1,212 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2013 University of Washington + * + * 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: Mitch Watrous (watrous@u.washington.edu) + */ + +#ifndef GNUPLOT_HELPER_H +#define GNUPLOT_HELPER_H + +#include +#include +#include +#include "ns3/object-factory.h" +#include "ns3/ptr.h" +#include "ns3/probe.h" +#include "ns3/gnuplot-aggregator.h" +#include "ns3/time-series-adaptor.h" + +namespace ns3 { + +/** + * \brief Helper class used to make gnuplot plots. + **/ +class GnuplotHelper +{ +public: + /** + * Constructs a gnuplot helper that will create a space separated + * gnuplot data file named "gnuplot-helper.dat", a gnuplot control + * file named "gnuplot-helper.plt", and a shell script to generate + * the gnuplot named "gnuplot-helper.sh" unless it is later + * configured otherwise. + */ + GnuplotHelper (); + + /** + * \param outputFileNameWithoutExtension name of gnuplot related files to + * write with no extension + * \param title plot title string to use for this plot. + * \param xLegend the legend for the x horizontal axis. + * \param yLegend the legend for the y vertical axis. + * \param terminalType terminal type setting string for output. The + * default terminal type is "png" + * + * Constructs a gnuplot helper that will create a space separated + * gnuplot data file named outputFileNameWithoutExtension + ".dat", + * a gnuplot control file named outputFileNameWithoutExtension + + * ".plt", and a shell script to generate the gnuplot named + * outputFileNameWithoutExtension + ".sh". + */ + GnuplotHelper (const std::string &outputFileNameWithoutExtension, + const std::string &title, + const std::string &xLegend, + const std::string &yLegend, + const std::string &terminalType = "png"); + + virtual ~GnuplotHelper (); + + /** + * \param outputFileNameWithoutExtension name of gnuplot related files to + * write with no extension + * \param title plot title string to use for this plot. + * \param xLegend the legend for the x horizontal axis. + * \param yLegend the legend for the y vertical axis. + * \param terminalType terminal type setting string for output. The + * default terminal type is "png" + * + * Configures plot related parameters for this gnuplot helper so + * that it will create a space separated gnuplot data file named + * outputFileNameWithoutExtension + ".dat", a gnuplot control file + * named outputFileNameWithoutExtension + ".plt", and a shell script + * to generate the gnuplot named outputFileNameWithoutExtension + + * ".sh". + */ + void ConfigurePlot (const std::string &outputFileNameWithoutExtension, + const std::string &title, + const std::string &xLegend, + const std::string &yLegend, + const std::string &terminalType = ".png"); + + /** + * \param typeId the type ID for the probe used when it is created. + * \param path Config path to access the probe. + * \param probeTraceSource the probe trace source to access. + * \param title the title to be associated to this dataset + * \param keyLocation the location of the key in the plot. + * + * Plots a dataset generated by hooking the ns-3 trace source with a + * probe, and then plot the values from the probeTraceSource. The dataset + * will have the provided title, and will consist of the 'newValue' + * at each timestamp. + * + * If the config path has more than one match in the system + * (e.g. there is a wildcard), then one dataset for each match will + * be plotted. The dataset titles will be suffixed with the matched + * characters for each of the wildcards in the config path, + * separated by spaces. For example, if the proposed dataset title + * is the string "bytes", and there are two wildcards in the path, + * then dataset titles like "bytes-0 0" or "bytes-12 9" will be + * possible as labels for the datasets that are plotted. + */ + void PlotProbe (const std::string &typeId, + const std::string &path, + const std::string &probeTraceSource, + const std::string &title, + enum GnuplotAggregator::KeyLocation keyLocation = GnuplotAggregator::KEY_INSIDE); + + /** + * \param typeId the type ID for the probe used when it is created. + * \param probeName the probe's name. + * \param path Config path to access the probe. + * + * \brief Adds a probe to be used to make the plot. + */ + void AddProbe (const std::string &typeId, + const std::string &probeName, + const std::string &path); + + /** + * \param adaptorName the timeSeriesAdaptor's name. + * + * \brief Adds a time series adaptor to be used to make the plot. + */ + void AddTimeSeriesAdaptor (const std::string &adaptorName); + + /** + * \param probeName the probe's name. + * + * \brief Gets the specified probe. + */ + Ptr GetProbe (std::string probeName) const; + + /** + * \brief Gets the aggregator. + * + * This function is non-const because an aggregator may be lazily + * created by this method. + */ + Ptr GetAggregator (); + +private: + /** + * \brief Constructs the aggregator. + */ + void ConstructAggregator (); + + /** + * \param typeId the type ID for the probe used when it is created. + * \param matchIdentifier this string is used to make the probe's + * context be unique. + * \param path Config path to access the probe. + * \param probeTraceSource the probe trace source to access. + * \param title the title to be associated to this dataset. + * + * \brief Connects the probe to the aggregator. + */ + void ConnectProbeToAggregator (const std::string &typeId, + const std::string &matchIdentifier, + const std::string &path, + const std::string &probeTraceSource, + const std::string &title); + + /// Used to create the probes and collectors as they are added. + ObjectFactory m_factory; + + /// The aggregator used to make the plots. + Ptr m_aggregator; + + /// Maps probe names to probes. + std::map, std::string> > m_probeMap; + + /// Maps time series adaptor names to time series adaptors. + std::map > m_timeSeriesAdaptorMap; + + /// Number of plot probes that have been created. + uint32_t m_plotProbeCount; + + /// The name of the output file to created without its extension. + std::string m_outputFileNameWithoutExtension; + + /// Title string to use for this plot. + std::string m_title; + + /// Legend for the x axis. + std::string m_xLegend; + + /// Legend for the y axis. + std::string m_yLegend; + + /// Terminal type for the plot. + std::string m_terminalType; + +}; // class GnuplotHelper + + +} // namespace ns3 + +#endif // GNUPLOT_HELPER_H diff --git a/src/stats/model/boolean-probe.cc b/src/stats/model/boolean-probe.cc new file mode 100644 index 000000000..d27b50271 --- /dev/null +++ b/src/stats/model/boolean-probe.cc @@ -0,0 +1,110 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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 + * + * Authors: L. Felipe Perrone (perrone@bucknell.edu) + * Tiago G. Rodrigues (tgr002@bucknell.edu) + * + * Modified by: Mitch Watrous (watrous@u.washington.edu) + */ + +#include "ns3/boolean-probe.h" +#include "ns3/object.h" +#include "ns3/log.h" +#include "ns3/names.h" +#include "ns3/config.h" +#include "ns3/trace-source-accessor.h" + +NS_LOG_COMPONENT_DEFINE ("BooleanProbe"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (BooleanProbe); + +TypeId +BooleanProbe::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::BooleanProbe") + .SetParent () + .AddConstructor () + .AddTraceSource ( "Output", + "The bool that serves as output for this probe", + MakeTraceSourceAccessor (&BooleanProbe::m_output)) + ; + return tid; +} + +BooleanProbe::BooleanProbe () +{ + NS_LOG_FUNCTION (this); + m_output = 0; +} + +BooleanProbe::~BooleanProbe () +{ + NS_LOG_FUNCTION (this); +} + +bool +BooleanProbe::GetValue (void) const +{ + NS_LOG_FUNCTION (this); + return m_output; +} +void +BooleanProbe::SetValue (bool newVal) +{ + NS_LOG_FUNCTION (this << newVal); + m_output = newVal; +} + +void +BooleanProbe::SetValueByPath (std::string path, bool newVal) +{ + NS_LOG_FUNCTION (path << newVal); + Ptr probe = Names::Find (path); + NS_ASSERT_MSG (probe, "Error: Can't find probe for path " << path); + probe->SetValue (newVal); +} + +bool +BooleanProbe::ConnectByObject (std::string traceSource, Ptr obj) +{ + NS_LOG_FUNCTION (this << traceSource << obj); + NS_LOG_DEBUG ("Name of probe (if any) in names database: " << Names::FindPath (obj)); + bool connected = obj->TraceConnectWithoutContext (traceSource, MakeCallback (&ns3::BooleanProbe::TraceSink, this)); + return connected; +} + +void +BooleanProbe::ConnectByPath (std::string path) +{ + NS_LOG_FUNCTION (this << path); + NS_LOG_DEBUG ("Name of probe to search for in config database: " << path); + Config::ConnectWithoutContext (path, MakeCallback (&ns3::BooleanProbe::TraceSink, this)); +} + +void +BooleanProbe::TraceSink (bool oldData, bool newData) +{ + NS_LOG_FUNCTION (this << oldData << newData); + if (IsEnabled ()) + { + m_output = newData; + } +} + +} // namespace ns3 diff --git a/src/stats/model/boolean-probe.h b/src/stats/model/boolean-probe.h new file mode 100644 index 000000000..96eaf2d98 --- /dev/null +++ b/src/stats/model/boolean-probe.h @@ -0,0 +1,105 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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 + * + * Authors: L. Felipe Perrone (perrone@bucknell.edu) + * Tiago G. Rodrigues (tgr002@bucknell.edu) + * + * Modified by: Mitch Watrous (watrous@u.washington.edu) + */ + +#ifndef BOOL_PROBE_H +#define BOOL_PROBE_H + +#include "ns3/probe.h" +#include "ns3/object.h" +#include "ns3/callback.h" +#include "ns3/boolean.h" +#include "ns3/traced-value.h" +#include "ns3/simulator.h" + +namespace ns3 { + +/** + * This class is designed to probe an underlying ns3 TraceSource exporting + * a bool. This probe exports a trace source "Output" of type bool. + * The Output trace source emits a value when either the trace source + * emits a new value, or when SetValue () is called. + * + * The current value of the probe can be polled with the GetValue () + * method. + */ +class BooleanProbe : public Probe +{ +public: + static TypeId GetTypeId (); + BooleanProbe (); + virtual ~BooleanProbe (); + + /** + * \return the most recent value + */ + bool GetValue (void) const; + + /** + * \param value set the traced bool to a new value + */ + void SetValue (bool value); + + /** + * \brief Set a probe value by its name in the Config system + * + * \param path Config path to access the probe + * \param value set the traced bool to a new value + */ + static void SetValueByPath (std::string path, bool value); + + /** + * \brief connect to a trace source attribute provided by a given object + * + * \param traceSource the name of the attribute TraceSource to connect to + * \param obj ns3::Object to connect to + * \return true if the trace source was successfully connected + */ + virtual bool ConnectByObject (std::string traceSource, Ptr obj); + + /** + * \brief connect to a trace source provided by a config path + * + * \param path Config path to bind to + * + * Note, if an invalid path is provided, the probe will not be connected + * to anything. + */ + virtual void ConnectByPath (std::string path); + +private: + /** + * \brief Method to connect to an underlying ns3::TraceSource of type bool + * + * \param oldData previous value of the bool + * \param newData new value of the bool + * + * \internal + */ + void TraceSink (bool oldData, bool newData); + + TracedValue m_output; +}; + +} // namespace ns3 + +#endif // BOOL_PROBE_H diff --git a/src/stats/model/data-collection-object.cc b/src/stats/model/data-collection-object.cc new file mode 100644 index 000000000..3c26492fc --- /dev/null +++ b/src/stats/model/data-collection-object.cc @@ -0,0 +1,96 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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 + * + * Authors: Tiago G. Rodrigues (tgr002@bucknell.edu) + */ + +#include "ns3/log.h" +#include "ns3/string.h" +#include "ns3/boolean.h" + +#include "data-collection-object.h" + +NS_LOG_COMPONENT_DEFINE ("DataCollectionObject"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (DataCollectionObject); + +TypeId +DataCollectionObject::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::DataCollectionObject") + .SetParent () + .AddConstructor () + .AddAttribute ( "Name", + "Object's name", + StringValue ("unnamed"), MakeStringAccessor (&DataCollectionObject::GetName, &DataCollectionObject::SetName), MakeStringChecker ()) + .AddAttribute ( "Enabled", + "Object's enabled status", + BooleanValue (true), MakeBooleanAccessor (&DataCollectionObject::m_enabled), MakeBooleanChecker ()) + ; + return tid; +} + +DataCollectionObject::DataCollectionObject () +{ +} + +DataCollectionObject::~DataCollectionObject () +{ + NS_LOG_FUNCTION (this); +} + +bool +DataCollectionObject::IsEnabled (void) const +{ + return m_enabled; +} + +std::string +DataCollectionObject::GetName (void) const +{ + return m_name; +} + +void +DataCollectionObject::SetName (std::string name) +{ + NS_LOG_FUNCTION (this << name); + for (size_t pos = name.find (" "); pos != std::string::npos; pos = name.find (" ", pos + 1, 1)) + { + name[pos] = '_'; + } + + m_name = name; +} + +void +DataCollectionObject::Enable (void) +{ + NS_LOG_FUNCTION (this); + m_enabled = true; +} + +void +DataCollectionObject::Disable (void) +{ + NS_LOG_FUNCTION (this); + m_enabled = false; +} + +} // namespace ns3 diff --git a/src/stats/model/data-collection-object.h b/src/stats/model/data-collection-object.h new file mode 100644 index 000000000..78b89def3 --- /dev/null +++ b/src/stats/model/data-collection-object.h @@ -0,0 +1,67 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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 + * + * Authors: Tiago G. Rodrigues (tgr002@bucknell.edu) + */ + +#ifndef DATA_COLLECTION_OBJECT_H +#define DATA_COLLECTION_OBJECT_H + +#include +#include "ns3/object.h" + +namespace ns3 { + +/** + * Base class for data collection framework objects. + * + * All data collection objects have 1) a string name, and 2) enabled + * or disabled status. + */ +class DataCollectionObject : public Object +{ +public: + static TypeId GetTypeId (); + + DataCollectionObject (); + virtual ~DataCollectionObject (); + + /// Set the status of an individual object. + void Enable (void); + void Disable (void); + + /// Check the status of an individual object. + virtual bool IsEnabled (void) const; + + /// Get the object's name. + std::string GetName (void) const; + + /// Set the object's name. All spaces are replaced by underscores. + void SetName (std::string name); + +protected: + /// Object's activation state. + bool m_enabled; + + /// Name of the object within the data collection framework + std::string m_name; + +}; + +} // namespace ns3 + +#endif // DATA_COLLECTION_OBJECT_H diff --git a/src/stats/model/double-probe.cc b/src/stats/model/double-probe.cc new file mode 100644 index 000000000..b3ff59191 --- /dev/null +++ b/src/stats/model/double-probe.cc @@ -0,0 +1,110 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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 + * + * Authors: L. Felipe Perrone (perrone@bucknell.edu) + * Tiago G. Rodrigues (tgr002@bucknell.edu) + * + * Modified by: Mitch Watrous (watrous@u.washington.edu) + */ + +#include "ns3/double-probe.h" +#include "ns3/object.h" +#include "ns3/log.h" +#include "ns3/names.h" +#include "ns3/config.h" +#include "ns3/trace-source-accessor.h" + +NS_LOG_COMPONENT_DEFINE ("DoubleProbe"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (DoubleProbe); + +TypeId +DoubleProbe::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::DoubleProbe") + .SetParent () + .AddConstructor () + .AddTraceSource ( "Output", + "The double that serves as output for this probe", + MakeTraceSourceAccessor (&DoubleProbe::m_output)) + ; + return tid; +} + +DoubleProbe::DoubleProbe () +{ + NS_LOG_FUNCTION (this); + m_output = 0; +} + +DoubleProbe::~DoubleProbe () +{ + NS_LOG_FUNCTION (this); +} + +double +DoubleProbe::GetValue (void) const +{ + NS_LOG_FUNCTION (this); + return m_output; +} +void +DoubleProbe::SetValue (double newVal) +{ + NS_LOG_FUNCTION (this << newVal); + m_output = newVal; +} + +void +DoubleProbe::SetValueByPath (std::string path, double newVal) +{ + NS_LOG_FUNCTION (path << newVal); + Ptr probe = Names::Find (path); + NS_ASSERT_MSG (probe, "Error: Can't find probe for path " << path); + probe->SetValue (newVal); +} + +bool +DoubleProbe::ConnectByObject (std::string traceSource, Ptr obj) +{ + NS_LOG_FUNCTION (this << traceSource << obj); + NS_LOG_DEBUG ("Name of probe (if any) in names database: " << Names::FindPath (obj)); + bool connected = obj->TraceConnectWithoutContext (traceSource, MakeCallback (&ns3::DoubleProbe::TraceSink, this)); + return connected; +} + +void +DoubleProbe::ConnectByPath (std::string path) +{ + NS_LOG_FUNCTION (this << path); + NS_LOG_DEBUG ("Name of probe to search for in config database: " << path); + Config::ConnectWithoutContext (path, MakeCallback (&ns3::DoubleProbe::TraceSink, this)); +} + +void +DoubleProbe::TraceSink (double oldData, double newData) +{ + NS_LOG_FUNCTION (this << oldData << newData); + if (IsEnabled ()) + { + m_output = newData; + } +} + +} // namespace ns3 diff --git a/src/stats/model/double-probe.h b/src/stats/model/double-probe.h new file mode 100644 index 000000000..dfc72f84f --- /dev/null +++ b/src/stats/model/double-probe.h @@ -0,0 +1,105 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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 + * + * Authors: L. Felipe Perrone (perrone@bucknell.edu) + * Tiago G. Rodrigues (tgr002@bucknell.edu) + * + * Modified by: Mitch Watrous (watrous@u.washington.edu) + */ + +#ifndef DOUBLE_PROBE_H +#define DOUBLE_PROBE_H + +#include "ns3/probe.h" +#include "ns3/object.h" +#include "ns3/callback.h" +#include "ns3/boolean.h" +#include "ns3/traced-value.h" +#include "ns3/simulator.h" + +namespace ns3 { + +/** + * This class is designed to probe an underlying ns3 TraceSource exporting + * a double. This probe exports a trace source "Output" of type double. + * The Output trace source emits a value when either the trace source + * emits a new value, or when SetValue () is called. + * + * The current value of the probe can be polled with the GetValue () + * method. + */ +class DoubleProbe : public Probe +{ +public: + static TypeId GetTypeId (); + DoubleProbe (); + virtual ~DoubleProbe (); + + /** + * \return the most recent value + */ + double GetValue (void) const; + + /** + * \param value set the traced double to a new value + */ + void SetValue (double value); + + /** + * \brief Set a probe value by its name in the Config system + * + * \param path Config path to access the probe + * \param value set the traced double to a new value + */ + static void SetValueByPath (std::string path, double value); + + /** + * \brief connect to a trace source attribute provided by a given object + * + * \param traceSource the name of the attribute TraceSource to connect to + * \param obj ns3::Object to connect to + * \return true if the trace source was successfully connected + */ + virtual bool ConnectByObject (std::string traceSource, Ptr obj); + + /** + * \brief connect to a trace source provided by a config path + * + * \param path Config path to bind to + * + * Note, if an invalid path is provided, the probe will not be connected + * to anything. + */ + virtual void ConnectByPath (std::string path); + +private: + /** + * \brief Method to connect to an underlying ns3::TraceSource of type double + * + * \param oldData previous value of the double + * \param newData new value of the double + * + * \internal + */ + void TraceSink (double oldData, double newData); + + TracedValue m_output; +}; + +} // namespace ns3 + +#endif // DOUBLE_PROBE_H diff --git a/src/stats/model/file-aggregator.cc b/src/stats/model/file-aggregator.cc new file mode 100644 index 000000000..0d51fbe33 --- /dev/null +++ b/src/stats/model/file-aggregator.cc @@ -0,0 +1,661 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2013 University of Washington + * + * 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: Mitch Watrous (watrous@u.washington.edu) + */ + +#include +#include +#include + +#include "file-aggregator.h" +#include "ns3/abort.h" +#include "ns3/log.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("FileAggregator"); + +NS_OBJECT_ENSURE_REGISTERED (FileAggregator); + +TypeId +FileAggregator::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::FileAggregator") + .SetParent () + ; + + return tid; +} + +FileAggregator::FileAggregator (const std::string &outputFileName, + enum FileType fileType) + : m_outputFileName (outputFileName), + m_fileType (fileType), + m_hasHeadingBeenSet (false), + m_1dFormat ("%e"), + m_2dFormat ("%e %e"), + m_3dFormat ("%e %e %e"), + m_4dFormat ("%e %e %e %e"), + m_5dFormat ("%e %e %e %e %e"), + m_6dFormat ("%e %e %e %e %e %e"), + m_7dFormat ("%e %e %e %e %e %e %e"), + m_8dFormat ("%e %e %e %e %e %e %e %e"), + m_9dFormat ("%e %e %e %e %e %e %e %e %e"), + m_10dFormat ("%e %e %e %e %e %e %e %e %e %e") +{ + // Set the values separator. + switch (m_fileType) + { + case COMMA_SEPARATED: + m_separator = ","; + break; + case TAB_SEPARATED: + m_separator = "\t"; + break; + default: + // Space separated. + m_separator = " "; + break; + } + + // Open the output file. + m_file.open (m_outputFileName.c_str ()); +} + +FileAggregator::~FileAggregator () +{ + // Close the output file. + m_file.close (); +} + +void +FileAggregator::SetFileType (enum FileType fileType) +{ + m_fileType = fileType; +} + +void +FileAggregator::SetHeading (const std::string &heading) +{ + if (!m_hasHeadingBeenSet) + { + m_heading = heading; + m_hasHeadingBeenSet = true; + + // Print the heading to the file. + m_file << m_heading << std::endl; + } +} + +void +FileAggregator::Set1dFormat (const std::string &format) +{ + m_1dFormat = format; +} + +void +FileAggregator::Set2dFormat (const std::string &format) +{ + m_2dFormat = format; +} + +void +FileAggregator::Set3dFormat (const std::string &format) +{ + m_3dFormat = format; +} + +void +FileAggregator::Set4dFormat (const std::string &format) +{ + m_4dFormat = format; +} + +void +FileAggregator::Set5dFormat (const std::string &format) +{ + m_5dFormat = format; +} + +void +FileAggregator::Set6dFormat (const std::string &format) +{ + m_6dFormat = format; +} + +void +FileAggregator::Set7dFormat (const std::string &format) +{ + m_7dFormat = format; +} + +void +FileAggregator::Set8dFormat (const std::string &format) +{ + m_8dFormat = format; +} + +void +FileAggregator::Set9dFormat (const std::string &format) +{ + m_9dFormat = format; +} + +void +FileAggregator::Set10dFormat (const std::string &format) +{ + m_10dFormat = format; +} + +void +FileAggregator::Write1d (std::string context, + double v1) +{ + if (m_enabled) + { + // Write the 1D data point to the file. + if (m_fileType == FORMATTED) + { + // Initially, have the C-style string in the buffer, which + // is terminated by a null character, be of length zero. + char buffer[500]; + int maxBufferSize = 500; + buffer[0] = 0; + + // Format the value. + int charWritten = snprintf (buffer, + maxBufferSize, + m_1dFormat.c_str (), + v1); + if (charWritten < 0) + { + NS_LOG_DEBUG ("Error writing value to output file"); + } + + // Write the formatted value. + m_file << buffer << std::endl; + } + else + { + // Write the value. + m_file << v1 << std::endl; + } + } +} + +void +FileAggregator::Write2d (std::string context, + double v1, + double v2) +{ + if (m_enabled) + { + // Write the 2D data point to the file. + if (m_fileType == FORMATTED) + { + // Initially, have the C-style string in the buffer, which + // is terminated by a null character, be of length zero. + char buffer[500]; + int maxBufferSize = 500; + buffer[0] = 0; + + // Format the values. + int charWritten = snprintf (buffer, + maxBufferSize, + m_2dFormat.c_str (), + v1, + v2); + if (charWritten < 0) + { + NS_LOG_DEBUG ("Error writing values to output file"); + } + + // Write the formatted values. + m_file << buffer << std::endl; + } + else + { + // Write the values with the proper separator. + m_file << v1 << m_separator + << v2 << std::endl; + } + } +} + +void +FileAggregator::Write3d (std::string context, + double v1, + double v2, + double v3) +{ + if (m_enabled) + { + // Write the 3D data point to the file. + if (m_fileType == FORMATTED) + { + // Initially, have the C-style string in the buffer, which + // is terminated by a null character, be of length zero. + char buffer[500]; + int maxBufferSize = 500; + buffer[0] = 0; + + // Format the values. + int charWritten = snprintf (buffer, + maxBufferSize, + m_3dFormat.c_str (), + v1, + v2, + v3); + if (charWritten < 0) + { + NS_LOG_DEBUG ("Error writing values to output file"); + } + + // Write the formatted values. + m_file << buffer << std::endl; + } + else + { + // Write the values with the proper separator. + m_file << v1 << m_separator + << v2 << m_separator + << v3 << std::endl; + } + } +} + +void +FileAggregator::Write4d (std::string context, + double v1, + double v2, + double v3, + double v4) +{ + if (m_enabled) + { + // Write the 4D data point to the file. + if (m_fileType == FORMATTED) + { + // Initially, have the C-style string in the buffer, which + // is terminated by a null character, be of length zero. + char buffer[500]; + int maxBufferSize = 500; + buffer[0] = 0; + + // Format the values. + int charWritten = snprintf (buffer, + maxBufferSize, + m_4dFormat.c_str (), + v1, + v2, + v3, + v4); + if (charWritten < 0) + { + NS_LOG_DEBUG ("Error writing values to output file"); + } + + // Write the formatted values. + m_file << buffer << std::endl; + } + else + { + // Write the values with the proper separator. + m_file << v1 << m_separator + << v2 << m_separator + << v3 << m_separator + << v4 << std::endl; + } + } +} + +void +FileAggregator::Write5d (std::string context, + double v1, + double v2, + double v3, + double v4, + double v5) +{ + if (m_enabled) + { + // Write the 5D data point to the file. + if (m_fileType == FORMATTED) + { + // Initially, have the C-style string in the buffer, which + // is terminated by a null character, be of length zero. + char buffer[500]; + int maxBufferSize = 500; + buffer[0] = 0; + + // Format the values. + int charWritten = snprintf (buffer, + maxBufferSize, + m_5dFormat.c_str (), + v1, + v2, + v3, + v4, + v5); + if (charWritten < 0) + { + NS_LOG_DEBUG ("Error writing values to output file"); + } + + // Write the formatted values. + m_file << buffer << std::endl; + } + else + { + // Write the values with the proper separator. + m_file << v1 << m_separator + << v2 << m_separator + << v3 << m_separator + << v4 << m_separator + << v5 << std::endl; + } + } +} + +void +FileAggregator::Write6d (std::string context, + double v1, + double v2, + double v3, + double v4, + double v5, + double v6) +{ + if (m_enabled) + { + // Write the 6D data point to the file. + if (m_fileType == FORMATTED) + { + // Initially, have the C-style string in the buffer, which + // is terminated by a null character, be of length zero. + char buffer[500]; + int maxBufferSize = 500; + buffer[0] = 0; + + // Format the values. + int charWritten = snprintf (buffer, + maxBufferSize, + m_6dFormat.c_str (), + v1, + v2, + v3, + v4, + v5, + v6); + if (charWritten < 0) + { + NS_LOG_DEBUG ("Error writing values to output file"); + } + + // Write the formatted values. + m_file << buffer << std::endl; + } + else + { + // Write the values with the proper separator. + m_file << v1 << m_separator + << v2 << m_separator + << v3 << m_separator + << v4 << m_separator + << v5 << m_separator + << v6 << std::endl; + } + } +} + +void +FileAggregator::Write7d (std::string context, + double v1, + double v2, + double v3, + double v4, + double v5, + double v6, + double v7) +{ + if (m_enabled) + { + // Write the 7D data point to the file. + if (m_fileType == FORMATTED) + { + // Initially, have the C-style string in the buffer, which + // is terminated by a null character, be of length zero. + char buffer[500]; + int maxBufferSize = 500; + buffer[0] = 0; + + // Format the values. + int charWritten = snprintf (buffer, + maxBufferSize, + m_7dFormat.c_str (), + v1, + v2, + v3, + v4, + v5, + v6, + v7); + if (charWritten < 0) + { + NS_LOG_DEBUG ("Error writing values to output file"); + } + + // Write the formatted values. + m_file << buffer << std::endl; + } + else + { + // Write the values with the proper separator. + m_file << v1 << m_separator + << v2 << m_separator + << v3 << m_separator + << v4 << m_separator + << v5 << m_separator + << v6 << m_separator + << v7 << std::endl; + } + } +} + +void +FileAggregator::Write8d (std::string context, + double v1, + double v2, + double v3, + double v4, + double v5, + double v6, + double v7, + double v8) +{ + if (m_enabled) + { + // Write the 8D data point to the file. + if (m_fileType == FORMATTED) + { + // Initially, have the C-style string in the buffer, which + // is terminated by a null character, be of length zero. + char buffer[500]; + int maxBufferSize = 500; + buffer[0] = 0; + + // Format the values. + int charWritten = snprintf (buffer, + maxBufferSize, + m_8dFormat.c_str (), + v1, + v2, + v3, + v4, + v5, + v6, + v7, + v8); + if (charWritten < 0) + { + NS_LOG_DEBUG ("Error writing values to output file"); + } + + // Write the formatted values. + m_file << buffer << std::endl; + } + else + { + // Write the values with the proper separator. + m_file << v1 << m_separator + << v2 << m_separator + << v3 << m_separator + << v4 << m_separator + << v5 << m_separator + << v6 << m_separator + << v7 << m_separator + << v8 << std::endl; + } + } +} + +void +FileAggregator::Write9d (std::string context, + double v1, + double v2, + double v3, + double v4, + double v5, + double v6, + double v7, + double v8, + double v9) +{ + if (m_enabled) + { + // Write the 9D data point to the file. + if (m_fileType == FORMATTED) + { + // Initially, have the C-style string in the buffer, which + // is terminated by a null character, be of length zero. + char buffer[500]; + int maxBufferSize = 500; + buffer[0] = 0; + + // Format the values. + int charWritten = snprintf (buffer, + maxBufferSize, + m_9dFormat.c_str (), + v1, + v2, + v3, + v4, + v5, + v6, + v7, + v8, + v9); + if (charWritten < 0) + { + NS_LOG_DEBUG ("Error writing values to output file"); + } + + // Write the formatted values. + m_file << buffer << std::endl; + } + else + { + // Write the values with the proper separator. + m_file << v1 << m_separator + << v2 << m_separator + << v3 << m_separator + << v4 << m_separator + << v5 << m_separator + << v6 << m_separator + << v7 << m_separator + << v8 << m_separator + << v9 << std::endl; + } + } +} + +void +FileAggregator::Write10d (std::string context, + double v1, + double v2, + double v3, + double v4, + double v5, + double v6, + double v7, + double v8, + double v9, + double v10) +{ + if (m_enabled) + { + // Write the 10D data point to the file. + if (m_fileType == FORMATTED) + { + // Initially, have the C-style string in the buffer, which + // is terminated by a null character, be of length zero. + char buffer[500]; + int maxBufferSize = 500; + buffer[0] = 0; + + // Format the values. + int charWritten = snprintf (buffer, + maxBufferSize, + m_10dFormat.c_str (), + v1, + v2, + v3, + v4, + v5, + v6, + v7, + v8, + v9, + v10); + if (charWritten < 0) + { + NS_LOG_DEBUG ("Error writing values to output file"); + } + + // Write the formatted values. + m_file << buffer << std::endl; + } + else + { + // Write the values with the proper separator. + m_file << v1 << m_separator + << v2 << m_separator + << v3 << m_separator + << v4 << m_separator + << v5 << m_separator + << v6 << m_separator + << v7 << m_separator + << v8 << m_separator + << v9 << m_separator + << v10 << std::endl; + } + } +} + +} // namespace ns3 + diff --git a/src/stats/model/file-aggregator.h b/src/stats/model/file-aggregator.h new file mode 100644 index 000000000..fb4de391f --- /dev/null +++ b/src/stats/model/file-aggregator.h @@ -0,0 +1,384 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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: L. Felipe Perrone (perrone@bucknell.edu) + * + * Modified by: Mitch Watrous (watrous@u.washington.edu) + * + */ + +#ifndef FILE_AGGREGATOR_H +#define FILE_AGGREGATOR_H + +#include +#include +#include +#include "ns3/data-collection-object.h" + +namespace ns3 { + +/** + * This aggregator sends values it receives to a file. + **/ +class FileAggregator : public DataCollectionObject +{ +public: + /// The type of file written by the aggregator. + enum FileType + { + FORMATTED, + SPACE_SEPARATED, + COMMA_SEPARATED, + TAB_SEPARATED + }; + + static TypeId GetTypeId (); + + /** + * \param outputFileName name of the file to write. + * \param fileType type of file to write. + * + * Constructs a file aggregator that will create a file named + * outputFileName with values printed as specified by fileType. The + * default file type is space-separated. + */ + FileAggregator (const std::string &outputFileName, + enum FileType fileType = SPACE_SEPARATED); + + virtual ~FileAggregator (); + + /** + * \param fileType file type specifies the separator to use in + * printing the file. + * + * \brief Set the file type to create, which determines the + * separator to use when printing values to the file. + */ + void SetFileType (enum FileType fileType); + + /** + * \param heading the heading string. + * + * \brief Sets the heading string that will be printed on the first + * line of the file. + * + * Note that the heading string will only be printed if it has been + * set by calling this function. + */ + void SetHeading (const std::string &heading); + + /** + * \param format the 1D format string. + * + * \brief Sets the 1D format string for the C-style sprintf() + * function. + */ + void Set1dFormat (const std::string &format); + + /** + * \param format the 2D format string. + * + * \brief Sets the 2D format string for the C-style sprintf() + * function. + */ + void Set2dFormat (const std::string &format); + + /** + * \param format the 3D format string. + * + * \brief Sets the 3D format string for the C-style sprintf() + * function. + */ + void Set3dFormat (const std::string &format); + + /** + * \param format the 4D format string. + * + * \brief Sets the 4D format string for the C-style sprintf() + * function. + */ + void Set4dFormat (const std::string &format); + + /** + * \param format the 5D format string. + * + * \brief Sets the 5D format string for the C-style sprintf() + * function. + */ + void Set5dFormat (const std::string &format); + + /** + * \param format the 6D format string. + * + * \brief Sets the 6D format string for the C-style sprintf() + * function. + */ + void Set6dFormat (const std::string &format); + + /** + * \param format the 7D format string. + * + * \brief Sets the 7D format string for the C-style sprintf() + * function. + */ + void Set7dFormat (const std::string &format); + + /** + * \param format the 8D format string. + * + * \brief Sets the 8D format string for the C-style sprintf() + * function. + */ + void Set8dFormat (const std::string &format); + + /** + * \param format the 9D format string. + * + * \brief Sets the 9D format string for the C-style sprintf() + * function. + */ + void Set9dFormat (const std::string &format); + + /** + * \param format the 10D format string. + * + * \brief Sets the 10D format string for the C-style sprintf() + * function. + */ + void Set10dFormat (const std::string &format); + + // Below are hooked to connectors exporting data + // They are not overloaded since it confuses the compiler when made + // into callbacks + + /** + * \param context specifies the 1D dataset these values came from. + * \param v1 value for the new data point. + * + * \brief Writes 1 value to the file. + */ + void Write1d (std::string context, + double v1); + + /** + * \param context specifies the 2D dataset these values came from. + * \param v1 first value for the new data point. + * \param v2 second value for the new data point. + * + * \brief Writes 2 values to the file. + */ + void Write2d (std::string context, + double v1, + double v2); + + /** + * \param context specifies the 3D dataset these values came from. + * \param v1 first value for the new data point. + * \param v2 second value for the new data point. + * \param v3 third value for the new data point. + * + * \brief Writes 3 values to the file. + */ + void Write3d (std::string context, + double v1, + double v2, + double v3); + + /** + * \param context specifies the 4D dataset these values came from. + * \param v1 first value for the new data point. + * \param v2 second value for the new data point. + * \param v3 third value for the new data point. + * \param v4 fourth value for the new data point. + * + * \brief Writes 4 values to the file. + */ + void Write4d (std::string context, + double v1, + double v2, + double v3, + double v4); + + /** + * \param context specifies the 5D dataset these values came from. + * \param v1 first value for the new data point. + * \param v2 second value for the new data point. + * \param v3 third value for the new data point. + * \param v4 fourth value for the new data point. + * \param v5 fifth value for the new data point. + * + * \brief Writes 5 values to the file. + */ + void Write5d (std::string context, + double v1, + double v2, + double v3, + double v4, + double v5); + + /** + * \param context specifies the 6D dataset these values came from. + * \param v1 first value for the new data point. + * \param v2 second value for the new data point. + * \param v3 third value for the new data point. + * \param v4 fourth value for the new data point. + * \param v5 fifth value for the new data point. + * \param v6 sixth value for the new data point. + * + * \brief Writes 6 values to the file. + */ + void Write6d (std::string context, + double v1, + double v2, + double v3, + double v4, + double v5, + double v6); + + /** + * \param context specifies the 7D dataset these values came from. + * \param v1 first value for the new data point. + * \param v2 second value for the new data point. + * \param v3 third value for the new data point. + * \param v4 fourth value for the new data point. + * \param v5 fifth value for the new data point. + * \param v6 sixth value for the new data point. + * \param v7 seventh value for the new data point. + * + * \brief Writes 7 values to the file. + */ + void Write7d (std::string context, + double v1, + double v2, + double v3, + double v4, + double v5, + double v6, + double v7); + + /** + * \param context specifies the 8D dataset these values came from. + * \param v1 first value for the new data point. + * \param v2 second value for the new data point. + * \param v3 third value for the new data point. + * \param v4 fourth value for the new data point. + * \param v5 fifth value for the new data point. + * \param v6 sixth value for the new data point. + * \param v7 seventh value for the new data point. + * \param v8 eighth value for the new data point. + * + * \brief Writes 8 values to the file. + */ + void Write8d (std::string context, + double v1, + double v2, + double v3, + double v4, + double v5, + double v6, + double v7, + double v8); + + /** + * \param context specifies the 9D dataset these values came from. + * \param v1 first value for the new data point. + * \param v2 second value for the new data point. + * \param v3 third value for the new data point. + * \param v4 fourth value for the new data point. + * \param v5 fifth value for the new data point. + * \param v6 sixth value for the new data point. + * \param v7 seventh value for the new data point. + * \param v8 eighth value for the new data point. + * \param v9 nineth value for the new data point. + * + * \brief Writes 9 values to the file. + */ + void Write9d (std::string context, + double v1, + double v2, + double v3, + double v4, + double v5, + double v6, + double v7, + double v8, + double v9); + + /** + * \param context specifies the 10D dataset these values came from. + * \param v1 first value for the new data point. + * \param v2 second value for the new data point. + * \param v3 third value for the new data point. + * \param v4 fourth value for the new data point. + * \param v5 fifth value for the new data point. + * \param v6 sixth value for the new data point. + * \param v7 seventh value for the new data point. + * \param v8 eighth value for the new data point. + * \param v9 nineth value for the new data point. + * \param v10 tenth value for the new data point. + * + * \brief Writes 10 values to the file. + */ + void Write10d (std::string context, + double v1, + double v2, + double v3, + double v4, + double v5, + double v6, + double v7, + double v8, + double v9, + double v10); + +private: + /// The file name. + std::string m_outputFileName; + + /// Used to write values to the file. + std::ofstream m_file; + + /// Determines the kind of file written by the aggregator. + enum FileType m_fileType; + + /// Printed between values in the file. + std::string m_separator; + + /// Indicates if the heading line for the file has been set. + bool m_hasHeadingBeenSet; + + /// Heading line for the outputfile. + std::string m_heading; + + /// Format strings for C-style sprintf() function. + std::string m_1dFormat; + std::string m_2dFormat; + std::string m_3dFormat; + std::string m_4dFormat; + std::string m_5dFormat; + std::string m_6dFormat; + std::string m_7dFormat; + std::string m_8dFormat; + std::string m_9dFormat; + std::string m_10dFormat; + +}; // class FileAggregator + + +} // namespace ns3 + +#endif // FILE_AGGREGATOR_H diff --git a/src/stats/model/get-wildcard-matches.cc b/src/stats/model/get-wildcard-matches.cc new file mode 100644 index 000000000..f4a325448 --- /dev/null +++ b/src/stats/model/get-wildcard-matches.cc @@ -0,0 +1,148 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2013 University of Washington + * + * 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: Mitch Watrous (watrous@u.washington.edu) + */ + +#include +#include +#include "get-wildcard-matches.h" +#include "ns3/assert.h" + +namespace ns3 { + +std::string +GetWildcardMatches (const std::string &configPath, + const std::string &matchedPath, + const std::string &wildcardSeparator) +{ + // If the Config path is just "*", return the whole matched path. + if (configPath == "*") + { + return matchedPath; + } + + std::vector nonWildcardTokens; + std::vector nonWildcardTokenPositions; + + size_t nonWildcardTokenCount; + size_t wildcardCount = 0; + + // Get the non-wildcard tokens from the Config path. + size_t tokenStart; + size_t asterisk = -1; + do + { + // Find the non-wildcard token. + tokenStart = asterisk + 1; + asterisk = configPath.find ("*", tokenStart); + + // If a wildcard character was found, increment this counter. + if (asterisk != std::string::npos) + { + wildcardCount++; + } + + // Save this non-wildcard token. + nonWildcardTokens.push_back (configPath.substr (tokenStart, asterisk - tokenStart)); + } + while (asterisk != std::string::npos); + + // If there are no wildcards, return an empty string. + if (wildcardCount == 0) + { + return ""; + } + + // Set the number of non-wildcard tokens in the Config path. + nonWildcardTokenCount = nonWildcardTokens.size (); + + size_t i; + + // Find the positions of the non-wildcard tokens in the matched path. + size_t token; + tokenStart = 0; + for (i = 0; i < nonWildcardTokenCount; i++) + { + // Look for the non-wilcard token. + token = matchedPath.find (nonWildcardTokens[i], tokenStart); + + // Make sure that the token is found. + if (token == std::string::npos) + { + NS_ASSERT_MSG (false, "Error: non-wildcard token not found in matched path"); + } + + // Save the position of this non-wildcard token. + nonWildcardTokenPositions.push_back (token); + + // Start looking for the next non-wildcard token after the end of + // this one. + tokenStart = token + nonWildcardTokens[i].size (); + } + + std::string wildcardMatches = ""; + + // Put the text matches from the matched path for each of the + // wildcards in the Config path into a string, separated by the + // specified separator. + size_t wildcardMatchesSet = 0; + size_t matchStart; + size_t matchEnd; + for (i = 0; i < nonWildcardTokenCount; i++) + { + // Find the start and end of this wildcard match. + matchStart = nonWildcardTokenPositions[i] + nonWildcardTokens[i].size (); + if (i != nonWildcardTokenCount - 1) + { + matchEnd = nonWildcardTokenPositions[i + 1] - 1; + } + else + { + matchEnd = matchedPath.length () - 1; + } + + // This algorithm gets confused by zero length non-wildcard + // tokens. So, only add this wildcard match and update the + // counters if the match was calculated to start before it began. + if (matchStart <= matchEnd) + { + // Add the wildcard match. + wildcardMatches += matchedPath.substr (matchStart, + matchEnd - matchStart + 1); + + // See if all of the wilcard matches have been set. + wildcardMatchesSet++; + if (wildcardMatchesSet == wildcardCount) + { + break; + } + else + { + // If there are still more to set, add the separator to + // the end of the one just added. + wildcardMatches += wildcardSeparator; + } + } + } + + // Return the wildcard matches. + return wildcardMatches; +} + +} // namespace ns3 + diff --git a/src/stats/model/get-wildcard-matches.h b/src/stats/model/get-wildcard-matches.h new file mode 100644 index 000000000..8eb0c2948 --- /dev/null +++ b/src/stats/model/get-wildcard-matches.h @@ -0,0 +1,44 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2013 University of Washington + * + * 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: Mitch Watrous (watrous@u.washington.edu) + */ + +#ifndef GET_WILDCARD_MATCHES_H +#define GET_WILDCARD_MATCHES_H + +#include + +namespace ns3 { + +/** + * \param configPath Config path to access the probe. + * \param matchedPath the path that matched the Config path. + * \param wildcardSeparator the text to put betwen the wildcard + * matches. By default, a space is used. + * + * \brief Returns the text matches from the matched path for each of + * the wildcards in the Config path, separated by the wild card + * separator. + */ +std::string GetWildcardMatches (const std::string &configPath, + const std::string &matchedPath, + const std::string &wildcardSeparator = " "); + +} // namespace ns3 + +#endif // GET_WILDCARD_MATCHES_H diff --git a/src/stats/model/gnuplot-aggregator.cc b/src/stats/model/gnuplot-aggregator.cc new file mode 100644 index 000000000..7e4927759 --- /dev/null +++ b/src/stats/model/gnuplot-aggregator.cc @@ -0,0 +1,327 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2013 University of Washington + * + * 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: Mitch Watrous (watrous@u.washington.edu) + */ + +#include +#include +#include + +#include "gnuplot-aggregator.h" +#include "ns3/abort.h" +#include "ns3/log.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("GnuplotAggregator"); +NS_OBJECT_ENSURE_REGISTERED (GnuplotAggregator); + +TypeId +GnuplotAggregator::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::GnuplotAggregator") + .SetParent () + ; + + return tid; +} + +GnuplotAggregator::GnuplotAggregator (const std::string &outputFileNameWithoutExtension) + : m_outputFileNameWithoutExtension (outputFileNameWithoutExtension), + m_graphicsFileName (m_outputFileNameWithoutExtension + ".png"), + m_title ("Data Values"), + m_xLegend ("X Values"), + m_yLegend ("Y Values"), + m_titleSet (false), + m_xAndYLegendsSet (false), + m_gnuplot (m_graphicsFileName) +{ +} + +GnuplotAggregator::~GnuplotAggregator () +{ + // Give any warnings that should be issued. + if (!m_titleSet) + { + NS_LOG_UNCOND ("Warning: The plot title was not set for the gnuplot aggregator"); + } + if (!m_xAndYLegendsSet) + { + NS_LOG_UNCOND ("Warning: The axis legends were not set for the gnuplot aggregator"); + } + + std::string dataFileName = m_outputFileNameWithoutExtension + ".dat"; + std::string plotFileName = m_outputFileNameWithoutExtension + ".plt"; + std::string scriptFileName = m_outputFileNameWithoutExtension + ".sh"; + + // Open the gnuplot plot and data files. + std::ofstream plotFile; + plotFile.open (plotFileName.c_str ()); + std::ofstream dataFile; + dataFile.open (dataFileName.c_str ()); + + // Skip any NaN's that appear in data. + m_gnuplot.AppendExtra ("set datafile missing \"-nan\""); + + // Write the gnuplot plot and data files. + m_gnuplot.GenerateOutput (plotFile, dataFile, dataFileName); + + // Close the gnuplot plot and data files. + plotFile.close (); + dataFile.close (); + + // Open the shell script file. + std::ofstream scriptFile; + scriptFile.open (scriptFileName.c_str ()); + + // Write the shell script file. + scriptFile << "#!/bin/sh" << std::endl; + scriptFile << std::endl; + scriptFile << "gnuplot " << plotFileName << std::endl; + + // Close the shell script file. + scriptFile.close (); +} + +void +GnuplotAggregator::Write2d (std::string context, double x, double y) +{ + if (m_2dDatasetMap.count (context) == 0) + { + NS_ABORT_MSG ("That 2D data set has not been added"); + return; + } + + if (m_enabled) + { + // Add this 2D data point to its dataset. + m_2dDatasetMap[context].Add (x, y); + } +} + +void +GnuplotAggregator::Write2dWithXErrorDelta (std::string context, + double x, + double y, + double errorDelta) +{ + // See if this dataset has been added. + if (m_2dDatasetMap.count (context) == 0) + { + NS_ABORT_MSG ("That 2D data set has not been added"); + return; + } + + if (m_enabled) + { + // Add this 2D data point with its error bar to its dataset. + m_2dDatasetMap[context].Add (x, y, errorDelta); + } +} + +void +GnuplotAggregator::Write2dWithYErrorDelta (std::string context, + double x, + double y, + double errorDelta) +{ + // See if this dataset has been added. + if (m_2dDatasetMap.count (context) == 0) + { + NS_ABORT_MSG ("That 2D data set has not been added"); + return; + } + + if (m_enabled) + { + // Add this 2D data point with its error bar to its dataset. + m_2dDatasetMap[context].Add (x, y, errorDelta); + } +} + +void +GnuplotAggregator::Write2dWithXYErrorDelta (std::string context, + double x, + double y, + double xErrorDelta, + double yErrorDelta) +{ + // See if this dataset has been added. + if (m_2dDatasetMap.count (context) == 0) + { + NS_ABORT_MSG ("That 2D data set has not been added"); + return; + } + + if (m_enabled) + { + // Add this 2D data point with its error bar to its dataset. + m_2dDatasetMap[context].Add (x, y, xErrorDelta, yErrorDelta); + } +} + +void +GnuplotAggregator::SetTerminal (const std::string &terminal) +{ + // Change the extension for the graphics file. + m_graphicsFileName = m_outputFileNameWithoutExtension + "." + terminal; + + // Update the gnuplot, too. + m_gnuplot.SetTerminal (terminal); + m_gnuplot.SetOutputFilename (m_graphicsFileName); +} + +void +GnuplotAggregator::SetTitle (const std::string &title) +{ + m_gnuplot.SetTitle (title); + m_titleSet = true; +} + +void +GnuplotAggregator::SetLegend (const std::string &xLegend, const std::string &yLegend) +{ + m_gnuplot.SetLegend (xLegend, yLegend); + m_xAndYLegendsSet = true; +} + +void +GnuplotAggregator::SetExtra (const std::string &extra) +{ + m_gnuplot.SetExtra (extra); +} + +void +GnuplotAggregator::AppendExtra (const std::string &extra) +{ + m_gnuplot.AppendExtra (extra); +} + +void +GnuplotAggregator::Add2dDataset (const std::string &dataset, const std::string &title) +{ + // See if this dataset had already been added. + if (m_2dDatasetMap.count (dataset) > 0) + { + NS_ABORT_MSG ("That 2D data set has already been added"); + } + + // Add this dataset to the map so that its values can be saved. + Gnuplot2dDataset gnuplot2dDataset (title); + m_2dDatasetMap[dataset] = gnuplot2dDataset; + + // Add this dataset to the plot so that its values can be plotted. + m_gnuplot.AddDataset (m_2dDatasetMap[dataset]); +} + +void +GnuplotAggregator::Set2dDatasetDefaultExtra (const std::string &extra) +{ + Gnuplot2dDataset::SetDefaultExtra (extra); +} + +void +GnuplotAggregator::Set2dDatasetExtra (const std::string &dataset, const std::string &extra) +{ + if (m_2dDatasetMap.count (dataset) == 0) + { + NS_ABORT_MSG ("That 2D data set has not been added"); + return; + } + + // Set the extra parameters for the dataset. + m_2dDatasetMap[dataset].SetExtra (extra); +} + +void +GnuplotAggregator::Write2dDatasetEmptyLine (const std::string &dataset) +{ + if (m_2dDatasetMap.count (dataset) == 0) + { + NS_ABORT_MSG ("That 2D data set has not been added"); + return; + } + + if (m_enabled) + { + // Add an empty line to the dataset. + m_2dDatasetMap[dataset].AddEmptyLine (); + } +} + +void +GnuplotAggregator::Set2dDatasetDefaultStyle (enum Gnuplot2dDataset::Style style) +{ + Gnuplot2dDataset::SetDefaultStyle (style); +} + +void +GnuplotAggregator::Set2dDatasetStyle (const std::string &dataset, enum Gnuplot2dDataset::Style style) +{ + if (m_2dDatasetMap.count (dataset) == 0) + { + NS_ABORT_MSG ("That 2D data set has not been added"); + return; + } + + // Set the style for the dataset. + m_2dDatasetMap[dataset].SetStyle (style); +} + +void +GnuplotAggregator::Set2dDatasetDefaultErrorBars (enum Gnuplot2dDataset::ErrorBars errorBars) +{ + Gnuplot2dDataset::SetDefaultErrorBars (errorBars); +} + +void +GnuplotAggregator::Set2dDatasetErrorBars (const std::string &dataset, enum Gnuplot2dDataset::ErrorBars errorBars) +{ + if (m_2dDatasetMap.count (dataset) == 0) + { + NS_ABORT_MSG ("That 2D data set has not been added"); + return; + } + + // Set the error bars for the dataset. + m_2dDatasetMap[dataset].SetErrorBars (errorBars); +} + +void +GnuplotAggregator::SetKeyLocation (enum GnuplotAggregator::KeyLocation keyLocation) +{ + // Set the specifed key location. + switch (keyLocation) + { + case NO_KEY: + m_gnuplot.AppendExtra ("set key off"); + break; + case KEY_ABOVE: + m_gnuplot.AppendExtra ("set key outside center above"); + break; + case KEY_BELOW: + m_gnuplot.AppendExtra ("set key outside center below"); + break; + default: + m_gnuplot.AppendExtra ("set key inside"); + break; + } +} + +} // namespace ns3 + diff --git a/src/stats/model/gnuplot-aggregator.h b/src/stats/model/gnuplot-aggregator.h new file mode 100644 index 000000000..84341dfe4 --- /dev/null +++ b/src/stats/model/gnuplot-aggregator.h @@ -0,0 +1,296 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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: L. Felipe Perrone (perrone@bucknell.edu) + * + * Modified by: Mitch Watrous (watrous@u.washington.edu) + * + */ + +#ifndef GNUPLOT_AGGREGATOR_H +#define GNUPLOT_AGGREGATOR_H + +#include +#include +#include "ns3/gnuplot.h" +#include "ns3/data-collection-object.h" + +namespace ns3 { + +/** + * This aggregator produces output used to make gnuplot plots. + **/ +class GnuplotAggregator : public DataCollectionObject +{ +public: + /// The location of the key in the plot. + enum KeyLocation + { + NO_KEY, + KEY_INSIDE, + KEY_ABOVE, + KEY_BELOW + }; + + static TypeId GetTypeId (); + + /** + * \param outputFileNameWithoutExtension name of gnuplot related + * files to write with no extension + * + * Constructs a gnuplot file aggregator that will create a space + * separated gnuplot data file named outputFileNameWithoutExtension + * + ".dat", a gnuplot control file named + * outputFileNameWithoutExtension + ".plt", and a shell script to + * generate the gnuplot named outputFileNameWithoutExtension + + * ".sh". + */ + GnuplotAggregator (const std::string &outputFileNameWithoutExtension); + + virtual ~GnuplotAggregator (); + + // Below are hooked to connectors exporting data + // They are not overloaded since it confuses the compiler when made + // into callbacks + + /** + * \param context specifies the gnuplot 2D dataset for these values + * \param x x coordinate for the new data point + * \param y y coordinate for the new data point + * + * \brief Writes a 2D value to a 2D gnuplot dataset. + * + * Use this method with error bar style NONE. + */ + void Write2d (std::string context, double x, double y); + + /** + * \param context specifies the gnuplot 2D dataset for these values + * \param x x coordinate for the new data point + * \param y y coordinate for the new data point + * \param errorDelta x data point uncertainty + * + * \brief Writes a 2D value to a 2D gnuplot dataset with error bars + * in the x direction. + * + * Use this method with error bar style X. + */ + void Write2dWithXErrorDelta (std::string context, + double x, + double y, + double errorDelta); + + /** + * \param context specifies the gnuplot 2D dataset for these values + * \param x x coordinate for the new data point + * \param y y coordinate for the new data point + * \param errorDelta y data point uncertainty + * + * \brief Writes a 2D value to a 2D gnuplot dataset with error bars + * in the y direction. + * + * Use this method with error bar style Y. + */ + void Write2dWithYErrorDelta (std::string context, + double x, + double y, + double errorDelta); + + /** + * \param context specifies the gnuplot 2D dataset for these values + * \param x x coordinate for the new data point + * \param y y coordinate for the new data point + * \param xErrorDelta x data point uncertainty + * \param yErrorDelta y data point uncertainty + * + * \brief Writes a 2D value to a 2D gnuplot dataset with error bars + * in the x and y directions. + * + * Use this method with error bar style XY. + */ + void Write2dWithXYErrorDelta (std::string context, + double x, + double y, + double xErrorDelta, + double yErrorDelta); + + // Methods to configure the plot + + /** + * \param terminal terminal setting string for output. The default terminal + * string is "png" + */ + void SetTerminal (const std::string &terminal); + + /** + * \param title set new plot title string to use for this plot. + */ + void SetTitle (const std::string &title); + + /** + * \param xLegend the legend for the x horizontal axis + * \param yLegend the legend for the y vertical axis + */ + void SetLegend (const std::string &xLegend, const std::string &yLegend); + + /** + * \param extra set extra gnuplot directive for output. + */ + void SetExtra (const std::string &extra); + + /** + * \param extra append extra gnuplot directive for output. + */ + void AppendExtra (const std::string &extra); + + // Methods for datasets + + /** + * \param dataset the gnuplot 2D dataset to be plotted. + * \param title the title to be associated to this dataset. + * + * \brief Adds a 2D dataset to the plot. + * + * Creates an empty dataset. Usually, the dataset's title is + * displayed in the legend box. + * + * The string in dataset should match the context for the Collector + * it is connected to. + */ + void Add2dDataset (const std::string &dataset, const std::string &title); + + /** + * \param extra extra formatting + * + * \brief Change extra formatting style parameters for newly created + * objects. + */ + static void Set2dDatasetDefaultExtra (const std::string &extra); + + /** + * \param dataset the gnuplot 2D dataset to be plotted. + * \param extra extra formatting + * + * \brief Add extra formatting parameters to this dataset. + * + * The string in dataset should match the context for the Collector + * it is connected to. + */ + void Set2dDatasetExtra (const std::string &dataset, const std::string &extra); + + /** + * \param dataset the gnuplot 2D dataset to be plotted. + * + * \brief Add an empty line in the data output sequence. + * + * Writes an empty line in the plot data, which breaks continuous + * lines and does other things in the output. + * + * The string in dataset should match the context for the Collector + * it is connected to. + */ + void Write2dDatasetEmptyLine (const std::string &dataset); + + /** + * \param style the style of plotting to use for newly created datasets. + * + * \brief Change default style for all newly created objects. + */ + static void Set2dDatasetDefaultStyle (enum Gnuplot2dDataset::Style style); + + /** + * \param dataset the gnuplot 2D dataset to be plotted. + * \param style the style of plotting to use for this dataset. + * + * \brief Set the style of plotting to use for this dataset. + * + * The string in dataset should match the context for the Collector + * it is connected to. + */ + void Set2dDatasetStyle (const std::string &dataset, enum Gnuplot2dDataset::Style style); + + /** + * \param errorBars the style of errorbars to use for newly created datasets. + * + * \brief Change default errorbars style for all newly created objects. + */ + static void Set2dDatasetDefaultErrorBars (enum Gnuplot2dDataset::ErrorBars errorBars); + + /** + * \param dataset the gnuplot 2D dataset to be plotted. + * \param errorBars the style of errorbars to display. + * + * \brief Set the error bars to use for this dataset. + * + * If you use any style other than none, you need + * to make sure you store the delta information in + * this dataset with the right GnuplotDataset::Add + * method. + * + * The string in dataset should match the context for the Collector + * it is connected to. + */ + void Set2dDatasetErrorBars (const std::string &dataset, enum Gnuplot2dDataset::ErrorBars errorBars); + + /** + * \param keyLocation the location of the key in the plot. + * + * \brief Set the location of the key in the plot. + */ + void SetKeyLocation (enum KeyLocation keyLocation); + +private: + /// The output file name without any extension. + std::string m_outputFileNameWithoutExtension; + + /// The graphics file name with its extension. + std::string m_graphicsFileName; + + /// The title. + std::string m_title; + + /// The terminal. + std::string m_terminal; + + /// The x legend. + std::string m_xLegend; + + /// The y legend. + std::string m_yLegend; + + /// The extra gnuplot command that will be added to the gnuplot + /// control statements. + std::string m_extra; + + /// Set equal to true after setting the title. + bool m_titleSet; + + /// Set equal to true after setting the x and y legends. + bool m_xAndYLegendsSet; + + /// Used to create gnuplot files. + Gnuplot m_gnuplot; + + /// Maps context strings to 2D datasets. + std::map m_2dDatasetMap; + +}; // class GnuplotAggregator + + +} // namespace ns3 + +#endif // GNUPLOT_AGGREGATOR_H diff --git a/src/stats/model/gnuplot.cc b/src/stats/model/gnuplot.cc index 6c81b7091..031997b6d 100644 --- a/src/stats/model/gnuplot.cc +++ b/src/stats/model/gnuplot.cc @@ -50,17 +50,30 @@ struct GnuplotDataset::Data virtual std::string GetCommand () const = 0; /** - * Prints the plot description used as argument to (s)plot. Either the - * function expression or a datafile description. Should include m_title and - * m_extra in the output. + * Prints the plot description used as argument to (s)plot. Either + * the function expression or a datafile description. Should include + * m_title and m_extra in the output. + * + * If more than one output file is being generated, i.e. separate + * data and control files, then the index for the current dataset + * and the name for the data file are also included. */ - virtual void PrintExpression (std::ostream &os) const = 0; + virtual void PrintExpression (std::ostream &os, + bool generateOneOutputFile, + unsigned int dataFileDatasetIndex, + std::string &dataFileName) const = 0; /** * Print the inline data file contents trailing the plot command. Empty for * functions. */ - virtual void PrintDatafile (std::ostream &os) const = 0; + virtual void PrintDataFile (std::ostream &os, bool generateOneOutputFile) const = 0; + + /** + * Checks to see if this GnuplotDataset is empty. + * \return indicates if this GnuplotDataset is empty. + */ + virtual bool IsEmpty () const = 0; }; GnuplotDataset::Data::Data(const std::string& title) @@ -142,8 +155,12 @@ struct Gnuplot2dDataset::Data2d : public GnuplotDataset::Data Data2d(const std::string& title); virtual std::string GetCommand () const; - virtual void PrintExpression (std::ostream &os) const; - virtual void PrintDatafile (std::ostream &os) const; + virtual void PrintExpression (std::ostream &os, + bool generateOneOutputFile, + unsigned int dataFileDatasetIndex, + std::string &dataFileName) const; + virtual void PrintDataFile (std::ostream &os, bool generateOneOutputFile) const; + virtual bool IsEmpty () const; }; Gnuplot2dDataset::Data2d::Data2d(const std::string& title) @@ -160,12 +177,24 @@ Gnuplot2dDataset::Data2d::GetCommand () const } void -Gnuplot2dDataset::Data2d::PrintExpression (std::ostream &os) const +Gnuplot2dDataset::Data2d::PrintExpression (std::ostream &os, + bool generateOneOutputFile, + unsigned int dataFileDatasetIndex, + std::string &dataFileName) const { - os << "'-' "; + // Print the appropriate thing based on whether separate output and + // date files are being generated. + if (generateOneOutputFile) + { + os << "\"-\" "; + } + else + { + os << "\"" << dataFileName << "\" index " << dataFileDatasetIndex; + } if (m_title.size ()) - os << " title '" << m_title << "'"; + os << " title \"" << m_title << "\""; switch (m_style) { case LINES: @@ -227,7 +256,7 @@ Gnuplot2dDataset::Data2d::PrintExpression (std::ostream &os) const } void -Gnuplot2dDataset::Data2d::PrintDatafile (std::ostream &os) const +Gnuplot2dDataset::Data2d::PrintDataFile (std::ostream &os, bool generateOneOutputFile) const { for (PointSet::const_iterator i = m_pointset.begin (); i != m_pointset.end (); ++i) @@ -252,7 +281,24 @@ Gnuplot2dDataset::Data2d::PrintDatafile (std::ostream &os) const break; } } - os << "e" << std::endl; + + // Print the appropriate thing based on whether separate output and + // date files are being generated. + if (generateOneOutputFile) + { + os << "e" << std::endl; + } + else + { + os << std::endl; + os << std::endl; + } +} + +bool +Gnuplot2dDataset::Data2d::IsEmpty () const +{ + return (m_pointset.size () == 0); } // --- Gnuplot2dDataset ---------------------------------------------------- // @@ -352,8 +398,12 @@ struct Gnuplot2dFunction::Function2d : public GnuplotDataset::Data Function2d(const std::string& title, const std::string& function); virtual std::string GetCommand () const; - virtual void PrintExpression (std::ostream &os) const; - virtual void PrintDatafile (std::ostream &os) const; + virtual void PrintExpression (std::ostream &os, + bool generateOneOutputFile, + unsigned int dataFileDatasetIndex, + std::string &dataFileName) const; + virtual void PrintDataFile (std::ostream &os, bool generateOneOutputFile) const; + virtual bool IsEmpty () const; }; Gnuplot2dFunction::Function2d::Function2d(const std::string& title, const std::string& function) @@ -369,22 +419,31 @@ Gnuplot2dFunction::Function2d::GetCommand () const } void -Gnuplot2dFunction::Function2d::PrintExpression (std::ostream &os) const +Gnuplot2dFunction::Function2d::PrintExpression (std::ostream &os, + bool generateOneOutputFile, + unsigned int dataFileDatasetIndex, + std::string &dataFileName) const { os << m_function; if (m_title.size ()) - os << " title '" << m_title << "'"; + os << " title \"" << m_title << "\""; if (m_extra.size ()) os << " " << m_extra; } void -Gnuplot2dFunction::Function2d::PrintDatafile (std::ostream &os) const +Gnuplot2dFunction::Function2d::PrintDataFile (std::ostream &os, bool generateOneOutputFile) const { } +bool +Gnuplot2dFunction::Function2d::IsEmpty () const +{ + return false; +} + // --- Gnuplot2dFunction --------------------------------------------------- // Gnuplot2dFunction::Gnuplot2dFunction (const std::string& title, const std::string& function) @@ -414,8 +473,12 @@ struct Gnuplot3dDataset::Data3d : public GnuplotDataset::Data Data3d(const std::string& title); virtual std::string GetCommand () const; - virtual void PrintExpression (std::ostream &os) const; - virtual void PrintDatafile (std::ostream &os) const; + virtual void PrintExpression (std::ostream &os, + bool generateOneOutputFile, + unsigned int dataFileDatasetIndex, + std::string &dataFileName) const; + virtual void PrintDataFile (std::ostream &os, bool generateOneOutputFile) const; + virtual bool IsEmpty () const; }; Gnuplot3dDataset::Data3d::Data3d(const std::string& title) @@ -431,22 +494,25 @@ Gnuplot3dDataset::Data3d::GetCommand () const } void -Gnuplot3dDataset::Data3d::PrintExpression (std::ostream &os) const +Gnuplot3dDataset::Data3d::PrintExpression (std::ostream &os, + bool generateOneOutputFile, + unsigned int dataFileDatasetIndex, + std::string &dataFileName) const { - os << "'-' "; + os << "\"-\" "; if (m_style.size ()) os << " " << m_style; if (m_title.size ()) - os << " title '" << m_title << "'"; + os << " title \"" << m_title << "\""; if (m_extra.size ()) os << " " << m_extra; } void -Gnuplot3dDataset::Data3d::PrintDatafile (std::ostream &os) const +Gnuplot3dDataset::Data3d::PrintDataFile (std::ostream &os, bool generateOneOutputFile) const { for (PointSet::const_iterator i = m_pointset.begin (); i != m_pointset.end (); ++i) @@ -461,6 +527,12 @@ Gnuplot3dDataset::Data3d::PrintDatafile (std::ostream &os) const os << "e" << std::endl; } +bool +Gnuplot3dDataset::Data3d::IsEmpty () const +{ + return (m_pointset.size () == 0); +} + // --- Gnuplot3dDataset ---------------------------------------------------- // std::string Gnuplot3dDataset::m_defaultStyle = ""; @@ -514,8 +586,12 @@ struct Gnuplot3dFunction::Function3d : public GnuplotDataset::Data Function3d(const std::string& title, const std::string& function); virtual std::string GetCommand () const; - virtual void PrintExpression (std::ostream &os) const; - virtual void PrintDatafile (std::ostream &os) const; + virtual void PrintExpression (std::ostream &os, + bool generateOneOutputFile, + unsigned int dataFileDatasetIndex, + std::string &dataFileName) const; + virtual void PrintDataFile (std::ostream &os, bool generateOneOutputFile) const; + virtual bool IsEmpty () const; }; Gnuplot3dFunction::Function3d::Function3d(const std::string& title, const std::string& function) @@ -531,22 +607,31 @@ Gnuplot3dFunction::Function3d::GetCommand () const } void -Gnuplot3dFunction::Function3d::PrintExpression (std::ostream &os) const +Gnuplot3dFunction::Function3d::PrintExpression (std::ostream &os, + bool generateOneOutputFile, + unsigned int dataFileDatasetIndex, + std::string &dataFileName) const { os << m_function; if (m_title.size ()) - os << " title '" << m_title << "'"; + os << " title \"" << m_title << "\""; if (m_extra.size ()) os << " " << m_extra; } void -Gnuplot3dFunction::Function3d::PrintDatafile (std::ostream &os) const +Gnuplot3dFunction::Function3d::PrintDataFile (std::ostream &os, bool generateOneOutputFile) const { } +bool +Gnuplot3dFunction::Function3d::IsEmpty () const +{ + return false; +} + // --- Gnuplot3dFunction --------------------------------------------------- // Gnuplot3dFunction::Gnuplot3dFunction (const std::string& title, const std::string& function) @@ -565,10 +650,17 @@ Gnuplot3dFunction::SetFunction (const std::string& function) Gnuplot::Gnuplot (const std::string& outputFilename, const std::string& title) : m_outputFilename (outputFilename), m_terminal ( DetectTerminal (outputFilename) ), - m_title (title) + m_title (title), + m_generateOneOutputFile (false), + m_dataFileDatasetIndex (0) { } +void Gnuplot::SetOutputFilename (const std::string& outputFilename) +{ + m_outputFilename = outputFilename; +} + std::string Gnuplot::DetectTerminal (const std::string& filename) { std::string::size_type dotpos = filename.rfind ('.'); @@ -623,25 +715,38 @@ Gnuplot::AddDataset (const GnuplotDataset& dataset) } void -Gnuplot::GenerateOutput (std::ostream &os) const +Gnuplot::GenerateOutput (std::ostream &os) +{ + // If this version of this function is called, it is assumed that a + // single output file is being generated. + m_generateOneOutputFile = true; + + // Send the gnuplot metadata to the same stream as the data stream. + GenerateOutput (os, os, ""); +} + +void +Gnuplot::GenerateOutput (std::ostream &osControl, + std::ostream &osData, + std::string dataFileName) { if (m_terminal.size ()) - os << "set terminal " << m_terminal << std::endl; + osControl << "set terminal " << m_terminal << std::endl; if (m_outputFilename.size ()) - os << "set output '" << m_outputFilename << "'" << std::endl; + osControl << "set output \"" << m_outputFilename << "\"" << std::endl; if (m_title.size ()) - os << "set title '" << m_title << "'" << std::endl; + osControl << "set title \"" << m_title << "\"" << std::endl; if (m_xLegend.size ()) - os << "set xlabel '" << m_xLegend << "'" << std::endl; + osControl << "set xlabel \"" << m_xLegend << "\"" << std::endl; if (m_yLegend.size ()) - os << "set ylabel '" << m_yLegend << "'" << std::endl; + osControl << "set ylabel \"" << m_yLegend << "\"" << std::endl; if (m_extra.size ()) - os << m_extra << std::endl; + osControl << m_extra << std::endl; if (m_datasets.empty ()) return; @@ -658,31 +763,49 @@ Gnuplot::GenerateOutput (std::ostream &os) const "Cannot mix 'plot' and 'splot' GnuplotDatasets."); } - os << command << " "; + osControl << command << " "; // Print all dataset expressions + bool isDataEmpty; for (Datasets::const_iterator i = m_datasets.begin (); i != m_datasets.end ();) { - i->m_data->PrintExpression (os); + // Only print the dataset if it's not empty. + isDataEmpty = i->m_data->IsEmpty (); + if (!isDataEmpty) + { + // Print the appropriate expression based on whether we are + // generating separate output and date files. + i->m_data->PrintExpression (osControl, + m_generateOneOutputFile, + m_dataFileDatasetIndex, + dataFileName); + + m_dataFileDatasetIndex++; + } i++; - - if (i != m_datasets.end ()) + if (i != m_datasets.end () && !isDataEmpty) { - os << ", "; + osControl << ", "; } } - os << std::endl; + osControl << std::endl; // followed by the inline datafile. for (Datasets::const_iterator i = m_datasets.begin (); i != m_datasets.end (); i++) { - i->m_data->PrintDatafile (os); + i->m_data->PrintDataFile (osData, m_generateOneOutputFile); } } +void +Gnuplot::SetDataFileDatasetIndex (unsigned int index) +{ + m_dataFileDatasetIndex = index; +} + // ------------------------------------------------------------------------- // GnuplotCollection::GnuplotCollection (const std::string& outputFilename) @@ -713,20 +836,42 @@ GnuplotCollection::GetPlot (unsigned int id) } void -GnuplotCollection::GenerateOutput (std::ostream &os) const +GnuplotCollection::GenerateOutput (std::ostream &os) { + // If this version of this function is called, it is assumed that a + // single output file is being generated. + if (m_terminal.size ()) os << "set terminal " << m_terminal << std::endl; if (m_outputFilename.size ()) - os << "set output '" << m_outputFilename << "'" << std::endl; + os << "set output \"" << m_outputFilename << "\"" << std::endl; - for (Plots::const_iterator i = m_plots.begin (); i != m_plots.end (); ++i) + for (Plots::iterator i = m_plots.begin (); i != m_plots.end (); ++i) { i->GenerateOutput (os); } } +void +GnuplotCollection::GenerateOutput (std::ostream &osControl, std::ostream &osData, +std::string dataFileName) +{ + // If this version of this function is called, it is assumed that + // separate output and date files are being generated. + + if (m_terminal.size ()) + osControl << "set terminal " << m_terminal << std::endl; + + if (m_outputFilename.size ()) + osControl << "set output \"" << m_outputFilename << "\"" << std::endl; + + for (Plots::iterator i = m_plots.begin (); i != m_plots.end (); ++i) + { + i->GenerateOutput (osControl, osData, dataFileName); + } +} + // ------------------------------------------------------------------------- // } // namespace ns3 diff --git a/src/stats/model/gnuplot.h b/src/stats/model/gnuplot.h index d72dd5e03..6a8779497 100644 --- a/src/stats/model/gnuplot.h +++ b/src/stats/model/gnuplot.h @@ -351,6 +351,13 @@ public: */ Gnuplot (const std::string& outputFilename="", const std::string& title = ""); + /** + * \param outputFilename the name of the file where the rendering of the + * graph will be generated if you feed the command stream output by + * Gnuplot::GenerateOutput to the gnuplot program. + */ + void SetOutputFilename (const std::string& outputFilename); + /** * Crude attempt to auto-detect the correct terminal setting by inspecting * the filename's extension. @@ -391,10 +398,37 @@ public: void AddDataset (const GnuplotDataset& dataset); /** - * \param os the output stream on which the relevant gnuplot commands should - * be generated. Including output file and terminal headers. + * \param os the output stream on which the relevant gnuplot + * commands should be generated. Including output file and terminal + * headers. + * + * \brief Writes gnuplot commands and data values to a single + * output stream. */ - void GenerateOutput (std::ostream &os) const; + void GenerateOutput (std::ostream &os); + + /** + * \param osControl the output stream on which the relevant gnuplot + * contol commands should be generated. Including output file and + * terminal headers. + * \param osData the output stream on which the relevant gnuplot + * data values should be generated. + * \param dataFileName the name for the data file that will be + * written. + * + * \brief Writes gnuplot commands and data values to two + * different outputs streams. + */ + void GenerateOutput (std::ostream &osControl, + std::ostream &osData, + std::string dataFileName); + + /** + * \param index the index for the data stream in the data file. + * + * \brief Sets the current data stream index in the data file. + */ + void SetDataFileDatasetIndex (unsigned int index); private: typedef std::vector Datasets; @@ -408,6 +442,10 @@ private: std::string m_xLegend; std::string m_yLegend; std::string m_extra; + + bool m_generateOneOutputFile; + + unsigned int m_dataFileDatasetIndex; }; /** @@ -446,8 +484,21 @@ public: * \param os the output stream on which the relevant gnuplot commands should * be generated. */ - void GenerateOutput (std::ostream &os) const; + void GenerateOutput (std::ostream &os); + /** + * \param osControl the output stream on which the relevant gnuplot + * contol commands should be generated. Including output file and + * terminal headers. + * \param osData the output stream on which the relevant gnuplot + * data values should be generated. + * \param dataFileName the name for the data file that will be + * written. + */ + void GenerateOutput (std::ostream &osControl, + std::ostream &osData, + std::string dataFileName); + private: typedef std::vector Plots; diff --git a/src/stats/model/probe.cc b/src/stats/model/probe.cc new file mode 100644 index 000000000..260ca0314 --- /dev/null +++ b/src/stats/model/probe.cc @@ -0,0 +1,71 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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 + * + * Authors: L. Felipe Perrone (perrone@bucknell.edu) + * Tiago G. Rodrigues (tgr002@bucknell.edu) + */ + +#include "ns3/probe.h" +#include "ns3/object.h" +#include "ns3/simulator.h" +#include "ns3/log.h" + +NS_LOG_COMPONENT_DEFINE ("Probe"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (Probe); + +TypeId +Probe::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::Probe") + .SetParent () + .AddAttribute ("Start", + "Time data collection starts", + TimeValue (Seconds (0)), + MakeTimeAccessor (&Probe::m_start), + MakeTimeChecker ()) + .AddAttribute ("Stop", + "Time when data collection stops. The special time value of 0 disables this attribute", + TimeValue (Seconds (0)), + MakeTimeAccessor (&Probe::m_stop), + MakeTimeChecker ()) + ; + return tid; +} + +Probe::Probe () +{ + NS_LOG_FUNCTION (this); +} + +Probe::~Probe () +{ + NS_LOG_FUNCTION (this); +} + +bool +Probe::IsEnabled (void) const +{ + return (DataCollectionObject::IsEnabled () + && Simulator::Now () >= m_start + && (m_stop == Seconds (0) || Simulator::Now () < m_stop)); + +} + +} // namespace ns3 diff --git a/src/stats/model/probe.h b/src/stats/model/probe.h new file mode 100644 index 000000000..661e1815e --- /dev/null +++ b/src/stats/model/probe.h @@ -0,0 +1,79 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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 + * + * Authors: L. Felipe Perrone (perrone@bucknell.edu) + * Tiago G. Rodrigues (tgr002@bucknell.edu) + */ + +#ifndef PROBE_H +#define PROBE_H + +#include "ns3/data-collection-object.h" +#include "ns3/nstime.h" + +namespace ns3 { + +/** + * Base class for probes. + * + * This class provides general functionality to control each + * probe and the data generated by it. + */ + +class Probe : public DataCollectionObject +{ +public: + static TypeId GetTypeId (); + Probe (); + virtual ~Probe (); + + /** + * \return true if Probe is currently enabled + */ + virtual bool IsEnabled (void) const; + + /** + * \brief connect to a trace source attribute provided by a given object + * + * \param traceSource the name of the attribute TraceSource to connect to + * \param obj ns3::Object to connect to + * \return true if the trace source was successfully connected + */ + virtual bool ConnectByObject (std::string traceSource, Ptr obj) = 0; + + /** + * \brief connect to a trace source provided by a config path + * + * \param path Config path to bind to + * + * Note, if an invalid path is provided, the probe will not be connected + * to anything. + */ + virtual void ConnectByPath (std::string path) = 0; + +protected: + /// Time when logging starts. + Time m_start; + + /// Time when logging stops. + Time m_stop; + +}; + +} // namespace ns3 + +#endif // PROBE_H diff --git a/src/stats/model/time-series-adaptor.cc b/src/stats/model/time-series-adaptor.cc new file mode 100644 index 000000000..a1d787bfa --- /dev/null +++ b/src/stats/model/time-series-adaptor.cc @@ -0,0 +1,112 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2013 University of Washington + * + * 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: Mitch Watrous (watrous@u.washington.edu) + */ + +#include +#include + +#include "ns3/time-series-adaptor.h" +#include "ns3/object.h" +#include "ns3/traced-value.h" +#include "ns3/log.h" +#include "ns3/simulator.h" + +NS_LOG_COMPONENT_DEFINE ("TimeSeriesAdaptor"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (TimeSeriesAdaptor); + +TypeId +TimeSeriesAdaptor::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TimeSeriesAdaptor") + .SetParent () + .AddConstructor () + .AddTraceSource ( "Output", + "The current simulation time versus the current value converted to a double", + MakeTraceSourceAccessor (&TimeSeriesAdaptor::m_output)) + ; + return tid; +} + +TimeSeriesAdaptor::TimeSeriesAdaptor () +{ + NS_LOG_FUNCTION (this); +} + +TimeSeriesAdaptor::~TimeSeriesAdaptor () +{ + NS_LOG_FUNCTION (this); +} + +void +TimeSeriesAdaptor::TraceSinkDouble (double oldData, double newData) +{ + NS_LOG_FUNCTION (this << oldData << newData); + + // Don't do anything if the time series adaptor is not enabled. + if (!IsEnabled ()) + { + NS_LOG_DEBUG ("Time series adaptor not enabled"); + return; + } + + // Time stamp the value with the current time in seconds. + m_output (Simulator::Now ().GetSeconds (), newData); +} + +void +TimeSeriesAdaptor::TraceSinkBoolean (bool oldData, bool newData) +{ + NS_LOG_FUNCTION (this << oldData << newData); + + // Call the trace sink that actually does something. + TraceSinkDouble (oldData, newData); +} + +void +TimeSeriesAdaptor::TraceSinkUinteger8 (uint8_t oldData, uint8_t newData) +{ + NS_LOG_FUNCTION (this << oldData << newData); + + // Call the trace sink that actually does something. + TraceSinkDouble (oldData, newData); +} + +void +TimeSeriesAdaptor::TraceSinkUinteger16 (uint16_t oldData, uint16_t newData) +{ + NS_LOG_FUNCTION (this << oldData << newData); + + // Call the trace sink that actually does something. + TraceSinkDouble (oldData, newData); +} + +void +TimeSeriesAdaptor::TraceSinkUinteger32 (uint32_t oldData, uint32_t newData) +{ + NS_LOG_FUNCTION (this << oldData << newData); + + // Call the trace sink that actually does something. + TraceSinkDouble (oldData, newData); +} + +} // namespace ns3 + diff --git a/src/stats/model/time-series-adaptor.h b/src/stats/model/time-series-adaptor.h new file mode 100644 index 000000000..520fc703a --- /dev/null +++ b/src/stats/model/time-series-adaptor.h @@ -0,0 +1,115 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2013 University of Washington + * + * 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: Mitch Watrous (watrous@u.washington.edu) + */ + +#ifndef TIME_SERIES_ADAPTOR_H +#define TIME_SERIES_ADAPTOR_H + +#include "ns3/data-collection-object.h" +#include "ns3/object.h" +#include "ns3/type-id.h" +#include "ns3/traced-value.h" + +namespace ns3 { + +/** + * \brief Takes probed values of different types and outputs the + * current time plus the value with both converted to doubles. + * + * The role of the TimeSeriesAdaptor class is that of an adaptor + * class, to take raw-valued probe data of different types, and output + * a tuple of two double values. The first is a timestamp which may + * be set to different resolutions (e.g. Seconds, Milliseconds, etc.) + * in the future, but which presently is hardcoded to Seconds. The second + * is the conversion of + * a non-double value to a double value (possibly with loss of precision). + * + * It should be noted that time series adaptors convert + * Simulation Time objects to double values in its output. + */ +class TimeSeriesAdaptor : public DataCollectionObject +{ +public: + static TypeId GetTypeId (void); + + TimeSeriesAdaptor (); + virtual ~TimeSeriesAdaptor (); + + /** + * \brief Trace sink for receiving data from double valued trace + * sources. + * \param oldData the original value. + * \param newData the new value. + * + * This method serves as a trace sink to double valued trace + * sources. + */ + void TraceSinkDouble (double oldData, double newData); + + /** + * \brief Trace sink for receiving data from bool valued trace + * sources. + * \param oldData the original value. + * \param newData the new value. + * + * This method serves as a trace sink to bool valued trace + * sources. + */ + void TraceSinkBoolean (bool oldData, bool newData); + + /** + * \brief Trace sink for receiving data from uint8_t valued trace + * sources. + * \param oldData the original value. + * \param newData the new value. + * + * This method serves as a trace sink to uint8_t valued trace + * sources. + */ + void TraceSinkUinteger8 (uint8_t oldData, uint8_t newData); + + /** + * \brief Trace sink for receiving data from uint16_t valued trace + * sources. + * \param oldData the original value. + * \param newData the new value. + * + * This method serves as a trace sink to uint16_t valued trace + * sources. + */ + void TraceSinkUinteger16 (uint16_t oldData, uint16_t newData); + + /** + * \brief Trace sink for receiving data from uint32_t valued trace + * sources. + * \param oldData the original value. + * \param newData the new value. + * + * This method serves as a trace sink to uint32_t valued trace + * sources. + */ + void TraceSinkUinteger32 (uint32_t oldData, uint32_t newData); + +private: + TracedCallback m_output; +}; + +} // namespace ns3 + +#endif // TIME_SERIES_ADAPTOR_H diff --git a/src/stats/model/uinteger-16-probe.cc b/src/stats/model/uinteger-16-probe.cc new file mode 100644 index 000000000..6e70e5ac3 --- /dev/null +++ b/src/stats/model/uinteger-16-probe.cc @@ -0,0 +1,109 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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 + * + * Authors: L. Felipe Perrone (perrone@bucknell.edu) + * Tiago G. Rodrigues (tgr002@bucknell.edu) + * + * Modified by: Mitch Watrous (watrous@u.washington.edu) + */ + +#include "ns3/uinteger-16-probe.h" +#include "ns3/log.h" +#include "ns3/names.h" +#include "ns3/config.h" +#include "ns3/trace-source-accessor.h" + +NS_LOG_COMPONENT_DEFINE ("Uinteger16Probe"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (Uinteger16Probe); + +TypeId +Uinteger16Probe::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::Uinteger16Probe") + .SetParent () + .AddConstructor () + .AddTraceSource ( "Output", + "The uint16_t that serves as output for this probe", + MakeTraceSourceAccessor (&Uinteger16Probe::m_output)) + ; + return tid; +} + +Uinteger16Probe::Uinteger16Probe () +{ + NS_LOG_FUNCTION (this); + m_output = 0; +} + +Uinteger16Probe::~Uinteger16Probe () +{ + NS_LOG_FUNCTION (this); +} + +uint16_t +Uinteger16Probe::GetValue (void) const +{ + NS_LOG_FUNCTION (this); + return m_output; +} +void +Uinteger16Probe::SetValue (uint16_t newVal) +{ + NS_LOG_FUNCTION (this << newVal); + m_output = newVal; +} + +void +Uinteger16Probe::SetValueByPath (std::string path, uint16_t newVal) +{ + NS_LOG_FUNCTION (path << newVal); + Ptr probe = Names::Find (path); + NS_ASSERT_MSG (probe, "Error: Can't find probe for path " << path); + probe->SetValue (newVal); +} + +bool +Uinteger16Probe::ConnectByObject (std::string traceSource, Ptr obj) +{ + NS_LOG_FUNCTION (this << traceSource << obj); + NS_LOG_DEBUG ("Name of probe (if any) in names database: " << Names::FindPath (obj)); + bool connected = obj->TraceConnectWithoutContext (traceSource, MakeCallback (&ns3::Uinteger16Probe::TraceSink, this)); + return connected; +} + +void +Uinteger16Probe::ConnectByPath (std::string path) +{ + NS_LOG_FUNCTION (this << path); + NS_LOG_DEBUG ("Name of probe to search for in config database: " << path); + Config::ConnectWithoutContext (path, MakeCallback (&ns3::Uinteger16Probe::TraceSink, this)); +} + +void +Uinteger16Probe::TraceSink (uint16_t oldData, uint16_t newData) +{ + NS_LOG_FUNCTION (this << oldData << newData); + if (IsEnabled ()) + { + m_output = newData; + } +} + +} // namespace ns3 diff --git a/src/stats/model/uinteger-16-probe.h b/src/stats/model/uinteger-16-probe.h new file mode 100644 index 000000000..95ae9dee8 --- /dev/null +++ b/src/stats/model/uinteger-16-probe.h @@ -0,0 +1,101 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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 + * + * Authors: L. Felipe Perrone (perrone@bucknell.edu) + * Tiago G. Rodrigues (tgr002@bucknell.edu) + * + * Modified by: Mitch Watrous (watrous@u.washington.edu) + */ + +#ifndef UINTEGER_16_PROBE_H +#define UINTEGER_16_PROBE_H + +#include "ns3/callback.h" +#include "ns3/probe.h" +#include "ns3/traced-value.h" + +namespace ns3 { + +/** + * This class is designed to probe an underlying ns3 TraceSource exporting + * an uint16_t. This probe exports a trace source "Output" of type uint16_t. + * The Output trace source emits a value when either the trace source + * emits a new value, or when SetValue () is called. + * + * The current value of the probe can be polled with the GetValue () method. + */ +class Uinteger16Probe : public Probe +{ +public: + static TypeId GetTypeId (); + Uinteger16Probe (); + virtual ~Uinteger16Probe (); + + /** + * \return the most recent value + */ + uint16_t GetValue (void) const; + + /** + * \param value set the traced uint16_t to a new value + */ + void SetValue (uint16_t value); + + /** + * \brief Set a probe value by its name in the Config system + * + * \param path Config path to access the probe + * \param value set the traced uint16_t to a new value + */ + static void SetValueByPath (std::string path, uint16_t value); + + /** + * \brief connect to a trace source attribute provided by a given object + * + * \param traceSource the name of the attribute TraceSource to connect to + * \param obj ns3::Object to connect to + * \return true if the trace source was successfully connected + */ + virtual bool ConnectByObject (std::string traceSource, Ptr obj); + + /** + * \brief connect to a trace source provided by a config path + * + * \param path Config path to bind to + * + * Note, if an invalid path is provided, the probe will not be connected + * to anything. + */ + virtual void ConnectByPath (std::string path); + +private: + /** + * \brief Method to connect to an underlying ns3::TraceSource of type uint16_t + * + * \param oldData previous value of the uint16_t + * \param newData new value of the uint16_t + * + * \internal + */ + void TraceSink (uint16_t oldData, uint16_t newData); + + TracedValue m_output; +}; + +} // namespace ns3 + +#endif // UINTEGER_16_PROBE_H diff --git a/src/stats/model/uinteger-32-probe.cc b/src/stats/model/uinteger-32-probe.cc new file mode 100644 index 000000000..28f9fdcff --- /dev/null +++ b/src/stats/model/uinteger-32-probe.cc @@ -0,0 +1,109 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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 + * + * Authors: L. Felipe Perrone (perrone@bucknell.edu) + * Tiago G. Rodrigues (tgr002@bucknell.edu) + * + * Modified by: Mitch Watrous (watrous@u.washington.edu) + */ + +#include "ns3/uinteger-32-probe.h" +#include "ns3/log.h" +#include "ns3/names.h" +#include "ns3/config.h" +#include "ns3/trace-source-accessor.h" + +NS_LOG_COMPONENT_DEFINE ("Uinteger32Probe"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (Uinteger32Probe); + +TypeId +Uinteger32Probe::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::Uinteger32Probe") + .SetParent () + .AddConstructor () + .AddTraceSource ( "Output", + "The uint32_t that serves as output for this probe", + MakeTraceSourceAccessor (&Uinteger32Probe::m_output)) + ; + return tid; +} + +Uinteger32Probe::Uinteger32Probe () +{ + NS_LOG_FUNCTION (this); + m_output = 0; +} + +Uinteger32Probe::~Uinteger32Probe () +{ + NS_LOG_FUNCTION (this); +} + +uint32_t +Uinteger32Probe::GetValue (void) const +{ + NS_LOG_FUNCTION (this); + return m_output; +} +void +Uinteger32Probe::SetValue (uint32_t newVal) +{ + NS_LOG_FUNCTION (this << newVal); + m_output = newVal; +} + +void +Uinteger32Probe::SetValueByPath (std::string path, uint32_t newVal) +{ + NS_LOG_FUNCTION (path << newVal); + Ptr probe = Names::Find (path); + NS_ASSERT_MSG (probe, "Error: Can't find probe for path " << path); + probe->SetValue (newVal); +} + +bool +Uinteger32Probe::ConnectByObject (std::string traceSource, Ptr obj) +{ + NS_LOG_FUNCTION (this << traceSource << obj); + NS_LOG_DEBUG ("Name of probe (if any) in names database: " << Names::FindPath (obj)); + bool connected = obj->TraceConnectWithoutContext (traceSource, MakeCallback (&ns3::Uinteger32Probe::TraceSink, this)); + return connected; +} + +void +Uinteger32Probe::ConnectByPath (std::string path) +{ + NS_LOG_FUNCTION (this << path); + NS_LOG_DEBUG ("Name of probe to search for in config database: " << path); + Config::ConnectWithoutContext (path, MakeCallback (&ns3::Uinteger32Probe::TraceSink, this)); +} + +void +Uinteger32Probe::TraceSink (uint32_t oldData, uint32_t newData) +{ + NS_LOG_FUNCTION (this << oldData << newData); + if (IsEnabled ()) + { + m_output = newData; + } +} + +} // namespace ns3 diff --git a/src/stats/model/uinteger-32-probe.h b/src/stats/model/uinteger-32-probe.h new file mode 100644 index 000000000..2c4a34283 --- /dev/null +++ b/src/stats/model/uinteger-32-probe.h @@ -0,0 +1,101 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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 + * + * Authors: L. Felipe Perrone (perrone@bucknell.edu) + * Tiago G. Rodrigues (tgr002@bucknell.edu) + * + * Modified by: Mitch Watrous (watrous@u.washington.edu) + */ + +#ifndef UINTEGER_32_PROBE_H +#define UINTEGER_32_PROBE_H + +#include "ns3/callback.h" +#include "ns3/probe.h" +#include "ns3/traced-value.h" + +namespace ns3 { + +/** + * This class is designed to probe an underlying ns3 TraceSource exporting + * an uint32_t. This probe exports a trace source "Output" of type uint32_t. + * The Output trace source emits a value when either the trace source + * emits a new value, or when SetValue () is called. + * + * The current value of the probe can be polled with the GetValue () method. + */ +class Uinteger32Probe : public Probe +{ +public: + static TypeId GetTypeId (); + Uinteger32Probe (); + virtual ~Uinteger32Probe (); + + /** + * \return the most recent value + */ + uint32_t GetValue (void) const; + + /** + * \param value set the traced uint32_t to a new value + */ + void SetValue (uint32_t value); + + /** + * \brief Set a probe value by its name in the Config system + * + * \param path Config path to access the probe + * \param value set the traced uint32_t to a new value + */ + static void SetValueByPath (std::string path, uint32_t value); + + /** + * \brief connect to a trace source attribute provided by a given object + * + * \param traceSource the name of the attribute TraceSource to connect to + * \param obj ns3::Object to connect to + * \return true if the trace source was successfully connected + */ + virtual bool ConnectByObject (std::string traceSource, Ptr obj); + + /** + * \brief connect to a trace source provided by a config path + * + * \param path Config path to bind to + * + * Note, if an invalid path is provided, the probe will not be connected + * to anything. + */ + virtual void ConnectByPath (std::string path); + +private: + /** + * \brief Method to connect to an underlying ns3::TraceSource of type uint32_t + * + * \param oldData previous value of the uint32_t + * \param newData new value of the uint32_t + * + * \internal + */ + void TraceSink (uint32_t oldData, uint32_t newData); + + TracedValue m_output; +}; + +} // namespace ns3 + +#endif // UINTEGER_32_PROBE_H diff --git a/src/stats/model/uinteger-8-probe.cc b/src/stats/model/uinteger-8-probe.cc new file mode 100644 index 000000000..823af2847 --- /dev/null +++ b/src/stats/model/uinteger-8-probe.cc @@ -0,0 +1,109 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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 + * + * Authors: L. Felipe Perrone (perrone@bucknell.edu) + * Tiago G. Rodrigues (tgr002@bucknell.edu) + * + * Modified by: Mitch Watrous (watrous@u.washington.edu) + */ + +#include "ns3/uinteger-8-probe.h" +#include "ns3/log.h" +#include "ns3/names.h" +#include "ns3/config.h" +#include "ns3/trace-source-accessor.h" + +NS_LOG_COMPONENT_DEFINE ("Uinteger8Probe"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (Uinteger8Probe); + +TypeId +Uinteger8Probe::GetTypeId () +{ + static TypeId tid = TypeId ("ns3::Uinteger8Probe") + .SetParent () + .AddConstructor () + .AddTraceSource ( "Output", + "The uint8_t that serves as output for this probe", + MakeTraceSourceAccessor (&Uinteger8Probe::m_output)) + ; + return tid; +} + +Uinteger8Probe::Uinteger8Probe () +{ + NS_LOG_FUNCTION (this); + m_output = 0; +} + +Uinteger8Probe::~Uinteger8Probe () +{ + NS_LOG_FUNCTION (this); +} + +uint8_t +Uinteger8Probe::GetValue (void) const +{ + NS_LOG_FUNCTION (this); + return m_output; +} +void +Uinteger8Probe::SetValue (uint8_t newVal) +{ + NS_LOG_FUNCTION (this << newVal); + m_output = newVal; +} + +void +Uinteger8Probe::SetValueByPath (std::string path, uint8_t newVal) +{ + NS_LOG_FUNCTION (path << newVal); + Ptr probe = Names::Find (path); + NS_ASSERT_MSG (probe, "Error: Can't find probe for path " << path); + probe->SetValue (newVal); +} + +bool +Uinteger8Probe::ConnectByObject (std::string traceSource, Ptr obj) +{ + NS_LOG_FUNCTION (this << traceSource << obj); + NS_LOG_DEBUG ("Name of probe (if any) in names database: " << Names::FindPath (obj)); + bool connected = obj->TraceConnectWithoutContext (traceSource, MakeCallback (&ns3::Uinteger8Probe::TraceSink, this)); + return connected; +} + +void +Uinteger8Probe::ConnectByPath (std::string path) +{ + NS_LOG_FUNCTION (this << path); + NS_LOG_DEBUG ("Name of probe to search for in config database: " << path); + Config::ConnectWithoutContext (path, MakeCallback (&ns3::Uinteger8Probe::TraceSink, this)); +} + +void +Uinteger8Probe::TraceSink (uint8_t oldData, uint8_t newData) +{ + NS_LOG_FUNCTION (this << oldData << newData); + if (IsEnabled ()) + { + m_output = newData; + } +} + +} // namespace ns3 diff --git a/src/stats/model/uinteger-8-probe.h b/src/stats/model/uinteger-8-probe.h new file mode 100644 index 000000000..6aeb7f0c0 --- /dev/null +++ b/src/stats/model/uinteger-8-probe.h @@ -0,0 +1,101 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2011 Bucknell University + * + * 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 + * + * Authors: L. Felipe Perrone (perrone@bucknell.edu) + * Tiago G. Rodrigues (tgr002@bucknell.edu) + * + * Modified by: Mitch Watrous (watrous@u.washington.edu) + */ + +#ifndef UINTEGER_8_PROBE_H +#define UINTEGER_8_PROBE_H + +#include "ns3/callback.h" +#include "ns3/probe.h" +#include "ns3/traced-value.h" + +namespace ns3 { + +/** + * This class is designed to probe an underlying ns3 TraceSource exporting + * an uint8_t. This probe exports a trace source "Output" of type uint8_t. + * The Output trace source emits a value when either the trace source + * emits a new value, or when SetValue () is called. + * + * The current value of the probe can be polled with the GetValue () method. + */ +class Uinteger8Probe : public Probe +{ +public: + static TypeId GetTypeId (); + Uinteger8Probe (); + virtual ~Uinteger8Probe (); + + /** + * \return the most recent value + */ + uint8_t GetValue (void) const; + + /** + * \param value set the traced uint8_t to a new value + */ + void SetValue (uint8_t value); + + /** + * \brief Set a probe value by its name in the Config system + * + * \param path Config path to access the probe + * \param value set the traced uint8_t to a new value + */ + static void SetValueByPath (std::string path, uint8_t value); + + /** + * \brief connect to a trace source attribute provided by a given object + * + * \param traceSource the name of the attribute TraceSource to connect to + * \param obj ns3::Object to connect to + * \return true if the trace source was successfully connected + */ + virtual bool ConnectByObject (std::string traceSource, Ptr obj); + + /** + * \brief connect to a trace source provided by a config path + * + * \param path Config path to bind to + * + * Note, if an invalid path is provided, the probe will not be connected + * to anything. + */ + virtual void ConnectByPath (std::string path); + +private: + /** + * \brief Method to connect to an underlying ns3::TraceSource of type uint8_t + * + * \param oldData previous value of the uint8_t + * \param newData new value of the uint8_t + * + * \internal + */ + void TraceSink (uint8_t oldData, uint8_t newData); + + TracedValue m_output; +}; + +} // namespace ns3 + +#endif // UINTEGER_8_PROBE_H diff --git a/src/stats/test/double-probe-test-suite.cc b/src/stats/test/double-probe-test-suite.cc new file mode 100644 index 000000000..7a33e6cc4 --- /dev/null +++ b/src/stats/test/double-probe-test-suite.cc @@ -0,0 +1,165 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ + +// Include a header file from your module to test. +#include "ns3/double-probe.h" +#include "ns3/test.h" +#include "ns3/core-module.h" + +using namespace ns3; + +class SampleEmitter : public Object +{ +public: + static TypeId GetTypeId (void); + SampleEmitter () + { + } + virtual ~SampleEmitter () + { + } + void Start () + { + Reschedule (); + } + void Reschedule () + { + m_time = m_var.GetValue (); + Simulator::Schedule (Seconds (m_time), &SampleEmitter::Report, this); + m_time += Simulator::Now ().GetSeconds (); + } + double GetTime () + { + return m_time; + } + double GetValue () + { + return aux; + } +private: + void Report () + { + aux = m_var.GetValue (); + m_trace = aux; + Reschedule (); + } + ExponentialVariable m_var; + double m_time; + TracedValue m_trace; + double aux; +}; + +Ptr s = CreateObject (); + +TypeId +SampleEmitter::GetTypeId (void) +{ + static TypeId tid = TypeId ("SampleEmitter") + .SetParent () + .AddTraceSource ("Emitter", "XX", MakeTraceSourceAccessor (&SampleEmitter::m_trace)) + ; + return tid; +} + +class ProbeTestCase1 : public TestCase +{ +public: + ProbeTestCase1 (); + virtual ~ProbeTestCase1 (); + +private: + virtual void DoRun (void); + void TraceSink (std::string context, double oldValue, double newValue); + uint32_t m_objectProbed; + uint32_t m_pathProbed; +}; + +ProbeTestCase1::ProbeTestCase1 () + : TestCase ("basic probe test case"), + m_objectProbed (0), + m_pathProbed (0) +{ +} + +ProbeTestCase1::~ProbeTestCase1 () +{ +} + +void +ProbeTestCase1::TraceSink (std::string context, double oldValue, double newValue) +{ + NS_TEST_ASSERT_MSG_GT (Simulator::Now (), Seconds (100), "Probed a value outside of the time window"); + NS_TEST_ASSERT_MSG_LT (Simulator::Now (), Seconds (200), "Probed a value outside of the time window"); + + NS_TEST_ASSERT_MSG_EQ_TOL (s->GetValue (), newValue, 0.00001, "Value probed different than value in the variable"); + + if (context == "testProbe") + { + m_objectProbed++; + } + else if (context == "testProbe2") + { + m_pathProbed++; + } +} + +void +ProbeTestCase1::DoRun (void) +{ + // Test that all instances of probe data are between time window specified + // Check also that probes can be hooked to sources by Object and by path + + Ptr p = CreateObject (); + p->SetName ("testProbe"); + + Simulator::Schedule (Seconds (1), &SampleEmitter::Start, s); + p->SetAttribute ("Start", TimeValue (Seconds (100.0))); + p->SetAttribute ("Stop", TimeValue (Seconds (200.0))); + Simulator::Stop (Seconds (300)); + + // Register our emitter object so we can fetch it by using the Config + // namespace + Names::Add ("/Names/SampleEmitter", s); + + // Hook probe to the emitter. + p->ConnectByObject ("Emitter", s); + + // Hook our test function to the probe trace source + p->TraceConnect ("Output", p->GetName (), MakeCallback (&ProbeTestCase1::TraceSink, this)); + + // Repeat but hook the probe to the object this time using the Config + // name set above + Ptr p2 = CreateObject (); + p2->SetName ("testProbe2"); + p2->SetAttribute ("Start", TimeValue (Seconds (100.0))); + p2->SetAttribute ("Stop", TimeValue (Seconds (200.0))); + + // Hook probe to the emitter. + p2->ConnectByPath ("/Names/SampleEmitter/Emitter"); + + // Hook our test function to the probe trace source + p2->TraceConnect ("Output", p2->GetName (), MakeCallback (&ProbeTestCase1::TraceSink, this)); + + Simulator::Run (); + + // Check that each trace sink was called + NS_TEST_ASSERT_MSG_GT (m_objectProbed, 0, "Trace sink for object probe never called"); + NS_TEST_ASSERT_MSG_GT (m_pathProbed, 0, "Trace sink for path probe never called"); + Simulator::Destroy (); +} + + +class ProbeTestSuite : public TestSuite +{ +public: + ProbeTestSuite (); +}; + +ProbeTestSuite::ProbeTestSuite () + : TestSuite ("double-probe", UNIT) +{ + AddTestCase (new ProbeTestCase1, TestCase::QUICK); +} + +// Do not forget to allocate an instance of this TestSuite +static ProbeTestSuite probeTestSuite; + diff --git a/src/stats/test/examples-to-run.py b/src/stats/test/examples-to-run.py index 37e6d7a49..da4c69777 100644 --- a/src/stats/test/examples-to-run.py +++ b/src/stats/test/examples-to-run.py @@ -8,7 +8,12 @@ # # See test.py for more information. cpp_examples = [ + ("double-probe-example", "True", "True"), + ("file-aggregator-example", "True", "True"), + ("file-helper-example", "True", "True"), + ("gnuplot-aggregator-example", "True", "True"), ("gnuplot-example", "False", "False"), + ("gnuplot-helper-example", "True", "True"), ] # A list of Python examples to run in order to ensure that they remain diff --git a/src/stats/wscript b/src/stats/wscript index 7cc66e6d7..3d9a98033 100644 --- a/src/stats/wscript +++ b/src/stats/wscript @@ -11,29 +11,44 @@ def configure(conf): "library 'sqlite3' not found") def build(bld): - obj = bld.create_ns3_module('stats', ['network']) + obj = bld.create_ns3_module('stats', ['core']) obj.source = [ + 'helper/delay-jitter-estimation.cc', + 'helper/file-helper.cc', + 'helper/gnuplot-helper.cc', 'model/data-calculator.cc', - 'model/packet-data-calculators.cc', 'model/time-data-calculators.cc', 'model/data-output-interface.cc', 'model/omnet-data-output.cc', 'model/data-collector.cc', 'model/gnuplot.cc', - 'helper/delay-jitter-estimation.cc', + 'model/data-collection-object.cc', + 'model/probe.cc', + 'model/boolean-probe.cc', + 'model/double-probe.cc', + 'model/uinteger-8-probe.cc', + 'model/uinteger-16-probe.cc', + 'model/uinteger-32-probe.cc', + 'model/time-series-adaptor.cc', + 'model/file-aggregator.cc', + 'model/gnuplot-aggregator.cc', + 'model/get-wildcard-matches.cc', ] module_test = bld.create_ns3_module_test_library('stats') module_test.source = [ 'test/basic-data-calculators-test-suite.cc', 'test/average-test-suite.cc', + 'test/double-probe-test-suite.cc', ] headers = bld(features='ns3header') headers.module = 'stats' headers.source = [ + 'helper/delay-jitter-estimation.h', + 'helper/file-helper.h', + 'helper/gnuplot-helper.h', 'model/data-calculator.h', - 'model/packet-data-calculators.h', 'model/time-data-calculators.h', 'model/basic-data-calculators.h', 'model/data-output-interface.h', @@ -41,7 +56,17 @@ def build(bld): 'model/data-collector.h', 'model/gnuplot.h', 'model/average.h', - 'helper/delay-jitter-estimation.h', + 'model/data-collection-object.h', + 'model/probe.h', + 'model/boolean-probe.h', + 'model/double-probe.h', + 'model/uinteger-8-probe.h', + 'model/uinteger-16-probe.h', + 'model/uinteger-32-probe.h', + 'model/time-series-adaptor.h', + 'model/file-aggregator.h', + 'model/gnuplot-aggregator.h', + 'model/get-wildcard-matches.h', ] if bld.env['SQLITE_STATS']: