// $Id$
//
-// Copyright (C) 2008
+// Copyright (C) 2008
// Fraunhofer Institute for Open Communication Systems (FOKUS)
// Competence Center NETwork research (NET), St. Augustin, GERMANY
// Stefan Bund <g0dil@berlios.de>
//#include "Statistics.ih"
// Custom includes
+#include <cmath>
+#include <cstdlib>
#include <sstream>
-#include <senf/Utils/Console/Console.hh>
+#include <senf/Utils/Format.hh>
#include "StatisticsTargets.hh"
//#include "Statistics.mpp"
#define prefix_
-///////////////////////////////cc.p////////////////////////////////////////
+//-/////////////////////////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////
+//-/////////////////////////////////////////////////////////////////////////////////////////////////
// senf::StatisticsBase
-prefix_ void senf::StatisticsBase::enter(float min, float avg, float max)
+prefix_ void senf::StatisticsBase::enter(unsigned n, float min, float avg, float max, float dev)
{
min_ = min;
avg_ = avg;
max_ = max;
- generateOutput();
- signalChildren();
+ dev_ = dev;
+ for (unsigned i (0); i < n; ++i)
+ generateOutput();
+ Children::iterator i (children_.begin());
+ Children::iterator const i_end (children_.end());
+ for (; i != i_end; ++i)
+ i->second.enter(n, min_, avg_, max_, dev_);
}
prefix_ senf::Collector & senf::StatisticsBase::operator[](unsigned rank)
return i->second;
}
+prefix_ senf::Collector const & senf::StatisticsBase::operator[](unsigned rank)
+ const
+{
+ Children::const_iterator i (children_.find(rank));
+ if (i == children_.end())
+ throw InvalidRankException();
+ return i->second;
+}
+
prefix_ senf::Collector & senf::StatisticsBase::collect(unsigned rank)
{
std::pair<Children::iterator, bool> state (
return OutputProxy<StatisticsBase>(this, &(i->second));
}
-//
-// generate an engineering style notation
-//
-char *format_eng( float f)
-{
- static char buf[16];
- if (f > 0) {
- int n = 0;
- while( f >= 1000.0f) {
- f /= 1000.f;
- n+=3;
- }
-
- sprintf( buf, " %3.2fe%+03d", f, n);
- }
- else if (f < 0) {
- int n = 0;
- while( f <= -1000.0f) {
- f *= 1000.f;
- n+=3;
- }
-
- sprintf( buf, "%3.2fe%+03d", f, n);
- }
- else{
- sprintf( buf, " 0.00");
- }
-
- return buf;
-}
-
-
prefix_ void senf::StatisticsBase::consoleList(unsigned level, std::ostream & os)
const
{
- os << boost::format("%s%-5d%|15t| %12s %12s %12s\n")
- % std::string(2*level,' ') % rank() % format_eng(min()) % format_eng(avg()) % format_eng(max());
+ namespace fmt = senf::format;
+
+ os << boost::format("%s%-5d%|15t| %12.5g %19.5g %12.5g\n")
+ % std::string(2*level,' ') % rank()
+ % fmt::eng(min()).setw() % fmt::eng(avg(),dev()).setw() % fmt::eng(max()).setw();
{
OutputMap::const_iterator i (outputs_.begin());
OutputMap::const_iterator i_end (outputs_.end());
for (; i != i_end; ++i)
- os << boost::format(" %3d %12s %12s %12s\n")
- % i->second.n
- % format_eng(i->second.min/i->second.n)
- % format_eng(i->second.avg/i->second.n)
- % format_eng(i->second.max/i->second.n);
+ os << boost::format(" %3d %12.5g %19.5g %12.5g\n")
+ % i->second.n
+ % fmt::eng(i->second.min).setw()
+ % fmt::eng(i->second.avg, i->second.dev).setw()
+ % fmt::eng(i->second.max).setw();
}
{
Children::const_iterator i (children_.begin());
prefix_ void senf::StatisticsBase::generateOutput()
{
- queue_.push_front(QueueEntry(min_, avg_, max_));
+ queue_.push_front(QueueEntry(min_, avg_, max_, dev_));
while (queue_.size() > maxQueueLen_)
queue_.pop_back();
OutputMap::iterator i (outputs_.begin());
OutputMap::iterator const i_end (outputs_.end());
for (; i != i_end; ++i) {
- i->second.min = i->second.avg = i->second.max = 0.0f;
+ i->second.min = i->second.avg = i->second.max = i->second.dev = 0.0f;
Queue::const_iterator j (queue_.begin());
Queue::const_iterator const j_end (queue_.end());
unsigned n (0);
i->second.min += j->min;
i->second.avg += j->avg;
i->second.max += j->max;
+ i->second.dev += j->dev;
}
- i->second.signal(i->second.min/n, i->second.avg/n, i->second.max/n);
+ i->second.min /= n;
+ i->second.avg /= n;
+ i->second.max /= n;
+ i->second.dev /= n;
+ i->second.signal(i->second.min, i->second.avg, i->second.max, i->second.dev);
}
}
-prefix_ void senf::StatisticsBase::signalChildren()
+//-/////////////////////////////////////////////////////////////////////////////////////////////////
+// senf::StatisticsBase::OutputEntry
+
+prefix_ void senf::StatisticsBase::OutputEntry::consoleList(std::ostream & os)
{
- Children::iterator i (children_.begin());
- Children::iterator const i_end (children_.end());
- for (; i != i_end; ++i)
- i->second.enter(min_, avg_, max_);
+ for (boost::ptr_vector<TargetBase>::iterator i (targets_.begin());
+ i != targets_.end(); ++i)
+ if (! i->label.empty())
+ os << i->label << "\n";
}
-///////////////////////////////////////////////////////////////////////////
+//-/////////////////////////////////////////////////////////////////////////////////////////////////
// senf::Statistics
prefix_ senf::Statistics::Statistics()
#endif
{
#ifndef SENF_DISABLE_CONSOLE
- dir.add("list", &Statistics::consoleList)
- .doc("List statistics collection intervals and current values.\n"
- "\n"
- "Columns:\n"
- " RANK Number of values collected. Since the statistics collectors form\n"
- " a tree, the value is indented according to it's tree location.\n"
- " WIN Size of output average window.\n"
- " MIN Last entered minimum value.\n"
- " AVG Last entered average value.\n"
- " MAX Last entered maximum value.");
- dir.add("collect", &Statistics::consoleCollect)
- .doc("Add statistics collection groups. The argument gives a sequence of collector\n"
- "ranks each building on the preceding collector:\n"
- "\n"
- " $ collect (10 60 60)\n"
- "\n"
- "Will start by collecting every 10 values together to a new value. 60 of such\n"
- "combined values will be collected together in the next step again followed by\n"
- "a collection of 60 values. If the statistics is entered with a frequency of\n"
- "10 values per second, this will provide combined statistics over the second,\n"
- "minutes and hours ranges.\n"
- "\n"
- "You may call collect multiple times. Any missing collection ranks will be\n"
- "added.")
- .arg("ranks","chain of collector ranks");
- dir.add("output", &Statistics::consoleOutput)
- .doc("Generate statistics output. This statement will add an additional output\n"
- "generator. This generator will be attached to the collector specified by\n"
- "the {rank} parameter. This parameter is a chain of successive rank values\n"
- "which specifies the exact collector to use. If the collector does not\n"
- "exist, it will be created (this is like automatically calling 'collect'\n"
- "with {rank} as argument).\n"
- "\n"
- "If the output is to be sent somewhere it must be connected to a statistics\n"
- "target.\n"
- "\n"
- "The output may optionally be built using a sliding average over the last\n"
- "{window} values.\n"
- "\n"
- " $ output ()\n"
- "\n"
- "will output the basic statistics value each time a new value is entered.\n"
- "\n"
- " $ output (10 60) 5\n"
- "\n"
- "Assuming that new data values are entered 10 times per second, this command\n"
- "will generate output once every minute. The value will be the average over\n"
- "the last 5 minutes.")
- .arg("rank","Rank chain selecting the value to generate output for")
- .arg("window","Optional size of sliding average window",
- senf::console::kw::default_value = 1u);
+ namespace fty = console::factory;
+
+ dir.add("list", fty::Command(&Statistics::consoleList, this)
+ .doc("List statistics collection intervals and current values.\n"
+ "\n"
+ "Columns:\n"
+ " RANK Number of values collected. Since the statistics collectors form\n"
+ " a tree, the value is indented according to it's tree location.\n"
+ " WIN Size of output average window.\n"
+ " MIN Last entered minimum value.\n"
+ " AVG Last entered average value.\n"
+ " DEV Standard deviation of average value over the collector rank.\n"
+ " MAX Last entered maximum value.") );
+ dir.add("collect", fty::Command(&Statistics::consoleCollect, this)
+ .doc("Add statistics collection groups. The argument gives a sequence of collector\n"
+ "ranks each building on the preceding collector:\n"
+ "\n"
+ " $ collect (10 60 60)\n"
+ "\n"
+ "Will start by collecting every 10 values together to a new value. 60 of such\n"
+ "combined values will be collected together in the next step again followed by\n"
+ "a collection of 60 values. If the statistics is entered with a frequency of\n"
+ "10 values per second, this will provide combined statistics over the second,\n"
+ "minutes and hours ranges.\n"
+ "\n"
+ "You may call collect multiple times. Any missing collection ranks will be\n"
+ "added.")
+ .arg("ranks","chain of collector ranks") );
+ dir.add("output", fty::Command(&Statistics::consoleOutput, this)
+ .doc("Generate statistics output. This statement will add an additional output\n"
+ "generator. This generator will be attached to the collector specified by\n"
+ "the {rank} parameter. This parameter is a chain of successive rank values\n"
+ "which specifies the exact collector to use. If the collector does not\n"
+ "exist, it will be created (this is like automatically calling 'collect'\n"
+ "with {rank} as argument).\n"
+ "\n"
+ "If the output is to be sent somewhere it must be connected to a statistics\n"
+ "target.\n"
+ "\n"
+ "The output may optionally be built using a sliding average over the last\n"
+ "{window} values.\n"
+ "\n"
+ " $ output ()\n"
+ "\n"
+ "will output the basic statistics value each time a new value is entered.\n"
+ "\n"
+ " $ output (10 60) 5\n"
+ "\n"
+ "Assuming that new data values are entered 10 times per second, this command\n"
+ "will generate output once every minute. The value will be the average over\n"
+ "the last 5 minutes.")
+ .arg("rank","Rank chain selecting the value to generate output for")
+ .arg("window","Optional size of sliding average window",
+ console::kw::default_value = 1u) );
#endif
}
prefix_ void senf::Statistics::consoleList(std::ostream & os)
{
- os << "RANK WIN MIN AVG MAX\n";
+ os << "RANK WIN MIN AVG MAX\n";
StatisticsBase::consoleList(0, os);
}
for (; i != i_end; ++i)
stats = & (stats->collect(*i));
-
+
}
prefix_ boost::shared_ptr<senf::console::DirectoryNode>
StatisticsBase * stats (this);
std::vector<unsigned>::const_iterator i (ranks.begin());
std::vector<unsigned>::const_iterator const i_end (ranks.end());
-
+
try {
for (; i != i_end; ++i)
stats = &(*stats)[*i];
for (; i != i_end; ++i)
stats = & (stats->collect(*i));
-
+
return stats->output(window).dir().node().thisptr();
}
return "";
}
-///////////////////////////////////////////////////////////////////////////
+//-/////////////////////////////////////////////////////////////////////////////////////////////////
// senf::Collector
-prefix_ void senf::Collector::enter(float min, float avg, float max)
+prefix_ void senf::Collector::enter(unsigned n, float min, float avg, float max, float dev)
{
- accAvg_ += avg;
if (min < accMin_) accMin_ = min;
if (max > accMax_) accMax_ = max;
- if (++i_ >= rank_) {
- StatisticsBase::enter(accMin_, accAvg_ / i_, accMax_);
- i_ = 0;
+
+ if (i_ + n >= rank_) {
+ accSum_ += (rank_-i_)*avg;
+ accSumSq_ += (rank_-i_)*(rank_-i_)*(avg*avg + dev*dev);
+ float accAvg (accSum_ / rank_);
+ float accDev (std::sqrt(std::max(0.0f,accSumSq_ / rank_ - accAvg*accAvg)));
+ StatisticsBase::enter(1, accMin_, accAvg, accMax_, accDev);
accMin_ = FLT_MAX;
- accAvg_ = 0.0f;
+ accSum_ = 0.0f;
+ accSumSq_ = 0.0f;
accMax_ = -FLT_MAX;
+ n -= (rank_ - i_);
+ i_ = 0;
+
+ if (n >= rank_) {
+ std::div_t d (std::div(int(n), int(rank_)));
+ StatisticsBase::enter(d.quot, min, avg, max, dev);
+ n = d.rem;
+ }
+ }
+
+ if (n>0) {
+ accSum_ += n*avg;
+ accSumSq_ += n*n*(avg*avg+dev*dev);
+ i_ += n;
+ if (min < accMin_) accMin_ = min;
+ if (max > accMax_) accMax_ = max;
}
}
return owner_->path() + "-" + senf::str(rank_);
}
-///////////////////////////////cc.e////////////////////////////////////////
+//-/////////////////////////////////////////////////////////////////////////////////////////////////
#undef prefix_
//#include "Statistics.mpp"