Statistics
| Branch: | Revision:

root / src / envir / envirbase.cc @ 08285dff

History | View | Annotate | Download (66.2 KB)

1 01873262 Georg Kunz
//==========================================================================
2
//  ENVIRBASE.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 <stdio.h>
19
#include <assert.h>
20
#include <fstream>
21 79b044ca Georg Kunz
#include <iomanip>
22 01873262 Georg Kunz
#include <set>
23
24
#include "args.h"
25
#include "envirbase.h"
26
#include "appreg.h"
27
#include "ccoroutine.h"
28
#include "csimulation.h"
29
#include "cscheduler.h"
30
#include "cpar.h"
31
#include "cproperties.h"
32
#include "cproperty.h"
33
#include "random.h"
34
#include "crng.h"
35
#include "cmodule.h"
36
#include "ccomponenttype.h"
37
#include "cxmlelement.h"
38
#include "cxmldoccache.h"
39
#include "chistogram.h"
40
#include "stringtokenizer.h"
41
#include "fnamelisttokenizer.h"
42
#include "chasher.h"
43
#include "cconfigoption.h"
44
#include "cmathfunction.h"
45
#include "cnedfunction.h"
46
#include "../nedxml/nedparser.h" // NEDParser::getBuiltInDeclarations()
47
#include "regmacros.h"
48
#include "stringutil.h"
49
#include "enumstr.h"
50
#include "simtime.h"
51
#include "resultrecorder.h"
52
#include "resultfilters.h"
53
#include "statisticparser.h"
54
55
#ifdef WITH_PARSIM
56
#include "cparsimcomm.h"
57
#include "parsim/cparsimpartition.h"
58
#include "parsim/cparsimsynchr.h"
59
#include "parsim/creceivedexception.h"
60
#endif
61
62
#include "opp_ctype.h"
63
#include "stringtokenizer.h"
64
#include "fileglobber.h"
65
#include "unitconversion.h"
66
#include "commonutil.h"
67
#include "../common/ver.h"
68
69
#include "timeutil.h"
70
#include "platmisc.h"
71
#include "fileutil.h"  // splitFileName
72
73
74
#ifdef USE_PORTABLE_COROUTINES /* coroutine stacks reside in main stack area */
75
76
# define TOTAL_STACK_SIZE   (2*1024*1024)
77
# define MAIN_STACK_SIZE       (128*1024)  // Tkenv needs more than 64K
78
79
#else /* nonportable coroutines, stacks are allocated on heap */
80
81
# define TOTAL_STACK_SIZE        0  // dummy value
82
# define MAIN_STACK_SIZE         0  // dummy value
83
84
#endif
85
86
87
USING_NAMESPACE
88
89
using std::ostream;
90
91
#define CREATE_BY_CLASSNAME(var,classname,baseclass,description) \
92
     baseclass *var ## _tmp = (baseclass *) createOne(classname); \
