Statistics
| Branch: | Revision:

root / src / envir / fileoutscalarmgr.cc @ fbe00e73

History | View | Annotate | Download (8.53 KB)

1
//==========================================================================
2
//  FILEOUTPUTSCALARMGR.CC - part of
3
//                     OMNeT++/OMNEST
4
//            Discrete System Simulation in C++
5
//
6
//  Author: Andras Varga
7
//
8
//==========================================================================
9

    
10
/*--------------------------------------------------------------*
11
  Copyright (C) 1992-2008 Andras Varga
12
  Copyright (C) 2006-2008 OpenSim Ltd.
13

14
  This file is distributed WITHOUT ANY WARRANTY. See the file
15
  `license' for details on this and other legal matters.
16
*--------------------------------------------------------------*/
17

    
18
#include "simkerneldefs.h"
19

    
20
#include <assert.h>
21
#include <string.h>
22
#include <fstream>
23
#include <locale.h>
24
#include "opp_ctype.h"
25
#include "cconfigoption.h"
26
#include "fileutil.h"
27
#include "envirbase.h"
28
#include "csimulation.h"
29
#include "cmodule.h"
30
#include "cstatistic.h"
31
#include "cdensityestbase.h"
32
#include "fileoutscalarmgr.h"
33
#include "ccomponenttype.h"
34
#include "stringutil.h"
35
#include "unitconversion.h"
36

    
37
USING_NAMESPACE
38

    
39
#define SCALAR_FILE_VERSION 2
40
#define DEFAULT_PRECISION  "14"
41

    
42
Register_PerRunConfigOption(CFGID_OUTPUT_SCALAR_FILE, "output-scalar-file", CFG_FILENAME, "${resultdir}/${configname}-${runnumber}.sca", "Name for the output scalar file.");
43
Register_PerRunConfigOption(CFGID_OUTPUT_SCALAR_PRECISION, "output-scalar-precision", CFG_INT, DEFAULT_PRECISION, "The number of significant digits for recording data into the output scalar file. The maximum value is ~15 (IEEE double precision).");
44
Register_PerRunConfigOption(CFGID_OUTPUT_SCALAR_FILE_APPEND, "output-scalar-file-append", CFG_BOOL, "false", "What to do when the output scalar file already exists: append to it (OMNeT++ 3.x behavior), or delete it and begin a new file (default).");
45

    
46
Register_PerObjectConfigOption(CFGID_SCALAR_RECORDING, "scalar-recording", CFG_BOOL, "true", "Whether the matching output scalars should be recorded. Syntax: <module-full-path>.<scalar-name>.scalar-recording=true/false. Example: **.queue.packetsDropped.scalar-recording=true");
47

    
48
Register_Class(cFileOutputScalarManager);
49

    
50
#ifdef CHECK
51
#undef CHECK
52
#endif
53
#define CHECK(fprintf)    if (fprintf<0) throw cRuntimeError("Cannot write output scalar file `%s'", fname.c_str())
54

    
55

    
56
cFileOutputScalarManager::cFileOutputScalarManager()
57
{
58
    f = NULL;
59
    prec = ev.getConfig()->getAsInt(CFGID_OUTPUT_SCALAR_PRECISION);
60
}
61

    
62
cFileOutputScalarManager::~cFileOutputScalarManager()
63
{
64
    closeFile();
65
}
66

    
67
void cFileOutputScalarManager::openFile()
68
{
69
    mkPath(directoryOf(fname.c_str()).c_str());
70
    f = fopen(fname.c_str(),"a");
71
    if (f==NULL)
72
        throw cRuntimeError("Cannot open output scalar file `%s'", fname.c_str());
73
}
74

    
75
void cFileOutputScalarManager::closeFile()
76
{
77
    if (f)
78
    {
79
        fclose(f);
80
        f = NULL;
81
    }
82
}
83

    
84
void cFileOutputScalarManager::startRun()
85
{
86
    // clean up file from previous runs
87
    closeFile();
88
    fname = ev.getConfig()->getAsFilename(CFGID_OUTPUT_SCALAR_FILE).c_str();
89
    dynamic_cast<EnvirBase *>(&ev)->processFileName(fname);
90
    if (ev.getConfig()->getAsBool(CFGID_OUTPUT_SCALAR_FILE_APPEND)==false)
91
        removeFile(fname.c_str(), "old output scalar file");
92
    run.reset();
93
}
94

    
95
void cFileOutputScalarManager::endRun()
96
{
97
    closeFile();
98
}
99

    
100
void cFileOutputScalarManager::init()
101
{
102
    if (!f)
103
    {
104
        openFile();
105
        if (!f) return;
106

    
107
        CHECK(fprintf(f, "version %d\n", SCALAR_FILE_VERSION));
108
    }
109

    
110
    if (!run.initialized)
111
    {
112
        run.initRun();
113

    
114
        // this is the first scalar written in this run, write out run attributes
115
        run.writeRunData(f, fname);
116

    
117
        // save iteration variables
118
        std::vector<const char *> v = ev.getConfigEx()->getIterationVariableNames();
119
        for (int i=0; i<(int)v.size(); i++)
120
        {
121
            const char *name = v[i];
122
            const char *value = ev.getConfigEx()->getVariable(v[i]);
123
            recordNumericIterationVariable(name, value);
124
        }
125
    }
126
}
127

    
128
void cFileOutputScalarManager::recordNumericIterationVariable(const char *name, const char *value)
129
{
130
    char *e;
131
    setlocale(LC_NUMERIC, "C");
132
    (void) strtod(value, &e);
133
    if (*e=='\0')
134
    {
135
        // plain number - just record as it is
136
        //XXX write with using an "itervar" keyword not "scalar" (needs to be understood by IDE as well)
137
        CHECK(fprintf(f, "scalar . \t%s \t%s\n", name, value));
138
    }
139
    else if (e!=value)
140
    {
141
        // starts with a number, so it might be something like "100s"; if so, record it as scalar with "unit" attribute
142
        double d;
143
        std::string unit;
144
        bool parsedOK = false;
145
        try {
146
            d = UnitConversion::parseQuantity(value, unit);
147
            parsedOK = true;
148
        } catch (std::exception& e) { }
149

    
150
        if (parsedOK)
151
        {
152
            CHECK(fprintf(f, "scalar . \t%s \t%.*g\n", name, prec, d));
153
            if (!unit.empty())
154
                CHECK(fprintf(f,"attr unit  %s\n", QUOTE(unit.c_str())));
155
        }
156
    }
157
}
158

    
159
void cFileOutputScalarManager::recordScalar(cComponent *component, const char *name, double value, opp_string_map *attributes)
160
{
161
    if (!run.initialized)
162
        init();
163
    if (!f)
164
        return;
165

    
166
    if (!name || !name[0])
167
        name = "(unnamed)";
168

    
169
    bool enabled = ev.getConfig()->getAsBool((component->getFullPath()+"."+name).c_str(), CFGID_SCALAR_RECORDING);
170
    if (enabled)
171
    {
172
        CHECK(fprintf(f, "scalar %s \t%s \t%.*g\n", QUOTE(component->getFullPath().c_str()), QUOTE(name), prec, value));
173
        if (attributes)
174
            for (opp_string_map::iterator it=attributes->begin(); it!=attributes->end(); it++)
175
                CHECK(fprintf(f,"attr %s  %s\n", QUOTE(it->first.c_str()), QUOTE(it->second.c_str())));
176
    }
177
}
178

    
179
void cFileOutputScalarManager::recordStatistic(cComponent *component, const char *name, cStatistic *statistic, opp_string_map *attributes)
180
{
181
    if (!run.initialized)
182
        init();
183
    if (!f)
184
        return;
185

    
186
    if (!name)
187
        name = statistic->getFullName();
188
    if (!name || !name[0])
189
        name = "(unnamed)";
190

    
191
    // check that recording this statistic is not disabled as a whole
192
    std::string objectFullPath = component->getFullPath() + "." + name;
193
    bool enabled = ev.getConfig()->getAsBool(objectFullPath.c_str(), CFGID_SCALAR_RECORDING);
194
    if (!enabled)
195
        return;
196

    
197
    // file format:
198
    //   statistic <modulepath> <statisticname>
199
    //   field count 343
200
    //   field weights 343
201
    //   field mean    2.1233
202
    //   field stddev  1.345
203
    //   attr unit s
204
    //   bin 0  3
205
    //   bin 10 13
206
    //   bin 20 19
207
    //   ...
208
    // In Scave, fields are read as separate scalars.
209
    CHECK(fprintf(f, "statistic %s \t%s\n", QUOTE(component->getFullPath().c_str()), QUOTE(name)));
210
    writeStatisticField("count", statistic->getCount());
211
    writeStatisticField("mean", statistic->getMean());
212
    writeStatisticField("stddev", statistic->getStddev());
213
    writeStatisticField("sum", statistic->getSum());
214
    writeStatisticField("sqrsum", statistic->getSqrSum());
215
    writeStatisticField("min", statistic->getMin());
216
    writeStatisticField("max", statistic->getMax());
217
    if (statistic->isWeighted())
218
    {
219
        writeStatisticField("weights", statistic->getWeights());
220
        writeStatisticField("weightedSum", statistic->getWeightedSum());
221
        writeStatisticField("sqrSumWeights", statistic->getSqrSumWeights());
222
        writeStatisticField("weightedSqrSum", statistic->getWeightedSqrSum());
223
    }
224

    
225
    if (attributes)
226
        for (opp_string_map::iterator it=attributes->begin(); it!=attributes->end(); it++)
227
            CHECK(fprintf(f,"attr %s  %s\n", QUOTE(it->first.c_str()), QUOTE(it->second.c_str())));
228

    
229
    if (dynamic_cast<cDensityEstBase *>(statistic))
230
    {
231
        // check that recording the histogram is enabled
232
        bool enabled = ev.getConfig()->getAsBool((objectFullPath+":histogram").c_str(), CFGID_SCALAR_RECORDING);
233
        if (enabled)
234
        {
235
            cDensityEstBase *hist = (cDensityEstBase *)statistic;
236
            if (!hist->isTransformed())
237
                hist->transform();
238

    
239
            int n = hist->getNumCells();
240
            if (n>0)
241
            {
242
                CHECK(fprintf(f, "bin\t-INF\t%lu\n", hist->getUnderflowCell()));
243
                for (int i=0; i<n; i++)
244
                    CHECK(fprintf(f, "bin\t%.*g\t%.*g\n", prec, hist->getBasepoint(i), prec, hist->getCellValue(i)));
245
                CHECK(fprintf(f, "bin\t%.*g\t%lu\n", prec, hist->getBasepoint(n), hist->getOverflowCell()));
246
            }
247
        }
248
    }
249
}
250

    
251
const char *cFileOutputScalarManager::getFileName() const
252
{
253
    return fname.c_str();
254
}
255

    
256
void cFileOutputScalarManager::flush()
257
{
258
    if (f)
259
        fflush(f);
260
}
261