From 49d1be220fe4c6d46f12155d4a0396cc70d538bf Mon Sep 17 00:00:00 2001 From: Andras Varga Date: Wed, 11 Nov 2009 12:44:09 +0100 Subject: [PATCH] Bug #645: fixes for opening stats file with OMNeT++ --- examples/stats/README | 20 ++++ examples/stats/wifi-example-sim.cc | 6 ++ src/contrib/stats/basic-data-calculators.h | 26 ++--- src/contrib/stats/data-calculator.cc | 16 +++ src/contrib/stats/data-calculator.h | 51 ++++++++++ src/contrib/stats/data-output-interface.h | 5 + src/contrib/stats/omnet-data-output.cc | 112 +++++++++++++++++---- src/contrib/stats/omnet-data-output.h | 24 +++-- src/contrib/stats/sqlite-data-output.cc | 19 ++++ src/contrib/stats/sqlite-data-output.h | 4 + src/contrib/stats/time-data-calculators.cc | 10 +- 11 files changed, 244 insertions(+), 49 deletions(-) diff --git a/examples/stats/README b/examples/stats/README index e4a98209c..2918f0e24 100644 --- a/examples/stats/README +++ b/examples/stats/README @@ -14,3 +14,23 @@ More information on the statistics package and this example is available online on the ns-3 wiki at: http://www.nsnam.org/wiki/index.php/Statistical_Framework_for_Network_Simulation + +*** Using ns-3 with the OMNeT++ analysis tool *** + +The stat framework can write out the result in a format that is compatible with the +output format of OMNeT++ 4 Discrete Event Simulator Framework. +Use the wifi-example-omnet.sh script to generate the results in OMNeT++ format. + +If you want to analyse the results with OMNeT++'s result analyser tool: +a) Download OMNeT++ 4 and install it. Start the IDE. (www.omnetpp.org) +b) If you do not want to install the whole simulator framework, there is a seperate + package which contains only the analysis tool from the OMNeT++ package. + You can download it from http://omnetpp.org/download/release/omnetpp-scave.tgz + +Once you are running the OMNeT++ IDE or the separate analysis tool (SCAVE) +- Choose File|Import...|Existing Projects into Workspace, then click [Next] +- Select root directory. (choose the examples/stats directory) and click [Finish] + +Double click the wifi-example-omnet.anf in the opened project and select +the Chart page to see the created chart. Experiment with the analysis tool and read its +documentation: http://omnetpp.org/doc/omnetpp40/userguide/ch09.html diff --git a/examples/stats/wifi-example-sim.cc b/examples/stats/wifi-example-sim.cc index 601f66492..17c504ab9 100644 --- a/examples/stats/wifi-example-sim.cc +++ b/examples/stats/wifi-example-sim.cc @@ -200,6 +200,7 @@ int main(int argc, char *argv[]) { Ptr > totalTx = CreateObject >(); totalTx->SetKey("wifi-tx-frames"); + totalTx->SetContext("node[0]"); Config::Connect("/NodeList/0/DeviceList/*/$ns3::WifiNetDevice/Mac/MacTx", MakeBoundCallback(&TxCallback, totalTx)); data.AddDataCalculator(totalTx); @@ -211,6 +212,7 @@ int main(int argc, char *argv[]) { Ptr totalRx = CreateObject(); totalRx->SetKey("wifi-rx-frames"); + totalRx->SetContext("node[1]"); Config::Connect("/NodeList/1/DeviceList/*/$ns3::WifiNetDevice/Mac/MacRx", MakeCallback(&PacketCounterCalculator::PacketUpdate, totalRx)); @@ -225,6 +227,7 @@ int main(int argc, char *argv[]) { Ptr appTx = CreateObject(); appTx->SetKey("sender-tx-packets"); + appTx->SetContext("node[0]"); Config::Connect("/NodeList/0/ApplicationList/*/$Sender/Tx", MakeCallback(&PacketCounterCalculator::PacketUpdate, appTx)); @@ -237,6 +240,7 @@ int main(int argc, char *argv[]) { Ptr > appRx = CreateObject >(); appRx->SetKey("receiver-rx-packets"); + appRx->SetContext("node[1]"); receiver->SetCounter(appRx); data.AddDataCalculator(appRx); @@ -263,6 +267,7 @@ int main(int argc, char *argv[]) { Ptr appTxPkts = CreateObject(); appTxPkts->SetKey("tx-pkt-size"); + appTxPkts->SetContext("node[0]"); Config::Connect("/NodeList/0/ApplicationList/*/$Sender/Tx", MakeCallback (&PacketSizeMinMaxAvgTotalCalculator::PacketUpdate, @@ -277,6 +282,7 @@ int main(int argc, char *argv[]) { Ptr delayStat = CreateObject(); delayStat->SetKey("delay"); + delayStat->SetContext("."); receiver->SetDelayTracker(delayStat); data.AddDataCalculator(delayStat); diff --git a/src/contrib/stats/basic-data-calculators.h b/src/contrib/stats/basic-data-calculators.h index 8968047ee..6063f4381 100644 --- a/src/contrib/stats/basic-data-calculators.h +++ b/src/contrib/stats/basic-data-calculators.h @@ -29,7 +29,8 @@ namespace ns3 { //------------------------------------------------------------ //-------------------------------------------- template - class MinMaxAvgTotalCalculator : public DataCalculator { + class MinMaxAvgTotalCalculator : public DataCalculator, + public StatisticalSummary { public: MinMaxAvgTotalCalculator(); virtual ~MinMaxAvgTotalCalculator(); @@ -38,6 +39,15 @@ namespace ns3 { virtual void Output(DataOutputCallback &callback) const; + long getCount() const { return m_count; } + double getSum() const { return m_total; } + double getMin() const { return m_min; } + double getMax() const { return m_max; } + double getMean() const { return m_total / (double)m_count; } + double getStddev() const { return NaN; } // unsupported + double getVariance() const { return NaN; } // unsupported + double getSqrSum() const { return NaN; } // unsupported + protected: virtual void DoDispose(void); @@ -86,23 +96,15 @@ namespace ns3 { } // end MinMaxAvgTotalCalculator::Update } + template void MinMaxAvgTotalCalculator::Output(DataOutputCallback &callback) const { - callback.OutputSingleton(m_key, "count", m_count); - if (m_count > 0) { - callback.OutputSingleton(m_key, "total", m_total); - callback.OutputSingleton(m_key, "average", m_total/m_count); - callback.OutputSingleton(m_key, "max", m_max); - callback.OutputSingleton(m_key, "min", m_min); - } - // end MinMaxAvgTotalCalculator::Output + callback.OutputStatistic(m_context, m_key, this); } - - //------------------------------------------------------------ //-------------------------------------------- template @@ -178,7 +180,7 @@ namespace ns3 { void CounterCalculator::Output(DataOutputCallback &callback) const { - callback.OutputSingleton(m_key, "count", m_count); + callback.OutputSingleton(m_context, m_key, m_count); // end CounterCalculator::Output } diff --git a/src/contrib/stats/data-calculator.cc b/src/contrib/stats/data-calculator.cc index e96c6d591..d473f92b9 100644 --- a/src/contrib/stats/data-calculator.cc +++ b/src/contrib/stats/data-calculator.cc @@ -27,6 +27,8 @@ using namespace ns3; NS_LOG_COMPONENT_DEFINE("DataCalculator"); +static double zero = 0; +const double ns3::NaN = zero / zero; //-------------------------------------------------------------- //---------------------------------------------- @@ -68,6 +70,20 @@ DataCalculator::GetKey() const // end DataCalculator::GetKey } +//---------------------------------------------- +void +DataCalculator::SetContext(const std::string context) +{ + m_context = context; + // end DataCalculator::SetContext +} + +std::string +DataCalculator::GetContext() const +{ + return m_context; + // end DataCalculator::GetContext +} //---------------------------------------------- void DataCalculator::Enable() diff --git a/src/contrib/stats/data-calculator.h b/src/contrib/stats/data-calculator.h index 70ed3bb72..4f95b5da3 100644 --- a/src/contrib/stats/data-calculator.h +++ b/src/contrib/stats/data-calculator.h @@ -26,9 +26,56 @@ #include "ns3/simulator.h" namespace ns3 { + extern const double NaN; + inline bool isNaN(double x) { return x != x; } class DataOutputCallback; + class StatisticalSummary { + public: + /** + * Returns the number of the observations. + */ + virtual long getCount() const = 0; + + /** + * Returns the sum of the values. + * @see getWeightedSum() + */ + virtual double getSum() const = 0; + + /** + * Returns the sum of the squared values. + * @see getWeightedSqrSum() + */ + virtual double getSqrSum() const = 0; + + /** + * Returns the minimum of the values. + */ + virtual double getMin() const = 0; + + /** + * Returns the maximum of the values. + */ + virtual double getMax() const = 0; + + /** + * Returns the mean of the (weighted) observations. + */ + virtual double getMean() const = 0; + + /** + * Returns the standard deviation of the (weighted) observations. + */ + virtual double getStddev() const = 0; + + /** + * Returns the variance of the (weighted) observations. + */ + virtual double getVariance() const = 0; + }; + //------------------------------------------------------------ //-------------------------------------------- class DataCalculator : public Object { @@ -43,6 +90,9 @@ namespace ns3 { void SetKey(const std::string key); std::string GetKey() const; + void SetContext(const std::string context); + std::string GetContext() const; + virtual void Start(const Time& startTime); virtual void Stop(const Time& stopTime); @@ -52,6 +102,7 @@ namespace ns3 { bool m_enabled; // Descendant classes *must* check & respect m_enabled! std::string m_key; + std::string m_context; virtual void DoDispose(void); diff --git a/src/contrib/stats/data-output-interface.h b/src/contrib/stats/data-output-interface.h index 20c7744e4..105c9f4db 100644 --- a/src/contrib/stats/data-output-interface.h +++ b/src/contrib/stats/data-output-interface.h @@ -23,6 +23,7 @@ #include "ns3/object.h" #include "ns3/nstime.h" +#include "ns3/data-calculator.h" namespace ns3 { @@ -52,6 +53,10 @@ namespace ns3 { public: virtual ~DataOutputCallback() {} + virtual void OutputStatistic(std::string key, + std::string variable, + const StatisticalSummary *statSum) = 0; + virtual void OutputSingleton(std::string key, std::string variable, int val) = 0; diff --git a/src/contrib/stats/omnet-data-output.cc b/src/contrib/stats/omnet-data-output.cc index d67742190..9903c9ec8 100644 --- a/src/contrib/stats/omnet-data-output.cc +++ b/src/contrib/stats/omnet-data-output.cc @@ -19,6 +19,7 @@ */ #include +#include #include "ns3/log.h" #include "ns3/nstime.h" @@ -54,26 +55,31 @@ OmnetDataOutput::DoDispose() } //---------------------------------------------- + +inline bool isNumeric(const std::string& s) { + char *endp; + strtod(s.c_str(), &endp); + return endp == s.c_str() + s.size(); +} + void OmnetDataOutput::Output(DataCollector &dc) { std::ofstream scalarFile; - std::string fn = m_filePrefix + ".sca"; - scalarFile.open(fn.c_str(), std::ios_base::app); + std::string fn = m_filePrefix +"-"+dc.GetRunLabel()+ ".sca"; + scalarFile.open(fn.c_str(), std::ios_base::out); - scalarFile << std::endl; + // TODO add timestamp to the runlevel scalarFile << "run " << dc.GetRunLabel() << std::endl; - scalarFile << std::endl; scalarFile << "attr experiment \"" << dc.GetExperimentLabel() << "\"" << std::endl; scalarFile << "attr strategy \"" << dc.GetStrategyLabel() << "\"" << std::endl; - scalarFile << "attr input \"" << dc.GetInputLabel() + scalarFile << "attr measurement \"" << dc.GetInputLabel() << "\"" << std::endl; scalarFile << "attr description \"" << dc.GetDescription() << "\"" << std::endl; - scalarFile << std::endl; for (MetadataList::iterator i = dc.MetadataBegin(); i != dc.MetadataEnd(); i++) { @@ -83,7 +89,18 @@ OmnetDataOutput::Output(DataCollector &dc) } scalarFile << std::endl; - + if (isNumeric(dc.GetInputLabel())) { + scalarFile << "scalar . measurement \"" << dc.GetInputLabel() + << "\"" << std::endl; + } + for (MetadataList::iterator i = dc.MetadataBegin(); + i != dc.MetadataEnd(); i++) { + std::pair blob = (*i); + if (isNumeric(blob.second)) { + scalarFile << "scalar . \"" << blob.first << "\" \"" << blob.second << "\"" + << std::endl; + } + } OmnetOutputCallback callback(&scalarFile); for (DataCalculatorList::iterator i = dc.DataCalculatorBegin(); @@ -97,6 +114,7 @@ OmnetDataOutput::Output(DataCollector &dc) // end OmnetDataOutput::Output } + OmnetDataOutput::OmnetOutputCallback::OmnetOutputCallback (std::ostream *scalar) : m_scalar(scalar) @@ -104,42 +122,92 @@ OmnetDataOutput::OmnetOutputCallback::OmnetOutputCallback } void -OmnetDataOutput::OmnetOutputCallback::OutputSingleton(std::string key, - std::string variable, +OmnetDataOutput::OmnetOutputCallback::OutputStatistic(std::string context, + std::string name, + const StatisticalSummary *statSum) +{ + if (context == "") + context = "."; + if (name == "") + name = "\"\""; + (*m_scalar) << "statistic " << context << " " << name << std::endl; + if (!isNaN(statSum->getCount())) + (*m_scalar) << "field count " << statSum->getCount() << std::endl; + if (!isNaN(statSum->getSum())) + (*m_scalar) << "field sum " << statSum->getSum() << std::endl; + if (!isNaN(statSum->getMean())) + (*m_scalar) << "field mean " << statSum->getMean() << std::endl; + if (!isNaN(statSum->getMin())) + (*m_scalar) << "field min " << statSum->getMin() << std::endl; + if (!isNaN(statSum->getMax())) + (*m_scalar) << "field max " << statSum->getMax() << std::endl; + if (!isNaN(statSum->getSqrSum())) + (*m_scalar) << "field sqrsum " << statSum->getSqrSum() << std::endl; + if (!isNaN(statSum->getStddev())) + (*m_scalar) << "field stddev " << statSum->getStddev() << std::endl; +} + +void +OmnetDataOutput::OmnetOutputCallback::OutputSingleton(std::string context, + std::string name, int val) { - (*m_scalar) << "scalar " << key << " " << variable << " " << val << std::endl; + if (context == "") + context = "."; + if (name == "") + name = "\"\""; + (*m_scalar) << "scalar " << context << " " << name << " " << val << std::endl; // end OmnetDataOutput::OmnetOutputCallback::OutputSingleton } + void -OmnetDataOutput::OmnetOutputCallback::OutputSingleton(std::string key, - std::string variable, +OmnetDataOutput::OmnetOutputCallback::OutputSingleton(std::string context, + std::string name, uint32_t val) { - (*m_scalar) << "scalar " << key << " " << variable << " " << val << std::endl; + if (context == "") + context = "."; + if (name == "") + name = "\"\""; + (*m_scalar) << "scalar " << context << " " << name << " " << val << std::endl; // end OmnetDataOutput::OmnetOutputCallback::OutputSingleton } + void -OmnetDataOutput::OmnetOutputCallback::OutputSingleton(std::string key, - std::string variable, +OmnetDataOutput::OmnetOutputCallback::OutputSingleton(std::string context, + std::string name, double val) { - (*m_scalar) << "scalar " << key << " " << variable << " " << val << std::endl; + if (context == "") + context = "."; + if (name == "") + name = "\"\""; + (*m_scalar) << "scalar " << context << " " << name << " " << val << std::endl; // end OmnetDataOutput::OmnetOutputCallback::OutputSingleton } + void -OmnetDataOutput::OmnetOutputCallback::OutputSingleton(std::string key, - std::string variable, +OmnetDataOutput::OmnetOutputCallback::OutputSingleton(std::string context, + std::string name, std::string val) { - (*m_scalar) << "scalar " << key << " " << variable << " " << val << std::endl; + if (context == "") + context = "."; + if (name == "") + name = "\"\""; + (*m_scalar) << "scalar " << context << " " << name << " " << val << std::endl; // end OmnetDataOutput::OmnetOutputCallback::OutputSingleton } + void -OmnetDataOutput::OmnetOutputCallback::OutputSingleton(std::string key, - std::string variable, +OmnetDataOutput::OmnetOutputCallback::OutputSingleton(std::string context, + std::string name, Time val) { - (*m_scalar) << "scalar " << key << " " << variable << " " << val << std::endl; + if (context == "") + context = "."; + if (name == "") + name = "\"\""; + (*m_scalar) << "scalar " << context << " " << name << " " << val.GetTimeStep() << std::endl; // end OmnetDataOutput::OmnetOutputCallback::OutputSingleton } diff --git a/src/contrib/stats/omnet-data-output.h b/src/contrib/stats/omnet-data-output.h index a3e1bb3f3..2fadf52e9 100644 --- a/src/contrib/stats/omnet-data-output.h +++ b/src/contrib/stats/omnet-data-output.h @@ -45,24 +45,28 @@ namespace ns3 { public: OmnetOutputCallback(std::ostream *scalar); - void OutputSingleton(std::string key, - std::string variable, + void OutputStatistic(std::string context, + std::string name, + const StatisticalSummary *statSum); + + void OutputSingleton(std::string context, + std::string name, int val); - void OutputSingleton(std::string key, - std::string variable, + void OutputSingleton(std::string context, + std::string name, uint32_t val); - void OutputSingleton(std::string key, - std::string variable, + void OutputSingleton(std::string context, + std::string name, double val); - void OutputSingleton(std::string key, - std::string variable, + void OutputSingleton(std::string context, + std::string name, std::string val); - void OutputSingleton(std::string key, - std::string variable, + void OutputSingleton(std::string context, + std::string name, Time val); private: diff --git a/src/contrib/stats/sqlite-data-output.cc b/src/contrib/stats/sqlite-data-output.cc index 5e59d0f41..f04a0e342 100644 --- a/src/contrib/stats/sqlite-data-output.cc +++ b/src/contrib/stats/sqlite-data-output.cc @@ -153,6 +153,25 @@ SqliteDataOutput::SqliteOutputCallback::SqliteOutputCallback // end SqliteDataOutput::SqliteOutputCallback::SqliteOutputCallback } +void +SqliteDataOutput::SqliteOutputCallback::OutputStatistic(std::string key, + std::string variable, + const StatisticalSummary *statSum) +{ + OutputSingleton(key,variable+"-count", (double)statSum->getCount()); + if (!isNaN(statSum->getSum())) + OutputSingleton(key,variable+"-total", statSum->getSum()); + if (!isNaN(statSum->getMax())) + OutputSingleton(key,variable+"-max", statSum->getMax()); + if (!isNaN(statSum->getMin())) + OutputSingleton(key,variable+"-min", statSum->getMin()); + if (!isNaN(statSum->getSqrSum())) + OutputSingleton(key,variable+"-sqrsum", statSum->getSqrSum()); + if (!isNaN(statSum->getStddev())) + OutputSingleton(key,variable+"-stddev", statSum->getStddev()); +} + + void SqliteDataOutput::SqliteOutputCallback::OutputSingleton(std::string key, std::string variable, diff --git a/src/contrib/stats/sqlite-data-output.h b/src/contrib/stats/sqlite-data-output.h index e14dbf995..4f6660eb2 100644 --- a/src/contrib/stats/sqlite-data-output.h +++ b/src/contrib/stats/sqlite-data-output.h @@ -48,6 +48,10 @@ namespace ns3 { public: SqliteOutputCallback(Ptr owner, std::string run); + void OutputStatistic(std::string key, + std::string variable, + const StatisticalSummary *statSum); + void OutputSingleton(std::string key, std::string variable, int val); diff --git a/src/contrib/stats/time-data-calculators.cc b/src/contrib/stats/time-data-calculators.cc index 768180862..81596af8f 100644 --- a/src/contrib/stats/time-data-calculators.cc +++ b/src/contrib/stats/time-data-calculators.cc @@ -70,12 +70,12 @@ TimeMinMaxAvgTotalCalculator::Update(const Time i) void TimeMinMaxAvgTotalCalculator::Output(DataOutputCallback &callback) const { - callback.OutputSingleton(m_key, "count", m_count); + callback.OutputSingleton(m_context, m_key + "-count", m_count); if (m_count > 0) { - callback.OutputSingleton(m_key, "total", m_total); - callback.OutputSingleton(m_key, "average", m_total/Scalar(m_count)); - callback.OutputSingleton(m_key, "max", m_max); - callback.OutputSingleton(m_key, "min", m_min); + callback.OutputSingleton(m_context, m_key + "-total", m_total); + callback.OutputSingleton(m_context, m_key + "-average", m_total/Scalar(m_count)); + callback.OutputSingleton(m_context, m_key + "-max", m_max); + callback.OutputSingleton(m_context, m_key + "-min", m_min); } // end TimeMinMaxAvgTotalCalculator::Output }