93
     var = dynamic_cast<baseclass *>(var ## _tmp); \
94
     if (!var) \
95
         throw cRuntimeError("Class \"%s\" is not subclassed from " #baseclass, (const char *)classname);
96
97
Register_GlobalConfigOptionU(CFGID_TOTAL_STACK, "total-stack", "B", NULL, "Specifies the maximum memory for activity() simple module stacks. You need to increase this value if you get a ``Cannot allocate coroutine stack'' error.");
98
Register_GlobalConfigOption(CFGID_PARALLEL_SIMULATION, "parallel-simulation", CFG_BOOL, "false", "Enables parallel distributed simulation.");
99
Register_GlobalConfigOption(CFGID_SCHEDULER_CLASS, "scheduler-class", CFG_STRING, "cSequentialScheduler", "Part of the Envir plugin mechanism: selects the scheduler class. This plugin interface allows for implementing real-time, hardware-in-the-loop, distributed and distributed parallel simulation. The class has to implement the cScheduler interface.");
100
Register_GlobalConfigOption(CFGID_PARSIM_COMMUNICATIONS_CLASS, "parsim-communications-class", CFG_STRING, "cFileCommunications", "If parallel-simulation=true, it selects the class that implements communication between partitions. The class must implement the cParsimCommunications interface.");
101
Register_GlobalConfigOption(CFGID_PARSIM_SYNCHRONIZATION_CLASS, "parsim-synchronization-class", CFG_STRING, "cNullMessageProtocol", "If parallel-simulation=true, it selects the parallel simulation algorithm. The class must implement the cParsimSynchronizer interface.");
102
Register_GlobalConfigOption(CFGID_OUTPUTVECTORMANAGER_CLASS, "outputvectormanager-class", CFG_STRING, "cIndexedFileOutputVectorManager", "Part of the Envir plugin mechanism: selects the output vector manager class to be used to record data from output vectors. The class has to implement the cOutputVectorManager interface.");
103
Register_GlobalConfigOption(CFGID_OUTPUTSCALARMANAGER_CLASS, "outputscalarmanager-class", CFG_STRING, "cFileOutputScalarManager", "Part of the Envir plugin mechanism: selects the output scalar manager class to be used to record data passed to recordScalar(). The class has to implement the cOutputScalarManager interface.");
104
Register_GlobalConfigOption(CFGID_SNAPSHOTMANAGER_CLASS, "snapshotmanager-class", CFG_STRING, "cFileSnapshotManager", "Part of the Envir plugin mechanism: selects the class to handle streams to which snapshot() writes its output. The class has to implement the cSnapshotManager interface.");
105
Register_GlobalConfigOption(CFGID_FNAME_APPEND_HOST, "fname-append-host", CFG_BOOL, NULL, "Turning it on will cause the host name and process Id to be appended to the names of output files (e.g. omnetpp.vec, omnetpp.sca). This is especially useful with distributed simulation. The default value is true if parallel simulation is enabled, false otherwise.");
106
Register_GlobalConfigOption(CFGID_DEBUG_ON_ERRORS, "debug-on-errors", CFG_BOOL, "false", "When set to true, runtime errors will cause the simulation program to break into the C++ debugger (if the simulation is running under one, or just-in-time debugging is activated). Once in the debugger, you can view the stack trace or examine variables.");
107
Register_GlobalConfigOption(CFGID_PRINT_UNDISPOSED, "print-undisposed", CFG_BOOL, "true", "Whether to report objects left (that is, not deallocated by simple module destructors) after network cleanup.");
108
Register_GlobalConfigOption(CFGID_SIMTIME_SCALE, "simtime-scale", CFG_INT, "-12", "Sets the scale exponent, and thus the resolution of time for the 64-bit fixed-point simulation time representation. Accepted values are -18..0; for example, -6 selects microsecond resolution. -12 means picosecond resolution, with a maximum simtime of ~110 days.");
109
Register_GlobalConfigOption(CFGID_NED_PATH, "ned-path", CFG_PATH, "", "A semicolon-separated list of directories. The directories will be regarded as roots of the NED package hierarchy, and all NED files will be loaded from their subdirectory trees. This option is normally left empty, as the OMNeT++ IDE sets the NED path automatically, and for simulations started outside the IDE it is more convenient to specify it via a command-line option or the NEDPATH environment variable.");
110 68da4f12 Georg Kunz
Register_GlobalConfigOption(CFGID_WRITE_RUNTIME_TO_FILE, "write-runtime-to-file", CFG_BOOL, "false", "Enables or disables writing of the simulation runtime to file.");
111
Register_GlobalConfigOption(CFGID_WRITE_RUNTIME_TO_FILENAME, "write-runtime-to-filename", CFG_STRING, "runtimes.dat", "Defines the name of the file in which the runtimes are stored.");
112 01873262 Georg Kunz
113
Register_PerRunConfigOption(CFGID_NETWORK, "network", CFG_STRING, NULL, "The name of the network to be simulated.  The package name can be omitted if the ini file is in the same directory as the NED file that contains the network.");
114
Register_PerRunConfigOption(CFGID_WARNINGS, "warnings", CFG_BOOL, "true", "Enables warnings.");
115
Register_PerRunConfigOptionU(CFGID_SIM_TIME_LIMIT, "sim-time-limit", "s", NULL, "Stops the simulation when simulation time reaches the given limit. The default is no limit.");
116
Register_PerRunConfigOptionU(CFGID_CPU_TIME_LIMIT, "cpu-time-limit", "s", NULL, "Stops the simulation when CPU usage has reached the given limit. The default is no limit.");
117
Register_PerRunConfigOptionU(CFGID_WARMUP_PERIOD, "warmup-period", "s", NULL, "Length of the initial warm-up period. When set, results belonging to the first x seconds of the simulation will not be recorded into output vectors, and will not be counted into output scalars (see option **.result-recording-modes). This option is useful for steady-state simulations. The default is 0s (no warmup period). Note that models that compute and record scalar results manually (via recordScalar()) will not automatically obey this setting.");
118
Register_PerRunConfigOption(CFGID_FINGERPRINT, "fingerprint", CFG_STRING, NULL, "The expected fingerprint of the simulation. When provided, a fingerprint will be calculated from the simulation event times and other quantities during simulation, and checked against the given one. Fingerprints are suitable for crude regression tests. As fingerprints occasionally differ across platforms, more than one fingerprint values can be specified here, separated by spaces, and a match with any of them will be accepted. To calculate the initial fingerprint, enter any dummy string (such as \"none\"), and run the simulation.");
119
Register_PerRunConfigOption(CFGID_NUM_RNGS, "num-rngs", CFG_INT, "1", "The number of random number generators.");
120
Register_PerRunConfigOption(CFGID_RNG_CLASS, "rng-class", CFG_STRING, "cMersenneTwister", "The random number generator class to be used. It can be `cMersenneTwister', `cLCG32', `cAkaroaRNG', or you can use your own RNG class (it must be subclassed from cRNG).");
121
Register_PerRunConfigOption(CFGID_SEED_SET, "seed-set", CFG_INT, "${runnumber}", "Selects the kth set of automatic random number seeds for the simulation. Meaningful values include ${repetition} which is the repeat loop counter (see repeat= key), and ${runnumber}.");
122
Register_PerRunConfigOption(CFGID_RESULT_DIR, "result-dir", CFG_STRING, "results", "Value for the ${resultdir} variable, which is used as the default directory for result files (output vector file, output scalar file, eventlog file, etc.)");
123
Register_PerRunConfigOption(CFGID_RECORD_EVENTLOG, "record-eventlog", CFG_BOOL, "false", "Enables recording an eventlog file, which can be later visualized on a sequence chart. See eventlog-file= option too.");
124
Register_PerRunConfigOption(CFGID_DEBUG_STATISTICS_RECORDING, "debug-statistics-recording", CFG_BOOL, "false", "Turns on the printing of debugging information related to statistics recording (@statistic properties)");
125
Register_PerObjectConfigOption(CFGID_PARTITION_ID, "partition-id", CFG_STRING, NULL, "With parallel simulation: in which partition the module should be instantiated. Specify numeric partition ID, or a comma-separated list of partition IDs for compound modules that span across multiple partitions. Ranges (\"5..9\") and \"*\" (=all) are accepted too.");
126
Register_PerObjectConfigOption(CFGID_RNG_K, "rng-%", CFG_INT, "", "Maps a module-local RNG to one of the global RNGs. Example: **.gen.rng-1=3 maps the local RNG 1 of modules matching `**.gen' to the global RNG 3. The default is one-to-one mapping.");
127
Register_PerObjectConfigOption(CFGID_RESULT_RECORDING_MODES, "result-recording-modes", CFG_STRING, "default", "Defines how to calculate results from the @statistic property matched by the wildcard. Special values: default, all: they select the modes listed in the record= key of @statistic; all selects all of them, default selects the non-optional ones (i.e. excludes the ones that end in a question mark). Example values: vector, count, last, sum, mean, min, max, timeavg, stats, histogram. More than one values are accepted, separated by commas. Expressions are allowed. Items prefixed with '-' get removed from the list. Example: **.queueLength.result-recording-modes=default,-vector,+timeavg");
128
129
// the following options are declared in other files
130
extern cConfigOption *CFGID_SCALAR_RECORDING;
131
extern cConfigOption *CFGID_VECTOR_RECORDING;
132
133
134
#define STRINGIZE0(x) #x
135
#define STRINGIZE(x) STRINGIZE0(x)
136
137
static const char *compilerInfo =
138
    #if defined __GNUC__
139
    "GCC " __VERSION__
140
    #elif defined _MSC_VER
141
    "Microsoft Visual C++ version " STRINGIZE(_MSC_VER)
142
    #elif defined __INTEL_COMPILER
143
    "Intel Compiler version " STRINGIZE(__INTEL_COMPILER)
144
    #else
145
    "unknown-compiler"
146
    #endif
147
    ;
148
149
static const char *buildInfoFormat =
150
    "%d-bit"
151
152
    #ifdef NDEBUG
153
    " RELEASE"
154
    #else
155
    " DEBUG"
156
    #endif
157
158
    " simtime_t=%s"
159
    " large-file-support=%s"
160
    ;
161
162
static const char *buildOptions = ""
163
    #ifdef USE_DOUBLE_SIMTIME
164
    " USE_DOUBLE_SIMTIME"
165
    #endif
166
167
    #ifdef WITHOUT_CPACKET
168
    " WITHOUT_CPACKET"
169
    #endif
170
171
    #ifdef WITH_PARSIM
172
    " WITH_PARSIM"
173
    #endif
174
175
    #ifdef WITH_MPI
176
    " WITH_MPI"
177
    #endif
178
179
    #ifdef WITH_NETBUILDER
180
    " WITH_NETBUILDER"
181
    #endif
182
183
    #ifdef WITH_AKAROA
184
    " WITH_AKAROA"
185
    #endif
186
187
    #ifdef XMLPARSER
188
    " XMLPARSER=" STRINGIZE(XMLPARSER)
189
    #endif
190
    ;
191
192
193
EnvirBase::EnvirBase()
194
{
195
    args = NULL;
196
    cfg = NULL;
197
    xmlcache = NULL;
198
199
    record_eventlog = false;
200
    eventlogmgr = NULL;
201
    outvectormgr = NULL;
202
    outscalarmgr = NULL;
203
    snapshotmgr = NULL;
204
205 08285dff Mirko Stoffers
    num_rngs_per_module = 0;
206 01873262 Georg Kunz
207
#ifdef WITH_PARSIM
208
    parsimcomm = NULL;
209
    parsimpartition = NULL;
210
#endif
211
212
    exitcode = 0;
213
}
214
215
EnvirBase::~EnvirBase()
216
{
217
    delete args;
218
    delete cfg;
219
    delete xmlcache;
220
221
    delete outvectormgr;
222
    delete outscalarmgr;
223
    delete snapshotmgr;
224
225 08285dff Mirko Stoffers
    for (int i = 0; i < rngs.size(); i++)
226 01873262 Georg Kunz
         delete rngs[i];
227
228
#ifdef WITH_PARSIM
229
    delete parsimcomm;
230
    delete parsimpartition;
231
#endif
232
}
233
234
int EnvirBase::run(int argc, char *argv[], cConfiguration *configobject)
235
{
236
    args = new ArgList();
237
    args->parse(argc, argv, "h?f:u:l:c:r:p:n:x:agGv");  //TODO share spec with startup.cc!
238
    cfg = dynamic_cast<cConfigurationEx *>(configobject);
239
    if (!cfg)
240
        throw cRuntimeError("Cannot cast configuration object %s to cConfigurationEx", configobject->getClassName());
241
242
    if (simulationRequired())
243
    {
244
        if (setup())
245
            run();
246
        shutdown();
247
    }
248
    return exitcode;
249
}
250
251
bool EnvirBase::simulationRequired()
252
{
253
    // handle -h and -v command-line options
254
    if (args->optionGiven('h'))
255
    {
256
        const char *category = args->optionValue('h',0);
257
        if (!category)
258
            printHelp();
259
        else
260
            dumpComponentList(category);
261
        return false;
262
    }
263
264
    if (args->optionGiven('v'))
265
    {
266
        struct opp_stat_t statbuf;
267
        ev << "\n";
268
        ev << "Build: " OMNETPP_RELEASE " " OMNETPP_BUILDID << "\n";
269
        ev << "Compiler: " << compilerInfo << "\n";
270
        ev << "Options: " << opp_stringf(buildInfoFormat,
271
                                         8*sizeof(void*),
272
                                         opp_typename(typeid(simtime_t)),
273
                                         sizeof(statbuf.st_size)>=8 ? "yes" : "no");
274
        ev << buildOptions << "\n";
275
        return false;
276
    }
277
278
    cConfigurationEx *cfg = getConfigEx();
279
280
    // -a option: print all config names, and number of runs in them
281
    if (args->optionGiven('a'))
282
    {
283
        ev.printf("\n");
284
        std::vector<std::string> configNames = cfg->getConfigNames();
285
        for (int i=0; i<(int)configNames.size(); i++)
286
            ev.printf("Config %s: %d\n", configNames[i].c_str(), cfg->getNumRunsInConfig(configNames[i].c_str()));
287
        return false;
288
    }
289
290
    // '-x' option: print number of runs in the given config, and exit (overrides configname)
291
    const char *configToPrint = args->optionValue('x');
292
    if (configToPrint)
293
    {
294
        //
295
        // IMPORTANT: the simulation launcher will parse the output of this
296
        // option, so it should be modified with care and the two kept in sync
297
        // (see OmnetppLaunchUtils.getSimulationRunInfo()).
298
        //
299
        // Rules:
300
        // (1) the number of runs should appear on the rest of the line
301
        //     after the "Number of runs:" text
302
        // (2) per-run information lines should span from the "Number of runs:"
303
        //     line until the next blank line ("\n\n").
304
        //
305
306
        // '-g'/'-G' options: modifies -x: print unrolled config, iteration variables, etc as well
307
        bool unrollBrief = args->optionGiven('g');
308
        bool unrollDetailed = args->optionGiven('G');
309
310
        ev.printf("\n");
311
        ev.printf("Config: %s\n", configToPrint);
312
        ev.printf("Number of runs: %d\n", cfg->getNumRunsInConfig(configToPrint));
313
314
        if (unrollBrief || unrollDetailed)
315
        {
316
            std::vector<std::string> runs = cfg->unrollConfig(configToPrint, unrollDetailed);
317
            const char *fmt = unrollDetailed ? "Run %d:\n%s" : "Run %d: %s\n";
318
            for (int i=0; i<(int)runs.size(); i++)
319
                ev.printf(fmt, i, runs[i].c_str());
320
        }
321
        return false;
322
    }
323
324
    return true;
325
}
326
327
bool EnvirBase::setup()
328
{
329
    try
330
    {
331
        // ensure correct numeric format in output files
332
        setPosixLocale();
333
334
        // set opt_* variables from ini file(s)
335
        readOptions();
336
337
        // initialize coroutine library
338
        if (TOTAL_STACK_SIZE!=0 && opt_total_stack<=MAIN_STACK_SIZE+4096)
339
        {
340
            ev.printf("Total stack size %d increased to %d\n", opt_total_stack, MAIN_STACK_SIZE);
341
            opt_total_stack = MAIN_STACK_SIZE+4096;
342
        }
343
        cCoroutine::init(opt_total_stack, MAIN_STACK_SIZE);
344
345
        // install XML document cache
346
        xmlcache = new cXMLDocCache();
347
348
        // install output vector manager
349
        CREATE_BY_CLASSNAME(outvectormgr, opt_outputvectormanager_class.c_str(), cOutputVectorManager, "output vector manager");
350
351
        // install output scalar manager
352
        CREATE_BY_CLASSNAME(outscalarmgr, opt_outputscalarmanager_class.c_str(), cOutputScalarManager, "output scalar manager");
353
354
        // install snapshot manager
355
        CREATE_BY_CLASSNAME(snapshotmgr, opt_snapshotmanager_class.c_str(), cSnapshotManager, "snapshot manager");
356
357
        // set up for sequential or distributed execution
358
        if (!opt_parsim)
359
        {
360
            // sequential
361
            cScheduler *scheduler;
362
            CREATE_BY_CLASSNAME(scheduler, opt_scheduler_class.c_str(), cScheduler, "event scheduler");
363 c87b95b0 Simon Tenbusch
            simulation.setScheduler(scheduler);
364 01873262 Georg Kunz
        }
365
        else
366
        {
367
#ifdef WITH_PARSIM
368
            // parsim: create components
369
            CREATE_BY_CLASSNAME(parsimcomm, opt_parsimcomm_class.c_str(), cParsimCommunications, "parallel simulation communications layer");
370
            parsimpartition = new cParsimPartition();
371
            cParsimSynchronizer *parsimsynchronizer;
372
            CREATE_BY_CLASSNAME(parsimsynchronizer, opt_parsimsynch_class.c_str(), cParsimSynchronizer, "parallel simulation synchronization layer");
373
374
            // wire them together (note: 'parsimsynchronizer' is also the scheduler for 'simulation')
375
            parsimpartition->setContext(&simulation, parsimcomm, parsimsynchronizer);
376
            parsimsynchronizer->setContext(&simulation, parsimpartition, parsimcomm);
377
            simulation.setScheduler(parsimsynchronizer);
378
379
            // initialize them
380
            parsimcomm->init();
381
#else
382
            throw cRuntimeError("Parallel simulation is turned on in the ini file, but OMNeT++ was compiled without parallel simulation support (WITH_PARSIM=no)");
383
#endif
384
        }
385
386
        // load NED files from folders on the NED path. Note: NED path is taken
387
        // from the "-n" command-line option or the NEDPATH variable ("-n" takes
388
        // precedence), and the "ned-path=" config entry gets appended to it.
389
        // If the result is still empty, we fall back to "." -- this is needed
390
        // for single-directory models to work
391
        const char *nedpath1 = args->optionValue('n',0);
392
        if (!nedpath1)
393
            nedpath1 = getenv("NEDPATH");
394
        std::string nedpath2 = getConfig()->getAsPath(CFGID_NED_PATH);
395
        std::string nedpath = opp_join(";", nedpath1, nedpath2.c_str());
396
        if (nedpath.empty())
397
            nedpath = ".";
398
399
        StringTokenizer tokenizer(nedpath.c_str(), PATH_SEPARATOR);
400
        std::set<std::string> foldersloaded;
401
        while (tokenizer.hasMoreTokens())
402
        {
403
            const char *folder = tokenizer.nextToken();
404
            if (foldersloaded.find(folder)==foldersloaded.end())
405
            {
406
                ev.printf("Loading NED files from %s:", folder); ev.flush();
407
                int count = simulation.loadNedSourceFolder(folder);
408
                ev.printf(" %d\n", count);
409
                foldersloaded.insert(folder);
410
            }
411
        }
412
        simulation.doneLoadingNedFiles();
413
    }
414
    catch (std::exception& e)
415
    {
416
        displayException(e);
417
        return false; // don't run the app
418
    }
419
    return true;
420
}
421
422
void EnvirBase::printHelp()
423
{
424
    ev << "\n";
425
    ev << "Command line options:\n";
426
    ev << "  <inifile> or -f <inifile>\n";
427
    ev << "                Use the given ini file instead of omnetpp.ini. More than one\n";
428
    ev << "                ini files can be loaded this way.\n";
429
    ev << "  -u <ui>       Selects the user interface. Standard choices are Cmdenv\n";
430
    ev << "                and Tkenv. To make a user interface available, you need\n";
431
    ev << "                to link the simulation executable with the Cmdenv/Tkenv\n";
432
    ev << "                library, or load it as shared library via the -l option.\n";
433
    ev << "  -n <nedpath>  When present, overrides the NEDPATH environment variable.\n";
434
    ev << "  -l <library>  Load the specified shared library (.so or .dll) on startup.\n";
435
    ev << "                The file name should be given without the .so or .dll suffix\n";
436
    ev << "                (it will be appended automatically.) The loaded module may\n";
437
    ev << "                contain simple modules, plugins, etc. Multiple -l options\n";
438
    ev << "                can be present.\n";
439
    ev << "  --<configuration-key>=<value>\n";
440
    ev << "                Any configuration option can be specified on the command\n";
441
    ev << "                line, and it takes precedence over settings specified in the\n";
442
    ev << "                ini file(s). Examples:\n";
443
    ev << "                      --debug-on-errors=true\n";
444
    ev << "                      --record-eventlog=true\n";
445
    ev << "                      --sim-time-limit=1000s\n";
446
    ev << "  -v            Print version and build info.\n";
447
    ev << "  -h            Print this help and exit.\n";
448
    ev << "  -h <category> Lists registered components:\n";
449
    ev << "    -h config         Prints the list of available config options\n";
450
    ev << "    -h configdetails  Prints the list of available config options, with\n";
451
    ev << "                      their documentation\n";
452
    ev << "    -h userinterfaces Lists available user interfaces (see -u option)\n";
453
    ev << "    -h classes        Lists registered C++ classes (including module classes)\n";
454
    ev << "    -h classdesc      Lists C++ classes that have associated reflection\n";
455
    ev << "                      information (needed for Tkenv inspectors)\n";
456
    ev << "    -h nedfunctions   Lists registered NED functions\n";
457
    ev << "    -h neddecls       Lists built-in NED component declarations\n";
458
    ev << "    -h units          Lists recognized physical units\n";
459
    ev << "    -h enums          Lists registered enums\n";
460
    ev << "    -h resultfilters  Lists result filters\n";
461
    ev << "    -h resultrecorders Lists result recorders\n";
462
    ev << "    -h all            Union of all the above\n";
463
    ev << "\n";
464
465
    // print specific help for each user interface
466
    cRegistrationList *table = omnetapps.getInstance();
467
    table->sort();
468
    for (int i=0; i<table->size(); i++)
469
    {
470
        // instantiate the ui, call printUISpecificHelp(), then dispose.
471
        // note: their ctors are not supposed to do anything but trivial member initializations
472
        cOmnetAppRegistration *appreg = check_and_cast<cOmnetAppRegistration *>(table->get(i));
473
        cEnvir *app = appreg->createOne();
474
        if (dynamic_cast<EnvirBase *>(app))
475
            ((EnvirBase *)app)->printUISpecificHelp();
476
        delete app;
477
    }
478
}
479
480
void EnvirBase::dumpComponentList(const char *category)
481
{
482
    bool wantAll = !strcmp(category, "all");
483
    bool processed = false;
484
    if (wantAll || !strcmp(category, "config") || !strcmp(category, "configdetails"))
485
    {
486
        processed = true;
487
        ev << "Supported configuration options:\n";
488
        bool printDescriptions = strcmp(category, "configdetails")==0;
489
490
        cRegistrationList *table = configOptions.getInstance();
491
        table->sort();
492
        for (int i=0; i<table->size(); i++)
493
        {
494
            cConfigOption *obj = dynamic_cast<cConfigOption *>(table->get(i));
495
            ASSERT(obj);
496
            if (!printDescriptions) ev << "  ";
497
            if (obj->isPerObject()) ev << "<object-full-path>.";
498
            ev << obj->getName() << "=";
499
            ev << "<" << cConfigOption::getTypeName(obj->getType()) << ">";
500
            if (obj->getUnit())
501
                ev << ", unit=\"" << obj->getUnit() << "\"";
502
            if (obj->getDefaultValue())
503
                ev << ", default:" << obj->getDefaultValue() << "";
504
            ev << "; " << (obj->isGlobal() ? "global" : obj->isPerObject() ? "per-object" : "per-run") << " setting";
505
            ev << "\n";
506
            if (printDescriptions && !opp_isempty(obj->getDescription()))
507
                ev << opp_indentlines(opp_breaklines(obj->getDescription(),75).c_str(), "    ") << "\n";
508
            if (printDescriptions) ev << "\n";
509
        }
510
        ev << "\n";
511
512
        ev << "Predefined variables that can be used in config values:\n";
513
        std::vector<const char *> v = getConfigEx()->getPredefinedVariableNames();
514
        for (int i=0; i<(int)v.size(); i++)
515
        {
516
            if (!printDescriptions) ev << "  ";
517
            ev << "${" << v[i] << "}\n";
518
            const char *desc = getConfigEx()->getVariableDescription(v[i]);
519
            if (printDescriptions && !opp_isempty(desc))
520
                ev << opp_indentlines(opp_breaklines(desc,75).c_str(), "    ") << "\n";
521
        }
522
        ev << "\n";
523
    }
524
    if (!strcmp(category, "jconfig")) // internal undocumented option, for maintenance purposes
525
    {
526
        // generate Java code for ConfigurationRegistry.java in the IDE
527
        processed = true;
528
        ev << "Supported configuration options (as Java code):\n";
529
        cRegistrationList *table = configOptions.getInstance();
530
        table->sort();
531
        for (int i=0; i<table->size(); i++)
532
        {
533
            cConfigOption *key = dynamic_cast<cConfigOption *>(table->get(i));
534
            ASSERT(key);
535
536
            std::string id = "CFGID_";
537
            for (const char *s = key->getName(); *s; s++)
538
                id.append(1, opp_isalpha(*s) ? opp_toupper(*s) : *s=='-' ? '_' : *s=='%' ? 'n' : *s);
539
            const char *method = key->isGlobal() ? "addGlobalOption" :
540
                                 !key->isPerObject() ? "addPerRunOption" :
541
                                 "addPerObjectOption";
542
            #define CASE(X)  case cConfigOption::X: typestring = #X; break;
543
            const char *typestring;
544
            switch (key->getType()) {
545
                CASE(CFG_BOOL)
546
                CASE(CFG_INT)
547
                CASE(CFG_DOUBLE)
548
                CASE(CFG_STRING)
549
                CASE(CFG_FILENAME)
550
                CASE(CFG_FILENAMES)
551
                CASE(CFG_PATH)
552
                CASE(CFG_CUSTOM)
553
            }
554
            #undef CASE
555
556
            ev << "    public static final ConfigOption " << id << " = ";
557
            ev << method << (key->getUnit() ? "U" : "") << "(\n";
558
            ev << "        \"" << key->getName() << "\", ";
559
            if (!key->getUnit())
560
                ev << typestring << ", ";
561
            else
562
                ev << "\"" << key->getUnit() << "\", ";
563
            if (!key->getDefaultValue())
564
                ev << "null";
565
            else
566
                ev << "\"" << opp_replacesubstring(key->getDefaultValue(), "\"", "\\\"", true) << "\"";
567
            ev << ",\n";
568
569
            std::string desc = key->getDescription();
570
            desc = opp_replacesubstring(desc.c_str(), "\n", "\\n\n", true); // keep explicit line breaks
571
            desc = opp_breaklines(desc.c_str(), 75);  // break long lines
572
            desc = opp_replacesubstring(desc.c_str(), "\"", "\\\"", true);
573
            desc = opp_replacesubstring(desc.c_str(), "\n", " \" +\n\"", true);
574
            desc = opp_replacesubstring(desc.c_str(), "\\n \"", "\\n\"", true); // remove bogus space after explicit line breaks
575
            desc = "\"" + desc + "\"";
576
577
            ev << opp_indentlines(desc.c_str(), "        ") << ");\n";
578
        }
579
        ev << "\n";
580
581
        std::vector<const char *> vars = getConfigEx()->getPredefinedVariableNames();
582
        for (int i=0; i<(int)vars.size(); i++)
583
        {
584
            opp_string id = vars[i];
585
            opp_strupr(id.buffer());
586
            const char *desc = getConfigEx()->getVariableDescription(vars[i]);
587
            ev << "    public static final String CFGVAR_" << id << " = addConfigVariable(";
588
            ev << "\"" << vars[i] << "\", \"" << opp_replacesubstring(desc, "\"", "\\\"", true) << "\");\n";
589
        }
590
        ev << "\n";
591
    }
592
    if (wantAll || !strcmp(category, "classes"))
593
    {
594
        processed = true;
595
        ev << "Registered C++ classes, including modules, channels and messages:\n";
596
        cRegistrationList *table = classes.getInstance();
597
        table->sort();
598
        for (int i=0; i<table->size(); i++)
599
        {
600
            cObject *obj = table->get(i);
601
            ev << "  class " << obj->getFullName() << "\n";
602
        }
603
        ev << "Note: if your class is not listed, it needs to be registered in the\n";
604
        ev << "C++ code using Define_Module(), Define_Channel() or Register_Class().\n";
605
        ev << "\n";
606
    }
607
    if (wantAll || !strcmp(category, "classdesc"))
608
    {
609
        processed = true;
610
        ev << "Classes that have associated reflection information (needed for Tkenv inspectors):\n";
611
        cRegistrationList *table = classDescriptors.getInstance();
612
        table->sort();
613
        for (int i=0; i<table->size(); i++)
614
        {
615
            cObject *obj = table->get(i);
616
            ev << "  class " << obj->getFullName() << "\n";
617
        }
618
        ev << "\n";
619
    }
620
    if (wantAll || !strcmp(category, "nedfunctions"))
621
    {
622
        processed = true;
623
        ev << "Functions that can be used in NED expressions and in omnetpp.ini:\n";
624
        cRegistrationList *table = nedFunctions.getInstance();
625
        table->sort();
626
        std::set<std::string> categories;
627
        for (int i=0; i<table->size(); i++)
628
        {
629
            cNEDFunction *nf = dynamic_cast<cNEDFunction *>(table->get(i));
630
            cMathFunction *mf = dynamic_cast<cMathFunction *>(table->get(i));
631
            categories.insert(nf ? nf->getCategory() : mf ? mf->getCategory() : "???");
632
        }
633
        for (std::set<std::string>::iterator ci=categories.begin(); ci!=categories.end(); ++ci)
634
        {
635
            std::string category = (*ci);
636
            ev << "\n Category \"" << category << "\":\n";
637
            for (int i=0; i<table->size(); i++)
638
            {
639
                cObject *obj = table->get(i);
640
                cNEDFunction *nf = dynamic_cast<cNEDFunction *>(table->get(i));
641
                cMathFunction *mf = dynamic_cast<cMathFunction *>(table->get(i));
642
                const char *fcat = nf ? nf->getCategory() : mf ? mf->getCategory() : "???";
643
                const char *desc = nf ? nf->getDescription() : mf ? mf->getDescription() : "???";
644
                if (fcat==category)
645
                {
646
                    ev << "  " << obj->getFullName() << " : " << obj->info() << "\n";
647
                    if (desc)
648
                        ev << "    " << desc << "\n";
649
                }
650
            }
651
        }
652
        ev << "\n";
653
    }
654
    if (wantAll || !strcmp(category, "neddecls"))
655
    {
656
        processed = true;
657
        ev << "Built-in NED declarations:\n\n";
658
        ev << "---START---\n";
659
        ev << NEDParser::getBuiltInDeclarations();
660
        ev << "---END---\n";
661
        ev << "\n";
662
    }
663
    if (wantAll || !strcmp(category, "units"))
664
    {
665
        processed = true;
666
        ev << "Recognized physical units (note: other units can be used as well, only\n";
667
        ev << "no automatic conversion will be available for them):\n";
668
        std::vector<const char *> units = UnitConversion::getAllUnits();
669
        for (int i=0; i<(int)units.size(); i++)
670
        {
671
            const char *u = units[i];
672
            const char *bu = UnitConversion::getBaseUnit(u);
673
            ev << "  " << u << "\t" << UnitConversion::getLongName(u);
674
            if (opp_strcmp(u,bu)!=0)
675
                ev << "\t" << UnitConversion::convertUnit(1,u,bu) << bu;
676
            ev << "\n";
677
        }
678
        ev << "\n";
679
    }
680
    if (wantAll || !strcmp(category, "enums"))
681
    {
682
        processed = true;
683
        ev << "Enums defined in .msg files\n";
684
        cRegistrationList *table = enums.getInstance();
685
        table->sort();
686
        for (int i=0; i<table->size(); i++)
687
        {
688
            cObject *obj = table->get(i);
689
            ev << "  " << obj->getFullName() << " : " << obj->info() << "\n";
690
        }
691
        ev << "\n";
692
    }
693
    if (wantAll || !strcmp(category, "userinterfaces"))
694
    {
695
        processed = true;
696
        ev << "User interfaces loaded:\n";
697
        cRegistrationList *table = omnetapps.getInstance();
698
        table->sort();
699
        for (int i=0; i<table->size(); i++)
700
        {
701
            cObject *obj = table->get(i);
702
            ev << "  " << obj->getFullName() << " : " << obj->info() << "\n";
703
        }
704
    }
705
706
    if (wantAll || !strcmp(category, "resultfilters"))
707
    {
708
        processed = true;
709
        ev << "Result filters that can be used in @statistic properties:\n";
710
        cRegistrationList *table = resultFilters.getInstance();
711
        table->sort();
712
        for (int i=0; i<table->size(); i++)
713
        {
714
            cObject *obj = table->get(i);
715
            ev << "  " << obj->getFullName() << " : " << obj->info() << "\n";
716
        }
717
    }
718
719
    if (wantAll || !strcmp(category, "resultrecorders"))
720
    {
721
        processed = true;
722
        ev << "Result recorders that can be used in @statistic properties:\n";
723
        cRegistrationList *table = resultRecorders.getInstance();
724
        table->sort();
725
        for (int i=0; i<table->size(); i++)
726
        {
727
            cObject *obj = table->get(i);
728
            ev << "  " << obj->getFullName() << " : " << obj->info() << "\n";
729
        }
730
    }
731
732
    if (!processed)
733
        throw cRuntimeError("Unrecognized category for '-h' option: %s", category);
734
}
735
736
int EnvirBase::getParsimProcId() const
737
{
738
#ifdef WITH_PARSIM
739
    return parsimcomm ? parsimcomm->getProcId() : 0;
740
#else
741
    return 0;
742
#endif
743
}
744
745
int EnvirBase::getParsimNumPartitions() const
746
{
747
#ifdef WITH_PARSIM
748
    return parsimcomm ? parsimcomm->getNumPartitions() : 0;
749
#else
750
    return 0;
751
#endif
752
}
753
754
755
void EnvirBase::shutdown()
756
{
757
    try
758
    {
759
        simulation.deleteNetwork();
760
#ifdef WITH_PARSIM
761
        if (opt_parsim && parsimpartition)
762
            parsimpartition->shutdown();
763
#endif
764
    }
765
    catch (std::exception& e)
766
    {
767
        displayException(e);
768
    }
769
}
770
771
void EnvirBase::startRun()
772
{
773
    resetClock();
774
    outvectormgr->startRun();
775
    outscalarmgr->startRun();
776
    snapshotmgr->startRun();
777
    if (record_eventlog)
778
        eventlogmgr->startRun();
779
    if (opt_parsim)
780
    {
781
#ifdef WITH_PARSIM
782
        parsimpartition->startRun();
783
#endif
784
    }
785
    simulation.startRun();
786
    flushLastLine();
787
}
788
789
void EnvirBase::endRun()
790
{
791
    // reverse order as startRun()
792
    simulation.endRun();
793
    if (opt_parsim)
794
    {
795
#ifdef WITH_PARSIM
796
        parsimpartition->endRun();
797
#endif
798
    }
799
    if (record_eventlog) {
800
        eventlogmgr->endRun();
801
        delete eventlogmgr;
802
        eventlogmgr = NULL;
803
        record_eventlog = false;
804
    }
805
    snapshotmgr->endRun();
806
    outscalarmgr->endRun();
807
    outvectormgr->endRun();
808
}
809
810
//-------------------------------------------------------------
811
812
void EnvirBase::configure(cComponent *component)
813
{
814
    addResultRecorders(component);
815
}
816
817
static int search_(std::vector<std::string>& v, const char *s)
818
{
819
    for (int i=0; i<(int)v.size(); i++)
820
        if (strcmp(v[i].c_str(), s)==0)
821
            return i;
822
    return -1;
823
}
824
825
inline void addIfNotContains_(std::vector<std::string>& v, const char *s)
826
{
827
    if (search_(v,s)==-1)
828
        v.push_back(s);
829
}
830
831
inline void addIfNotContains_(std::vector<std::string>& v, const std::string& s)
832
{
833
    if (search_(v,s.c_str())==-1)
834
        v.push_back(s);
835
}
836
837
void EnvirBase::addResultRecorders(cComponent *component)
838
{
839
    std::vector<const char *> statisticNames = component->getProperties()->getIndicesFor("statistic");
840
    std::string componentFullPath;
841
    for (int i = 0; i < (int)statisticNames.size(); i++)
842
    {
843
        const char *statisticName = statisticNames[i];
844
        if (componentFullPath.empty())
845
            componentFullPath = component->getFullPath();
846
        std::string statisticFullPath = componentFullPath + "." + statisticName;
847
848
        bool scalarsEnabled = ev.getConfig()->getAsBool(statisticFullPath.c_str(), CFGID_SCALAR_RECORDING);
849
        bool vectorsEnabled = ev.getConfig()->getAsBool(statisticFullPath.c_str(), CFGID_VECTOR_RECORDING);
850
        if (!scalarsEnabled && !vectorsEnabled)
851
            continue;
852
853
        cProperty *statisticProperty = component->getProperties()->get("statistic", statisticName);
854
        ASSERT(statisticProperty!=NULL);
855
856
        // collect the list of result recorders
857
        std::string modesOption = ev.getConfig()->getAsString(statisticFullPath.c_str(), CFGID_RESULT_RECORDING_MODES, "");
858
        std::vector<std::string> modes = extractRecorderList(modesOption.c_str(), statisticProperty);
859
860
        // if there are result recorders, add source filters and recorders
861
        if (!modes.empty())
862
        {
863
            // add source
864
            bool hasSourceKey = statisticProperty->getNumValues("source") > 0;
865
            const char *sourceSpec = hasSourceKey ? statisticProperty->getValue("source",0) : statisticName;
866
            SignalSource source = doStatisticSource(component, statisticName, sourceSpec, opt_warmupperiod!=0);
867
868
            // add result recorders
869
            for (int j = 0; j < (int)modes.size(); j++)
870
                doResultRecorder(source, modes[j].c_str(), scalarsEnabled, vectorsEnabled, component, statisticName);
871
        }
872
    }
873
874
    if (opt_debug_statistics_recording)
875
        dumpResultRecorders(component);
876
}
877
878
std::vector<std::string> EnvirBase::extractRecorderList(const char *modesOption, cProperty *statisticProperty)
879
{
880
    // "-" means "none"
881
    if (!modesOption[0] || (modesOption[0]=='-' && !modesOption[1]))
882
        return std::vector<std::string>();
883
884
    std::vector<std::string> modes; // the result
885
886
    // if first configured mode starts with '+' or '-', assume "default" as base
887
    if (modesOption[0]=='-' || modesOption[0]=='+')
888
    {
889
        // collect the mandatory record= items from @statistic (those not ending in '?')
890
        int n = statisticProperty->getNumValues("record");
891
        for (int i = 0; i < n; i++) {
892
            const char *m = statisticProperty->getValue("record", i);
893
            if (m[strlen(m)-1] != '?')
894
                addIfNotContains_(modes, m);
895
        }
896
    }
897
898
    // loop through all modes
899
    StringTokenizer tokenizer(modesOption, ","); //XXX we should ignore commas within parens
900
    while (tokenizer.hasMoreTokens())
901
    {
902
        const char *mode = tokenizer.nextToken();
903
        if (!strcmp(mode, "default"))
904
        {
905
            // collect the mandatory record= items from @statistic (those not ending in '?')
906
            int n = statisticProperty->getNumValues("record");
907
            for (int i = 0; i < n; i++) {
908
                const char *m = statisticProperty->getValue("record", i);
909
                if (m[strlen(m)-1] != '?')
910
                    addIfNotContains_(modes, m);
911
            }
912
        }
913
        else if (!strcmp(mode, "all"))
914
        {
915
            // collect all record= items from @statistic (strip trailing '?' if present)
916
            int n = statisticProperty->getNumValues("record");
917
            for (int i = 0; i < n; i++) {
918
                const char *m = statisticProperty->getValue("record", i);
919
                if (m[strlen(m)-1] != '?')
920
                    addIfNotContains_(modes, m);
921
                else
922
                    addIfNotContains_(modes, std::string(m, strlen(m)-1));
923
            }
924
        }
925
        else if (mode[0] == '-')
926
        {
927
            // remove from modes
928
            int k = search_(modes, mode+1);
929
            if (k != -1)
930
                modes.erase(modes.begin()+k);
931
        }
932
        else {
933
            // add to modes
934
            addIfNotContains_(modes, mode[0]=='+' ? mode+1 : mode);
935
        }
936
    }
937
    return modes;
938
}
939
940
static bool opp_isidentifier(const char *s)
941
{
942
    if (!opp_isalpha(s[0]) && s[0]!='_')
943
        return false;
944
    while (*++s)
945
        if (!opp_isalnum(*s))
946
            return false;
947
    return true;
948
}
949
950
SignalSource EnvirBase::doStatisticSource(cComponent *component, const char *statisticName, const char *sourceSpec, bool needWarmupFilter)
951
{
952
    try
953
    {
954
        if (opp_isidentifier(sourceSpec))
955
        {
956
            // simple case: just a signal name
957
            simsignal_t signalID = cComponent::registerSignal(sourceSpec);
958
            if (!needWarmupFilter)
959
                return SignalSource(component, signalID);
960
            else
961
            {
962
                WarmupPeriodFilter *warmupFilter = new WarmupPeriodFilter();
963
                component->subscribe(signalID, warmupFilter);
964
                return SignalSource(warmupFilter);
965
            }
966
        }
967
        else
968
        {
969
            StatisticSourceParser parser;
970
            return parser.parse(component, statisticName, sourceSpec, needWarmupFilter);
971
        }
972
    }
973
    catch (std::exception& e)
974
    {
975
        throw cRuntimeError("Error adding statistic '%s' to module %s (NED type: %s): error in source=%s: %s",
976
            statisticName, component->getFullPath().c_str(), component->getNedTypeName(), sourceSpec, e.what());
977
    }
978
}
979
980
void EnvirBase::doResultRecorder(const SignalSource& source, const char *recordingMode, bool scalarsEnabled, bool vectorsEnabled, cComponent *component, const char *statisticName)
981
{
982
    try
983
    {
984
        if (opp_isidentifier(recordingMode))
985
        {
986
            // simple case: just a plain recorder
987
            bool recordsVector = !strcmp(recordingMode, "vector");  // the only vector recorder is "vector"
988
            if (recordsVector ? !vectorsEnabled : !scalarsEnabled)
989
                return; // no point in creating if recording is disabled
990
991
            ResultRecorder *recorder = ResultRecorderDescriptor::get(recordingMode)->create();
992
            recorder->init(component, statisticName, recordingMode);
993
            source.subscribe(recorder);
994
        }
995
        else
996
        {
997
            // something more complicated: use parser
998
            StatisticRecorderParser parser;
999
            parser.parse(source, recordingMode, scalarsEnabled, vectorsEnabled, component, statisticName);
1000
        }
1001
    }
1002
    catch (std::exception& e)
1003
    {
1004
        throw cRuntimeError("Error adding statistic '%s' to module %s (NED type: %s): bad recording mode '%s': %s",
1005
            statisticName, component->getFullPath().c_str(), component->getNedTypeName(), recordingMode, e.what());
1006
    }
1007
}
1008
1009
void EnvirBase::dumpResultRecorders(cComponent *component)
1010
{
1011
    bool componentPathPrinted = false;
1012
    std::vector<simsignal_t> signals = component->getLocalListenedSignals();
1013
    for (unsigned int i = 0; i < signals.size(); i++)
1014
    {
1015
        bool signalNamePrinted = false;
1016
        simsignal_t signalID = signals[i];
1017
        std::vector<cIListener*> listeners = component->getLocalSignalListeners(signalID);
1018
        for (unsigned int j = 0; j < listeners.size(); j++) {
1019
            if (dynamic_cast<ResultListener*>(listeners[j])) {
1020
                if (!componentPathPrinted) {
1021
                    ev << component->getFullPath() << " (" << component->getNedTypeName() << "):\n";
1022
                    componentPathPrinted = true;
1023
                }
1024
                if (!signalNamePrinted) {
1025
                    ev << "    \"" << cComponent::getSignalName(signalID) << "\" (signalID="  << signalID << "):\n";
1026
                    signalNamePrinted = true;
1027
                }
1028
                dumpResultRecorderChain((ResultListener *)listeners[j], 0);
1029
            }
1030
        }
1031
    }
1032
}
1033
1034
void EnvirBase::dumpResultRecorderChain(ResultListener *listener, int depth)
1035
{
1036
    for (int i = 0; i < depth+2; i++)
1037
        ev << "    ";
1038
    ev << listener->str();
1039
    if (dynamic_cast<ResultRecorder*>(listener))
1040
        ev << " ==> " << ((ResultRecorder*)listener)->getResultName();
1041
    ev << "\n";
1042
1043
    if (dynamic_cast<ResultFilter *>(listener))
1044
    {
1045
        std::vector<ResultListener *> delegates = ((ResultFilter*)listener)->getDelegates();
1046
        for (unsigned int i=0; i < delegates.size(); i++)
1047
            dumpResultRecorderChain(delegates[i], depth+1);
1048
    }
1049
}
1050
1051
void EnvirBase::readParameter(cPar *par)
1052
{
1053
    ASSERT(!par->isSet());  // must be unset at this point
1054
1055
    // get it from the ini file
1056
    std::string moduleFullPath = par->getOwner()->getFullPath();
1057
    const char *str = getConfigEx()->getParameterValue(moduleFullPath.c_str(), par->getName(), par->containsValue());
1058
1059
/* XXX hack to use base directory for resolving xml files location has been commented out
1060
 * FIXME a solution needs to be worked out!
1061
    if (str[0]=='x' && !strncmp(str,"xmldoc",6) && !opp_isalnum(str[6]))
1062
    {
1063
        // Make XML file location relative to the ini file in which it occurs.
1064
        // Find substring between first two quotes (that is, the XML filename),
1065
        // and prefix it with the directory.
1066
        const char *begQuote = strchr(str+6,'"');
1067
        if (!begQuote)
1068
            return std::string(str);
1069
        const char *endQuote = strchr(begQuote+1,'"');
1070
        while (endQuote && *(endQuote-1)=='\\' && *(endQuote-2)!='\\')
1071
            endQuote = strchr(endQuote+1,'"');
1072
        if (!endQuote)
1073
            return std::string(str);
1074
        std::string fname(begQuote+1, endQuote-begQuote-1);
1075
        const char *baseDir = getConfig()->getBaseDirectoryFor(NULL, "Parameters", parname);
1076
        fname = tidyFilename(concatDirAndFile(baseDir, fname.c_str()).c_str(),true);
1077
        std::string ret = std::string(str, begQuote-str+1) + fname + endQuote;
1078
        //XXX use "ret" further!!!
1079
    }
1080
*/
1081
1082
    if (opp_strcmp(str, "default")==0)
1083
    {
1084
        ASSERT(par->containsValue());  // cConfiguration should not return "=default" lines for params that have no default value
1085
        par->acceptDefault();
1086
    }
1087
    else if (opp_strcmp(str, "ask")==0)
1088
    {
1089
        askParameter(par, false);
1090
    }
1091
    else if (!opp_isempty(str))
1092
    {
1093
        par->parse(str);
1094
    }
1095
    else
1096
    {
1097
        // str empty: no value in the ini file
1098
        if (par->containsValue())
1099
            par->acceptDefault();
1100
        else
1101
            askParameter(par, true);
1102
    }
1103
}
1104
1105
bool EnvirBase::isModuleLocal(cModule *parentmod, const char *modname, int index)
1106
{
1107
#ifdef WITH_PARSIM
1108
    if (!opt_parsim)
1109
       return true;
1110
1111
    // toplevel module is local everywhere
1112
    if (!parentmod)
1113
       return true;
1114
1115
    // find out if this module is (or has any submodules that are) on this partition
1116
    char parname[MAX_OBJECTFULLPATH];
1117
    if (index<0)
1118
        sprintf(parname,"%s.%s", parentmod->getFullPath().c_str(), modname);
1119
    else
1120
        sprintf(parname,"%s.%s[%d]", parentmod->getFullPath().c_str(), modname, index); //FIXME this is incorrectly chosen for non-vector modules too!
1121
    std::string procIds = getConfig()->getAsString(parname, CFGID_PARTITION_ID, "");
1122
    if (procIds.empty())
1123
    {
1124
        // modules inherit the setting from their parents, except when the parent is the system module (the network) itself
1125
        if (!parentmod->getParentModule())
1126
            throw cRuntimeError("incomplete partitioning: missing value for '%s'",parname);
1127
        // "true" means "inherit", because an ancestor which answered "false" doesn't get recursed into
1128
        return true;
1129
    }
1130
    else if (strcmp(procIds.c_str(), "*") == 0)
1131
    {
1132
        // present on all partitions (provided that ancestors have "*" set as well)
1133
        return true;
1134
    }
1135
    else
1136
    {
1137
        // we expect a partition Id (or partition Ids, separated by commas) where this
1138
        // module needs to be instantiated. So we return true if any of the numbers
1139
        // is the Id of the local partition, otherwise false.
1140
        EnumStringIterator procIdIter(procIds.c_str());
1141
        if (procIdIter.hasError())
1142
            throw cRuntimeError("wrong partitioning: syntax error in value '%s' for '%s' "
1143
                                "(allowed syntax: '', '*', '1', '0,3,5-7')",
1144
                                procIds.c_str(), parname);
1145
        int numPartitions = parsimcomm->getNumPartitions();
1146
        int myProcId = parsimcomm->getProcId();
1147
        for (; procIdIter()!=-1; procIdIter++)
1148
        {
1149
            if (procIdIter() >= numPartitions)
1150
                throw cRuntimeError("wrong partitioning: value %d too large for '%s' (total partitions=%d)",
1151
                                    procIdIter(), parname, numPartitions);
1152
            if (procIdIter() == myProcId)
1153
                return true;
1154
        }
1155
        return false;
1156
    }
1157
#else
1158
    return true;
1159
#endif
1160
}
1161
1162
cXMLElement *EnvirBase::getXMLDocument(const char *filename, const char *path)
1163
{
1164
    cXMLElement *documentnode = xmlcache->getDocument(filename);
1165
    assert(documentnode);
1166
    if (path)
1167
    {
1168
        ModNameParamResolver resolver(simulation.getContextModule()); // resolves $MODULE_NAME etc in XPath expr.
1169
        return cXMLElement::getDocumentElementByPath(documentnode, path, &resolver);
1170
    }
1171
    else
1172
    {
1173
        // returns the root element (child of the document node)
1174
        return documentnode->getFirstChild();
1175
    }
1176
}
1177
1178
void EnvirBase::forgetXMLDocument(const char *filename)
1179
{
1180
    xmlcache->forgetDocument(filename);
1181
}
1182
1183
void EnvirBase::flushXMLDocumentCache()
1184
{
1185
    xmlcache->flushCache();
1186
}
1187
1188
cConfiguration *EnvirBase::getConfig()
1189
{
1190
    return cfg;
1191
}
1192
1193
cConfigurationEx *EnvirBase::getConfigEx()
1194
{
1195
    return cfg;
1196
}
1197
1198
//-------------------------------------------------------------
1199
1200
void EnvirBase::bubble(cComponent *component, const char *text)
1201
{
1202
    if (record_eventlog)
1203
        eventlogmgr->bubble(component, text);
1204
}
1205
1206
void EnvirBase::objectDeleted(cObject *object)
1207
{
1208
    // TODO?
1209
}
1210
1211
void EnvirBase::simulationEvent(cMessage *msg)
1212
{
1213
    if (record_eventlog)
1214
        eventlogmgr->simulationEvent(msg);
1215
}
1216
1217 2f5cc443 Simon Tenbusch
void EnvirBase::simulationEventEnd(double complexity)
1218
{
1219
    if (record_eventlog)
1220
        eventlogmgr->simulationEventEnd(complexity);
1221
}
1222
1223
1224 01873262 Georg Kunz
void EnvirBase::beginSend(cMessage *msg)
1225
{
1226
    if (record_eventlog)
1227
        eventlogmgr->beginSend(msg);
1228
}
1229
1230
void EnvirBase::messageScheduled(cMessage *msg)
1231
{
1232
    if (record_eventlog)
1233
        eventlogmgr->messageScheduled(msg);
1234
}
1235
1236
void EnvirBase::messageCancelled(cMessage *msg)
1237
{
1238
    if (record_eventlog)
1239
        eventlogmgr->messageCancelled(msg);
1240
}
1241
1242
void EnvirBase::messageSendDirect(cMessage *msg, cGate *toGate, simtime_t propagationDelay, simtime_t transmissionDelay)
1243
{
1244
    if (record_eventlog)
1245
        eventlogmgr->messageSendDirect(msg, toGate, propagationDelay, transmissionDelay);
1246
}
1247
1248
void EnvirBase::messageSendHop(cMessage *msg, cGate *srcGate)
1249
{
1250
    if (record_eventlog)
1251
        eventlogmgr->messageSendHop(msg, srcGate);
1252
}
1253
1254
void EnvirBase::messageSendHop(cMessage *msg, cGate *srcGate, simtime_t propagationDelay, simtime_t transmissionDelay)
1255
{
1256
    if (record_eventlog)
1257
        eventlogmgr->messageSendHop(msg, srcGate, propagationDelay, transmissionDelay);
1258
}
1259
1260
void EnvirBase::endSend(cMessage *msg)
1261
{
1262
    if (record_eventlog)
1263
        eventlogmgr->endSend(msg);
1264
}
1265
1266
void EnvirBase::messageDeleted(cMessage *msg)
1267
{
1268
    if (record_eventlog)
1269
        eventlogmgr->messageDeleted(msg);
1270
}
1271
1272
void EnvirBase::componentMethodBegin(cComponent *from, cComponent *to, const char *methodFmt, va_list va, bool silent)
1273
{
1274
    if (record_eventlog)
1275
        eventlogmgr->componentMethodBegin(from, to, methodFmt, va);
1276
}
1277
1278
void EnvirBase::componentMethodEnd()
1279
{
1280
    if (record_eventlog)
1281
        eventlogmgr->componentMethodEnd();
1282
}
1283
1284
void EnvirBase::moduleCreated(cModule *newmodule)
1285
{
1286
    if (record_eventlog)
1287
        eventlogmgr->moduleCreated(newmodule);
1288
}
1289
1290
void EnvirBase::moduleDeleted(cModule *module)
1291
{
1292
    if (record_eventlog)
1293
        eventlogmgr->moduleDeleted(module);
1294
}
1295
1296
void EnvirBase::moduleReparented(cModule *module, cModule *oldparent)
1297
{
1298
    if (record_eventlog)
1299
        eventlogmgr->moduleReparented(module, oldparent);
1300
}
1301
1302
void EnvirBase::gateCreated(cGate *newgate)
1303
{
1304
    if (record_eventlog)
1305
        eventlogmgr->gateCreated(newgate);
1306
}
1307
1308
void EnvirBase::gateDeleted(cGate *gate)
1309
{
1310
    if (record_eventlog)
1311
        eventlogmgr->gateDeleted(gate);
1312
}
1313
1314
void EnvirBase::connectionCreated(cGate *srcgate)
1315
{
1316
    if (record_eventlog)
1317
        eventlogmgr->connectionCreated(srcgate);
1318
}
1319
1320
void EnvirBase::connectionDeleted(cGate *srcgate)
1321
{
1322
    if (record_eventlog)
1323
        eventlogmgr->connectionDeleted(srcgate);
1324
}
1325
1326
void EnvirBase::displayStringChanged(cComponent *component)
1327
{
1328
    if (record_eventlog)
1329
        eventlogmgr->displayStringChanged(component);
1330
}
1331
1332
void EnvirBase::sputn(const char *s, int n)
1333
{
1334
    if (record_eventlog)
1335
        eventlogmgr->sputn(s, n);
1336
}
1337
1338
void EnvirBase::undisposedObject(cObject *obj)
1339
{
1340
    if (opt_print_undisposed)
1341
        ::printf("undisposed object: (%s) %s -- check module destructor\n", obj->getClassName(), obj->getFullPath().c_str());
1342
}
1343
1344
//-------------------------------------------------------------
1345
1346
void EnvirBase::processFileName(opp_string& fname)
1347
{
1348
    std::string text = fname.c_str();
1349
1350
    // insert ".<hostname>.<pid>" if requested before file extension
1351
    // (note: parsimProcId cannot be appended because of initialization order)
1352
    if (opt_fname_append_host)
1353
    {
1354
        std::string extension = "";
1355
        std::string::size_type index = text.rfind('.');
1356
        if (index != std::string::npos) {
1357
          extension = std::string(text, index);
1358
          text.erase(index);
1359
        }
1360
1361
        const char *hostname=getenv("HOST");
1362
        if (!hostname)
1363
            hostname=getenv("HOSTNAME");
1364
        if (!hostname)
1365
            hostname=getenv("COMPUTERNAME");
1366
        if (!hostname)
1367
            throw cRuntimeError("Cannot append hostname to file name `%s': no HOST, HOSTNAME "
1368
                                "or COMPUTERNAME (Windows) environment variable",
1369
                                fname.c_str());
1370
        int pid = getpid();
1371
1372
        // append
1373
        text += opp_stringf(".%s.%d%s", hostname, pid, extension.c_str());
1374
    }
1375
    fname = text.c_str();
1376
}
1377
1378
void EnvirBase::readOptions()
1379
{
1380
    cConfiguration *cfg = getConfig();
1381
1382
    opt_total_stack = (size_t) cfg->getAsDouble(CFGID_TOTAL_STACK, TOTAL_STACK_SIZE);
1383
    opt_parsim = cfg->getAsBool(CFGID_PARALLEL_SIMULATION);
1384
    if (!opt_parsim)
1385
    {
1386
        opt_scheduler_class = cfg->getAsString(CFGID_SCHEDULER_CLASS);
1387
    }
1388
    else
1389
    {
1390
#ifdef WITH_PARSIM
1391
        opt_parsimcomm_class = cfg->getAsString(CFGID_PARSIM_COMMUNICATIONS_CLASS);
1392
        opt_parsimsynch_class = cfg->getAsString(CFGID_PARSIM_SYNCHRONIZATION_CLASS);
1393
#else
1394
        throw cRuntimeError("Parallel simulation is turned on in the ini file, but OMNeT++ was compiled without parallel simulation support (WITH_PARSIM=no)");
1395
#endif
1396
    }
1397
1398
    opt_outputvectormanager_class = cfg->getAsString(CFGID_OUTPUTVECTORMANAGER_CLASS);
1399
    opt_outputscalarmanager_class = cfg->getAsString(CFGID_OUTPUTSCALARMANAGER_CLASS);
1400
    opt_snapshotmanager_class = cfg->getAsString(CFGID_SNAPSHOTMANAGER_CLASS);
1401
1402
    opt_fname_append_host = cfg->getAsBool(CFGID_FNAME_APPEND_HOST, opt_parsim);
1403
1404
    ev.debug_on_errors = cfg->getAsBool(CFGID_DEBUG_ON_ERRORS);
1405
    opt_print_undisposed = cfg->getAsBool(CFGID_PRINT_UNDISPOSED);
1406
1407
    int scaleexp = (int) cfg->getAsInt(CFGID_SIMTIME_SCALE);
1408
    SimTime::setScaleExp(scaleexp);
1409
1410
    // note: this is read per run as well, but Tkenv needs its value on startup too
1411
    opt_inifile_network_dir = cfg->getConfigEntry(CFGID_NETWORK->getName()).getBaseDirectory();
1412
1413
    // other options are read on per-run basis
1414
}
1415
1416
void EnvirBase::readPerRunOptions()
1417
{
1418
    cConfiguration *cfg = getConfig();
1419
1420
    // get options from ini file
1421
    opt_network_name = cfg->getAsString(CFGID_NETWORK);
1422
    opt_inifile_network_dir = cfg->getConfigEntry(CFGID_NETWORK->getName()).getBaseDirectory();
1423
    opt_warnings = cfg->getAsBool(CFGID_WARNINGS);
1424
    opt_simtimelimit = cfg->getAsDouble(CFGID_SIM_TIME_LIMIT);
1425
    opt_cputimelimit = (long) cfg->getAsDouble(CFGID_CPU_TIME_LIMIT);
1426
    opt_warmupperiod = cfg->getAsDouble(CFGID_WARMUP_PERIOD);
1427
    opt_fingerprint = cfg->getAsString(CFGID_FINGERPRINT);
1428
    opt_num_rngs = cfg->getAsInt(CFGID_NUM_RNGS);
1429
    opt_rng_class = cfg->getAsString(CFGID_RNG_CLASS);
1430
    opt_seedset = cfg->getAsInt(CFGID_SEED_SET);
1431
    opt_debug_statistics_recording = cfg->getAsBool(CFGID_DEBUG_STATISTICS_RECORDING);
1432
1433
    simulation.setWarmupPeriod(opt_warmupperiod);
1434
1435
    // install hasher object
1436
    if (!opt_fingerprint.empty())
1437
        simulation.setHasher(new cHasher());
1438
    else
1439
        simulation.setHasher(NULL);
1440
1441
    // run RNG self-test on RNG class selected for this run
1442
    cRNG *testrng;
1443
    CREATE_BY_CLASSNAME(testrng, opt_rng_class.c_str(), cRNG, "random number generator");
1444
    testrng->selfTest();
1445
    delete testrng;
1446
1447
    // set up RNGs
1448 08285dff Mirko Stoffers
    for (int i = 0; i < rngs.size(); i++)
1449 01873262 Georg Kunz
         delete rngs[i];
1450
1451 08285dff Mirko Stoffers
    num_rngs_per_module = opt_num_rngs;
1452
        createRNGs();
1453 01873262 Georg Kunz
1454
    // init nextuniquenumber -- startRun() is too late because simple module ctors have run by then
1455
    nextuniquenumber = 0;
1456
#ifdef WITH_PARSIM
1457
    if (opt_parsim)
1458
        nextuniquenumber = (unsigned)parsimcomm->getProcId() * ((~0UL) / (unsigned)parsimcomm->getNumPartitions());
1459
#endif
1460
1461
    // open message log file. Note: in startRun() it would be too late,
1462
    // because modules have already been created by then
1463
    record_eventlog = cfg->getAsBool(CFGID_RECORD_EVENTLOG);
1464
    if (record_eventlog) {
1465
        eventlogmgr = new EventlogFileManager();
1466
        eventlogmgr->configure();
1467
        eventlogmgr->open();
1468
    }
1469
}
1470
1471 08285dff Mirko Stoffers
void EnvirBase::createRNGs()
1472
{
1473
        int offset=rngs.size();
1474
        int moduleId=offset/num_rngs_per_module;
1475
        for(int i=0;i<num_rngs_per_module;i++)
1476
        {
1477
                int pos=i+offset;
1478
                cRNG *rng;
1479
                CREATE_BY_CLASSNAME(rng, opt_rng_class.c_str(), cRNG, "random number generator");
1480
        assert(rngs.size()==pos);
1481
                rngs.push_back(rng);
1482
        rngs[pos]->initialize(opt_seedset*moduleId, i, num_rngs_per_module, getParsimProcId(), getParsimNumPartitions(), getConfig());
1483
        }
1484
}
1485
1486 01873262 Georg Kunz
void EnvirBase::setEventlogRecording(bool enabled)
1487
{
1488
    // NOTE: eventlogmgr must be non-NULL when record_eventlog is true
1489
    if (enabled && !eventlogmgr) {
1490
        eventlogmgr = new EventlogFileManager();
1491
        eventlogmgr->configure();
1492
        eventlogmgr->open();
1493
        eventlogmgr->recordSimulation();
1494
    }
1495
    record_eventlog = enabled;
1496
}
1497
1498
bool EnvirBase::hasEventlogRecordingIntervals() const
1499
{
1500
    return eventlogmgr && eventlogmgr->hasRecordingIntervals();
1501
}
1502
1503
void EnvirBase::clearEventlogRecordingIntervals()
1504
{
1505
    if (eventlogmgr)
1506
        eventlogmgr->clearRecordingIntervals();
1507
}
1508
1509
//-------------------------------------------------------------
1510
1511 08285dff Mirko Stoffers
int EnvirBase::getNumRNGsPerModule() const
1512 01873262 Georg Kunz
{
1513 08285dff Mirko Stoffers
    return num_rngs_per_module;
1514 01873262 Georg Kunz
}
1515
1516
cRNG *EnvirBase::getRNG(int k)
1517
{
1518 08285dff Mirko Stoffers
    if (k<0 || k>=rngs.size())
1519
        throw cRuntimeError("RNG index %d is out of range (num-rngs=%d*<numberOfModules>=%d, check the configuration)", k, num_rngs_per_module, rngs.size());
1520 01873262 Georg Kunz
    return rngs[k];
1521
}
1522
1523
void EnvirBase::getRNGMappingFor(cComponent *component)
1524
{
1525
    cConfigurationEx *cfg = getConfigEx();
1526
    std::string componentFullPath = component->getFullPath();
1527
    std::vector<const char *> suffixes = cfg->getMatchingPerObjectConfigKeySuffixes(componentFullPath.c_str(), "rng-*"); // CFGID_RNG_K
1528
    if (suffixes.size()==0)
1529
        return;
1530
1531
    // extract into tmpmap[]
1532
    int mapsize=0;
1533
    int tmpmap[100];
1534
    for (int i=0; i<(int)suffixes.size(); i++)
1535
    {
1536
        const char *suffix = suffixes[i];  // contains "rng-1", "rng-4" or whichever has been found in the config for this module/channel
1537
        const char *value = cfg->getPerObjectConfigValue(componentFullPath.c_str(), suffix);
1538
        ASSERT(value!=NULL);
1539
        char *s1, *s2;
1540
        int modRng = strtol(suffix+strlen("rng-"), &s1, 10);
1541
        int physRng = strtol(value, &s2, 10);
1542
        if (*s1!='\0' || *s2!='\0')
1543
            throw cRuntimeError("Configuration error: %s=%s for module/channel %s: "
1544
                                "numeric RNG indices expected",
1545
                                suffix, value, component->getFullPath().c_str());
1546
1547 08285dff Mirko Stoffers
        if (physRng>rngs.size())
1548 01873262 Georg Kunz
            throw cRuntimeError("Configuration error: rng-%d=%d of module/channel %s: "
1549
                                "RNG index out of range (num-rngs=%d)",
1550 08285dff Mirko Stoffers
                                modRng, physRng, component->getFullPath().c_str(), rngs.size());
1551 01873262 Georg Kunz
        if (modRng>=mapsize)
1552
        {
1553
            if (modRng>=100)
1554
                throw cRuntimeError("Configuration error: rng-%d=... of module/channel %s: "
1555
                                    "local RNG index out of supported range 0..99",
1556
                                    modRng, component->getFullPath().c_str());
1557
            while (mapsize<=modRng)
1558
            {
1559
                tmpmap[mapsize] = mapsize;
1560
                mapsize++;
1561
            }
1562
        }
1563
        tmpmap[modRng] = physRng;
1564
    }
1565
1566
    // install map into the module
1567
    if (mapsize>0)
1568
    {
1569
        int *map = new int[mapsize];
1570
        memcpy(map, tmpmap, mapsize*sizeof(int));
1571
        component->setRNGMap(mapsize, map);
1572
    }
1573
}
1574
1575
//-------------------------------------------------------------
1576
1577
void *EnvirBase::registerOutputVector(const char *modulename, const char *vectorname)
1578
{
1579
    assert(outvectormgr);
1580
    return outvectormgr->registerVector(modulename, vectorname);
1581
}
1582
1583
void EnvirBase::deregisterOutputVector(void *vechandle)
1584
{
1585
    assert(outvectormgr);
1586
    outvectormgr->deregisterVector(vechandle);
1587
}
1588
1589
void EnvirBase::setVectorAttribute(void *vechandle, const char *name, const char *value)
1590
{
1591
    assert(outvectormgr);
1592
    outvectormgr->setVectorAttribute(vechandle, name, value);
1593
}
1594
1595
bool EnvirBase::recordInOutputVector(void *vechandle, simtime_t t, double value)
1596
{
1597
    assert(outvectormgr);
1598
    return outvectormgr->record(vechandle, t, value);
1599
}
1600
1601
//-------------------------------------------------------------
1602
1603
void EnvirBase::recordScalar(cComponent *component, const char *name, double value, opp_string_map *attributes)
1604
{
1605
    assert(outscalarmgr);
1606
    outscalarmgr->recordScalar(component, name, value, attributes);
1607
}
1608
1609
void EnvirBase::recordStatistic(cComponent *component, const char *name, cStatistic *statistic, opp_string_map *attributes)
1610
{
1611
    assert(outscalarmgr);
1612
    outscalarmgr->recordStatistic(component, name, statistic, attributes);
1613
}
1614
1615
//-------------------------------------------------------------
1616
1617
std::ostream *EnvirBase::getStreamForSnapshot()
1618
{
1619
    return snapshotmgr->getStreamForSnapshot();
1620
}
1621
1622
void EnvirBase::releaseStreamForSnapshot(std::ostream *os)
1623
{
1624
    snapshotmgr->releaseStreamForSnapshot(os);
1625
}
1626
1627
//-------------------------------------------------------------
1628
1629
unsigned long EnvirBase::getUniqueNumber()
1630
{
1631
    // TBD check for overflow
1632
    return nextuniquenumber++;
1633
}
1634
1635
void EnvirBase::displayException(std::exception& ex)
1636
{
1637
    cException *e = dynamic_cast<cException *>(&ex);
1638
    if (!e)
1639
        ev.printfmsg("Error: %s.", ex.what());
1640
    else
1641
        ev.printfmsg("%s", e->getFormattedMessage().c_str());
1642
}
1643
1644
bool EnvirBase::idle()
1645
{
1646
    return false;
1647
}
1648
1649
int EnvirBase::getArgCount() const
1650
{
1651
    return args->getArgCount();
1652
}
1653
1654
char **EnvirBase::getArgVector() const
1655
{
1656
    return args->getArgVector();
1657
}
1658
1659
//-------------------------------------------------------------
1660
1661
void EnvirBase::resetClock()
1662
{
1663
    timeval now;
1664
    gettimeofday(&now, NULL);
1665
    laststarted = simendtime = simbegtime = now;
1666
    elapsedtime.tv_sec = elapsedtime.tv_usec = 0;
1667
}
1668
1669
void EnvirBase::startClock()
1670
{
1671
    gettimeofday(&laststarted, NULL);
1672
}
1673
1674
void EnvirBase::stopClock()
1675
{
1676
    gettimeofday(&simendtime, NULL);
1677
    elapsedtime = elapsedtime + simendtime - laststarted;
1678
    simulatedtime = simulation.getSimTime();
1679 68da4f12 Georg Kunz
1680
    bool storeRuntimes = getConfig()->getAsBool(CFGID_WRITE_RUNTIME_TO_FILE);
1681
    if (storeRuntimes)
1682
    {
1683
        std::string filename = getConfig()->getAsString(CFGID_WRITE_RUNTIME_TO_FILENAME);
1684
        std::ofstream outfile;
1685
        outfile.open(filename.c_str(), std::ios::out | std::ios::app);
1686 79b044ca Georg Kunz
        outfile << elapsedtime.tv_sec << "." << std::setfill('0') << std::setw(6) << elapsedtime.tv_usec << endl;
1687 68da4f12 Georg Kunz
        outfile.close();
1688
    }
1689 01873262 Georg Kunz
}
1690
1691
timeval EnvirBase::totalElapsed()
1692
{
1693
    timeval now;
1694
    gettimeofday(&now, NULL);
1695
    return now - laststarted + elapsedtime;
1696
}
1697
1698
void EnvirBase::checkTimeLimits()
1699
{
1700
    if (opt_simtimelimit!=0 && simulation.getSimTime()>=opt_simtimelimit)
1701
         throw cTerminationException(eSIMTIME);
1702
    if (opt_cputimelimit==0) // no limit
1703
         return;
1704
    if (disable_tracing && (simulation.getEventNumber()&0xFF)!=0) // optimize: in Express mode, don't call gettimeofday() on every event
1705
         return;
1706
    timeval now;
1707
    gettimeofday(&now, NULL);
1708
    long elapsedsecs = now.tv_sec - laststarted.tv_sec + elapsedtime.tv_sec;
1709
    if (elapsedsecs>=opt_cputimelimit)
1710
         throw cTerminationException(eREALTIME);
1711
}
1712
1713
void EnvirBase::stoppedWithTerminationException(cTerminationException& e)
1714
{
1715
    // if we're running in parallel and this exception is NOT one we received
1716
    // from other partitions, then notify other partitions
1717
#ifdef WITH_PARSIM
1718
    if (opt_parsim && !dynamic_cast<cReceivedTerminationException *>(&e))
1719
        parsimpartition->broadcastTerminationException(e);
1720
#endif
1721
}
1722
1723
void EnvirBase::stoppedWithException(std::exception& e)
1724
{
1725
    // if we're running in parallel and this exception is NOT one we received
1726
    // from other partitions, then notify other partitions
1727
#ifdef WITH_PARSIM
1728
    if (opt_parsim && !dynamic_cast<cReceivedException *>(&e))
1729
        parsimpartition->broadcastException(e);
1730
#endif
1731
}
1732
1733
void EnvirBase::checkFingerprint()
1734
{
1735
    if (opt_fingerprint.empty() || !simulation.getHasher())
1736
        return;
1737
1738
    int k = 0;
1739
    StringTokenizer tokenizer(opt_fingerprint.c_str());
1740
    while (tokenizer.hasMoreTokens())
1741
    {
1742
        const char *fingerprint = tokenizer.nextToken();
1743
        if (simulation.getHasher()->equals(fingerprint))
1744
        {
1745
            printfmsg("Fingerprint successfully verified: %s", fingerprint);
1746
            return;
1747
        }
1748
        k++;
1749
    }
1750
1751
    printfmsg("Fingerprint mismatch! calculated: %s, expected: %s%s",
1752
              simulation.getHasher()->str().c_str(),
1753
              (k>=2 ? "one of: " : ""), opt_fingerprint.c_str());
1754
}
1755
1756
cModuleType *EnvirBase::resolveNetwork(const char *networkname)
1757
{
1758
    cModuleType *network = NULL;
1759
    std::string inifilePackage = simulation.getNedPackageForFolder(opt_inifile_network_dir.c_str());
1760
1761
    bool hasInifilePackage = !inifilePackage.empty() && strcmp(inifilePackage.c_str(),"-")!=0;
1762
    if (hasInifilePackage)
1763
        network = cModuleType::find((inifilePackage+"."+networkname).c_str());
1764
    if (!network)
1765
        network = cModuleType::find(networkname);
1766
    if (!network) {
1767
        if (hasInifilePackage)
1768
            throw cRuntimeError("Network `%s' or `%s' not found, check .ini and .ned files",
1769
                                networkname, (inifilePackage+"."+networkname).c_str());
1770
        else
1771
            throw cRuntimeError("Network `%s' not found, check .ini and .ned files", networkname);
1772
    }
1773
    if (!network->isNetwork())
1774
        throw cRuntimeError("Module type `%s' is not a network", network->getFullName());
1775
    return network;
1776
}
1777