PPI: Missing commit
[senf.git] / Utils / Statistics.cc
1 // $Id$
2 //
3 // Copyright (C) 2008 
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 //     Stefan Bund <g0dil@berlios.de>
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 /** \file
24     \brief Statistics non-inline non-template implementation */
25
26 #include "Statistics.hh"
27 //#include "Statistics.ih"
28
29 // Custom includes
30 #include <sstream>
31 #include "Console/Console.hh"
32 #include "StatisticsTargets.hh"
33
34 //#include "Statistics.mpp"
35 #define prefix_
36 ///////////////////////////////cc.p////////////////////////////////////////
37
38 ///////////////////////////////////////////////////////////////////////////
39 // senf::StatisticsBase
40
41 prefix_ void senf::StatisticsBase::enter(float min, float avg, float max)
42 {
43     min_ = min;
44     avg_ = avg;
45     max_ = max;
46     generateOutput();
47     signalChildren();
48 }
49
50 prefix_ senf::Collector & senf::StatisticsBase::operator[](unsigned rank)
51 {
52     Children::iterator i (children_.find(rank));
53     if (i == children_.end())
54         throw InvalidRankException();
55     return i->second;
56 }
57
58 prefix_ senf::Collector & senf::StatisticsBase::collect(unsigned rank)
59 {
60     std::pair<Children::iterator, bool> state (
61         children_.insert(std::make_pair(rank, Collector(this, rank))) );
62     if (! state.second)
63         throw DuplicateRankException();
64     return state.first->second;
65 }
66
67 prefix_ senf::StatisticsBase::OutputProxy<senf::StatisticsBase>
68 senf::StatisticsBase::output(unsigned n)
69 {
70     OutputMap::iterator i (outputs_.find(n));
71     if (i == outputs_.end()) {
72         i = outputs_.insert(std::make_pair(n, OutputEntry(n))).first;
73         std::stringstream nm;
74         nm << "output" << path() << ":" << n;
75         base().dir.node().add(nm.str(), i->second.dir);
76         detail::StatisticsLoggerRegistry::instance().apply(*this, n, i->second.dir);
77     }
78     if (n > maxQueueLen_)
79         maxQueueLen_ = n;
80     return OutputProxy<StatisticsBase>(this, &(i->second));
81 }
82
83 prefix_ void senf::StatisticsBase::consoleList(unsigned level, std::ostream & os)
84     const
85 {
86     os << boost::format("%s%-5d%|15t|  %12g  %12g  %12g\n") 
87         % std::string(2*level,' ') % rank() % min() % avg() % max();
88     {
89         OutputMap::const_iterator i (outputs_.begin());
90         OutputMap::const_iterator i_end (outputs_.end());
91         for (; i != i_end; ++i)
92             os << boost::format("            %3d  %12g  %12g  %12g\n")
93                 % i->second.n 
94                 % (i->second.min/i->second.n) 
95                 % (i->second.avg/i->second.n)
96                 % (i->second.max/i->second.n);
97     }
98     {
99         Children::const_iterator i (children_.begin());
100         Children::const_iterator const i_end (children_.end());
101         for (; i != i_end; ++i)
102             i->second.consoleList(level+1, os);
103     }
104 }
105
106 prefix_ void senf::StatisticsBase::generateOutput()
107 {
108     queue_.push_front(QueueEntry(min_, avg_, max_));
109     while (queue_.size() > maxQueueLen_)
110         queue_.pop_back();
111
112     OutputMap::iterator i (outputs_.begin());
113     OutputMap::iterator const i_end (outputs_.end());
114     for (; i != i_end; ++i) {
115         i->second.min = i->second.avg = i->second.max = 0.0f;
116         Queue::const_iterator j (queue_.begin());
117         Queue::const_iterator const j_end (queue_.end());
118         unsigned n (0);
119         for (; n < i->second.n && j != j_end; ++n, ++j) {
120             i->second.min += j->min;
121             i->second.avg += j->avg;
122             i->second.max += j->max;
123         }
124         i->second.signal(i->second.min/n, i->second.avg/n, i->second.max/n);
125     }
126 }
127
128 prefix_ void senf::StatisticsBase::signalChildren()
129 {
130     Children::iterator i (children_.begin());
131     Children::iterator const i_end  (children_.end());
132     for (; i != i_end; ++i)
133         i->second.enter(min_, avg_, max_);
134 }
135
136 ///////////////////////////////////////////////////////////////////////////
137 // senf::Statistics
138
139 prefix_ senf::Statistics::Statistics()
140 #ifndef SENF_DISABLE_CONSOLE
141     : dir (this)
142 #endif
143 {
144 #ifndef SENF_DISABLE_CONSOLE
145     dir.add("list", &Statistics::consoleList)
146         .doc("List statistics collection intervals and current values.\n"
147              "\n"
148              "Columns:\n"
149              "    RANK    Number of values collected. Since the statistics collectors form\n"
150              "            a tree, the value is indented according to it's tree location.\n"
151              "    WIN     Size of output average window.\n"
152              "    MIN     Last entered minimum value.\n"
153              "    AVG     Last entered average value.\n"
154              "    MAX     Last entered maximum value.");
155     dir.add("collect", &Statistics::consoleCollect)
156         .doc("Add statistics collection groups. The argument gives a sequence of collector\n"
157              "ranks each building on the preceding collector:\n"
158              "\n"
159              "    $ collect (10 60 60)\n"
160              "\n"
161              "Will start by collecting every 10 values together to a new value. 60 of such\n"
162              "combined values will be collected together in the next step again followed by\n"
163              "a collection of 60 values. If the statistics is entered with a frequency of\n"
164              "10 values per second, this will provide combined statistics over the second,\n"
165              "minutes and hours ranges.\n"
166              "\n"
167              "You may call collect multiple times. Any missing collection ranks will be\n"
168              "added.")
169         .arg("ranks","chain of collector ranks");
170     dir.add("output", &Statistics::consoleOutput)
171         .doc("Generate statistics output. This statement will add an additional output\n"
172              "generator. This generator will be attached to the collector specified by\n"
173              "the {rank} parameter. This parameter is a chain of successive rank values\n"
174              "which specifies the exact collector to use. If the collector does not\n"
175              "exist, it will be created (this is like automatically calling 'collect'\n"
176              "with {rank} as argument).\n"
177              "\n"
178              "If the output is to be sent somewhere it must be connected to a statistics\n"
179              "target.\n"
180              "\n"
181              "The output may optionally be built using a sliding average over the last\n"
182              "{window} values.\n"
183              "\n"
184              "    $ output ()\n"
185              "\n"
186              "will output the basic statistics value each time a new value is entered.\n"
187              "\n"
188              "    $ output (10 60) 5\n"
189              "\n"
190              "Assuming that new data values are entered 10 times per second, this command\n"
191              "will generate output once every minute. The value will be the average over\n"
192              "the last 5 minutes.")
193         .arg("rank","Rank chain selecting the value to generate output for")
194         .arg("window","Optional size of sliding average window",
195              senf::console::kw::default_value = 1u);
196 #endif
197 }
198
199 prefix_ void senf::Statistics::consoleList(std::ostream & os)
200 {
201     os << "RANK        WIN           MIN           AVG           MAX\n";
202     StatisticsBase::consoleList(0, os);
203 }
204
205 prefix_ void senf::Statistics::consoleCollect(std::vector<unsigned> & ranks)
206 {
207     StatisticsBase * stats (this);
208     std::vector<unsigned>::const_iterator i (ranks.begin());
209     std::vector<unsigned>::const_iterator const i_end (ranks.end());
210
211     try {
212         for (; i != i_end; ++i)
213             stats = &(*stats)[*i];
214     }
215     catch (InvalidRankException &) {}
216
217     for (; i != i_end; ++i)
218         stats = & (stats->collect(*i));
219         
220 }
221
222 prefix_  boost::shared_ptr<senf::console::DirectoryNode>
223 senf::Statistics::consoleOutput(std::vector<unsigned> & ranks, unsigned window)
224 {
225     StatisticsBase * stats (this);
226     std::vector<unsigned>::const_iterator i (ranks.begin());
227     std::vector<unsigned>::const_iterator const i_end (ranks.end());
228     
229     try {
230         for (; i != i_end; ++i)
231             stats = &(*stats)[*i];
232     }
233     catch (InvalidRankException &) {}
234
235     for (; i != i_end; ++i)
236         stats = & (stats->collect(*i));
237     
238     return stats->output(window).dir().node().thisptr();
239 }
240
241 prefix_ senf::Statistics & senf::Statistics::v_base()
242 {
243     return *this;
244 }
245
246 prefix_ std::string senf::Statistics::v_path()
247     const
248 {
249     return "";
250 }
251
252 ///////////////////////////////////////////////////////////////////////////
253 // senf::Collector
254
255 prefix_ void senf::Collector::enter(float min, float avg, float max)
256 {
257     accAvg_ += avg;
258     if (min < accMin_) accMin_ = min;
259     if (max > accMax_) accMax_ = max;
260     if (++i_ >= rank_) {
261         StatisticsBase::enter(accMin_, accAvg_ / rank_, accMax_);
262         i_ = 0;
263         accMin_ = FLT_MAX;
264         accAvg_ = 0.0f;
265         accMax_ = -FLT_MAX;
266     }
267 }
268
269 prefix_ senf::Statistics & senf::Collector::v_base()
270 {
271     return owner_->base();
272 }
273
274 prefix_ std::string senf::Collector::v_path()
275     const
276 {
277     return owner_->path() + "-" + senf::str(rank_);
278 }
279
280 ///////////////////////////////cc.e////////////////////////////////////////
281 #undef prefix_
282 //#include "Statistics.mpp"
283
284 \f
285 // Local Variables:
286 // mode: c++
287 // fill-column: 100
288 // comment-column: 40
289 // c-file-style: "senf"
290 // indent-tabs-mode: nil
291 // ispell-local-dictionary: "american"
292 // compile-command: "scons -u test"
293 // End: