Statistics
| Branch: | Revision:

root / src / envir / envirbase.cc @ c87b95b0

History | View | Annotate | Download (65.4 KB)

1
//==========================================================================
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
#include <set>
22

    
23
#include "args.h"
24
#include "envirbase.h"
25
#include "appreg.h"
26
#include "ccoroutine.h"
27
#include "csimulation.h"
28
#include "cscheduler.h"
29
#include "cpar.h"
30
#include "cproperties.h"
31
#include "cproperty.h"
32
#include "random.h"
33
#include "crng.h"
34
#include "cmodule.h"
35
#include "ccomponenttype.h"
36
#include "cxmlelement.h"
37
#include "cxmldoccache.h"
38
#include "chistogram.h"
39
#include "stringtokenizer.h"
40
#include "fnamelisttokenizer.h"
41
#include "chasher.h"
42
#include "cconfigoption.h"
43
#include "cmathfunction.h"
44
#include "cnedfunction.h"
45
#include "../nedxml/nedparser.h" // NEDParser::getBuiltInDeclarations()
46
#include "regmacros.h"
47
#include "stringutil.h"
48
#include "enumstr.h"
49
#include "simtime.h"
50
#include "resultrecorder.h"
51
#include "resultfilters.h"
52
#include "statisticparser.h"
53

    
54
#ifdef WITH_PARSIM
55
#include "cparsimcomm.h"
56
#include "parsim/cparsimpartition.h"
57
#include "parsim/cparsimsynchr.h"
58
#include "parsim/creceivedexception.h"
59
#endif
60

    
61
#include "opp_ctype.h"
62
#include "stringtokenizer.h"
63
#include "fileglobber.h"
64
#include "unitconversion.h"
65
#include "commonutil.h"
66
#include "../common/ver.h"
67

    
68
#include "timeutil.h"
69
#include "platmisc.h"
70
#include "fileutil.h"  // splitFileName
71

    
72

    
73
#ifdef USE_PORTABLE_COROUTINES /* coroutine stacks reside in main stack area */
74

    
75
# define TOTAL_STACK_SIZE   (2*1024*1024)
76
# define MAIN_STACK_SIZE       (128*1024)  // Tkenv needs more than 64K
77

    
78
#else /* nonportable coroutines, stacks are allocated on heap */
79

    
80
# define TOTAL_STACK_SIZE        0  // dummy value
81
# define MAIN_STACK_SIZE         0  // dummy value
82

    
83
#endif
84

    
85

    
86
USING_NAMESPACE
87

    
88
using std::ostream;
89

    
90
#define CREATE_BY_CLASSNAME(var,classname,baseclass,description) \
91
     baseclass *var ## _tmp = (baseclass *) createOne(classname); \
92
     var = dynamic_cast<baseclass *>(var ## _tmp); \
93
     if (!var) \
94
         throw cRuntimeError("Class \"%s\" is not subclassed from " #baseclass, (const char *)classname);
95

    
96
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.");
97
Register_GlobalConfigOption(CFGID_PARALLEL_SIMULATION, "parallel-simulation", CFG_BOOL, "false", "Enables parallel distributed simulation.");
98
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.");
99
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.");
100
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.");
101
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.");
102
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.");
103
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.");
104
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.");
105
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.");
106
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.");
107
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.");
108
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.");
109

    
110
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.");
111
Register_PerRunConfigOption(CFGID_WARNINGS, "warnings", CFG_BOOL, "true", "Enables warnings.");
112
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.");
113
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.");
114
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.");
115
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.");
116
Register_PerRunConfigOption(CFGID_NUM_RNGS, "num-rngs", CFG_INT, "1", "The number of random number generators.");
117
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).");
118
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}.");
119
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.)");
120
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.");
121
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)");
122
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.");
123
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.");
124
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");
125

    
126
// the following options are declared in other files
127
extern cConfigOption *CFGID_SCALAR_RECORDING;
128
extern cConfigOption *CFGID_VECTOR_RECORDING;
129

    
130

    
131
#define STRINGIZE0(x) #x
132
#define STRINGIZE(x) STRINGIZE0(x)
133

    
134
static const char *compilerInfo =
135
    #if defined __GNUC__
136
    "GCC " __VERSION__
137
    #elif defined _MSC_VER
138
    "Microsoft Visual C++ version " STRINGIZE(_MSC_VER)
139
    #elif defined __INTEL_COMPILER
140
    "Intel Compiler version " STRINGIZE(__INTEL_COMPILER)
141
    #else
142
    "unknown-compiler"
143
    #endif
144
    ;
145

    
146
static const char *buildInfoFormat =
147
    "%d-bit"
148

    
149
    #ifdef NDEBUG
150
    " RELEASE"
151
    #else
152
    " DEBUG"
153
    #endif
154

    
155
    " simtime_t=%s"
156
    " large-file-support=%s"
157
    ;
158

    
159
static const char *buildOptions = ""
160
    #ifdef USE_DOUBLE_SIMTIME
161
    " USE_DOUBLE_SIMTIME"
162
    #endif
163

    
164
    #ifdef WITHOUT_CPACKET
165
    " WITHOUT_CPACKET"
166
    #endif
167

    
168
    #ifdef WITH_PARSIM
169
    " WITH_PARSIM"
170
    #endif
171

    
172
    #ifdef WITH_MPI
173
    " WITH_MPI"
174
    #endif
175

    
176
    #ifdef WITH_NETBUILDER
177
    " WITH_NETBUILDER"
178
    #endif
179

    
180
    #ifdef WITH_AKAROA
181
    " WITH_AKAROA"
182
    #endif
183

    
184
    #ifdef XMLPARSER
185
    " XMLPARSER=" STRINGIZE(XMLPARSER)
186
    #endif
187
    ;
188

    
189

    
190
EnvirBase::EnvirBase()
191
{
192
    args = NULL;
193
    cfg = NULL;
194
    xmlcache = NULL;
195

    
196
    record_eventlog = false;
197
    eventlogmgr = NULL;
198
    outvectormgr = NULL;
199
    outscalarmgr = NULL;
200
    snapshotmgr = NULL;
201

    
202
    num_rngs = 0;
203
    rngs = NULL;
204

    
205
#ifdef WITH_PARSIM
206
    parsimcomm = NULL;
207
    parsimpartition = NULL;
208
#endif
209

    
210
    exitcode = 0;
211
}
212

    
213
EnvirBase::~EnvirBase()
214
{
215
    delete args;
216
    delete cfg;
217
    delete xmlcache;
218

    
219
    delete outvectormgr;
220
    delete outscalarmgr;
221
    delete snapshotmgr;
222

    
223
    for (int i = 0; i < num_rngs; i++)
224
         delete rngs[i];
225
    delete [] rngs;
226

    
227
#ifdef WITH_PARSIM
228
    delete parsimcomm;
229
    delete parsimpartition;
230
#endif
231
}
232

    
233
int EnvirBase::run(int argc, char *argv[], cConfiguration *configobject)
234
{
235
    args = new ArgList();
236
    args->parse(argc, argv, "h?f:u:l:c:r:p:n:x:agGv");  //TODO share spec with startup.cc!
237
    cfg = dynamic_cast<cConfigurationEx *>(configobject);
238
    if (!cfg)
239
        throw cRuntimeError("Cannot cast configuration object %s to cConfigurationEx", configobject->getClassName());
240

    
241
    if (simulationRequired())
242
    {
243
        if (setup())
244
            run();
245
        shutdown();
246
    }
247
    return exitcode;
248
}
249

    
250
bool EnvirBase::simulationRequired()
251
{
252
    // handle -h and -v command-line options
253
    if (args->optionGiven('h'))
254
    {
255
        const char *category = args->optionValue('h',0);
256
        if (!category)
257
            printHelp();
258
        else
259
            dumpComponentList(category);
260
        return false;
261
    }
262

    
263
    if (args->optionGiven('v'))
264
    {
265
        struct opp_stat_t statbuf;
266
        ev << "\n";
267
        ev << "Build: " OMNETPP_RELEASE " " OMNETPP_BUILDID << "\n";
268
        ev << "Compiler: " << compilerInfo << "\n";
269
        ev << "Options: " << opp_stringf(buildInfoFormat,
270
                                         8*sizeof(void*),
271
                                         opp_typename(typeid(simtime_t)),
272
                                         sizeof(statbuf.st_size)>=8 ? "yes" : "no");
273
        ev << buildOptions << "\n";
274
        return false;
275
    }
276

    
277
    cConfigurationEx *cfg = getConfigEx();
278

    
279
    // -a option: print all config names, and number of runs in them
280
    if (args->optionGiven('a'))
281
    {
282
        ev.printf("\n");
283
        std::vector<std::string> configNames = cfg->getConfigNames();
284
        for (int i=0; i<(int)configNames.size(); i++)
285
            ev.printf("Config %s: %d\n", configNames[i].c_str(), cfg->getNumRunsInConfig(configNames[i].c_str()));
286
        return false;
287
    }
288

    
289
    // '-x' option: print number of runs in the given config, and exit (overrides configname)
290
    const char *configToPrint = args->optionValue('x');
291
    if (configToPrint)
292
    {
293
        //
294
        // IMPORTANT: the simulation launcher will parse the output of this
295
        // option, so it should be modified with care and the two kept in sync
296
        // (see OmnetppLaunchUtils.getSimulationRunInfo()).
297
        //
298
        // Rules:
299
        // (1) the number of runs should appear on the rest of the line
300
        //     after the "Number of runs:" text
301
        // (2) per-run information lines should span from the "Number of runs:"
302
        //     line until the next blank line ("\n\n").
303
        //
304

    
305
        // '-g'/'-G' options: modifies -x: print unrolled config, iteration variables, etc as well
306
        bool unrollBrief = args->optionGiven('g');
307
        bool unrollDetailed = args->optionGiven('G');
308

    
309
        ev.printf("\n");
310
        ev.printf("Config: %s\n", configToPrint);
311
        ev.printf("Number of runs: %d\n", cfg->getNumRunsInConfig(configToPrint));
312

    
313
        if (unrollBrief || unrollDetailed)
314
        {
315
            std::vector<std::string> runs = cfg->unrollConfig(configToPrint, unrollDetailed);
316
            const char *fmt = unrollDetailed ? "Run %d:\n%s" : "Run %d: %s\n";
317
            for (int i=0; i<(int)runs.size(); i++)
318
                ev.printf(fmt, i, runs[i].c_str());
319
        }
320
        return false;
321
    }
322

    
323
    return true;
324
}
325

    
326
bool EnvirBase::setup()
327
{
328
    try
329
    {
330
        // ensure correct numeric format in output files
331
        setPosixLocale();
332

    
333
        // set opt_* variables from ini file(s)
334
        readOptions();
335

    
336
        // initialize coroutine library
337
        if (TOTAL_STACK_SIZE!=0 && opt_total_stack<=MAIN_STACK_SIZE+4096)
338
        {
339
            ev.printf("Total stack size %d increased to %d\n", opt_total_stack, MAIN_STACK_SIZE);
340
            opt_total_stack = MAIN_STACK_SIZE+4096;
341
        }
342
        cCoroutine::init(opt_total_stack, MAIN_STACK_SIZE);
343

    
344
        // install XML document cache
345
        xmlcache = new cXMLDocCache();
346

    
347
        // install output vector manager
348
        CREATE_BY_CLASSNAME(outvectormgr, opt_outputvectormanager_class.c_str(), cOutputVectorManager, "output vector manager");
349

    
350
        // install output scalar manager
351
        CREATE_BY_CLASSNAME(outscalarmgr, opt_outputscalarmanager_class.c_str(), cOutputScalarManager, "output scalar manager");
352

    
353
        // install snapshot manager
354
        CREATE_BY_CLASSNAME(snapshotmgr, opt_snapshotmanager_class.c_str(), cSnapshotManager, "snapshot manager");
355

    
356
        // set up for sequential or distributed execution
357
        if (!opt_parsim)
358
        {
359
            // sequential
360
            cScheduler *scheduler;
361
            CREATE_BY_CLASSNAME(scheduler, opt_scheduler_class.c_str(), cScheduler, "event scheduler");
362
            simulation.setScheduler(scheduler);
363
        }
364
        else
365
        {
366
#ifdef WITH_PARSIM
367
            // parsim: create components
368
            CREATE_BY_CLASSNAME(parsimcomm, opt_parsimcomm_class.c_str(), cParsimCommunications, "parallel simulation communications layer");
369
            parsimpartition = new cParsimPartition();
370
            cParsimSynchronizer *parsimsynchronizer;
371
            CREATE_BY_CLASSNAME(parsimsynchronizer, opt_parsimsynch_class.c_str(), cParsimSynchronizer, "parallel simulation synchronization layer");
372

    
373
            // wire them together (note: 'parsimsynchronizer' is also the scheduler for 'simulation')
374
            parsimpartition->setContext(&simulation, parsimcomm, parsimsynchronizer);
375
            parsimsynchronizer->setContext(&simulation, parsimpartition, parsimcomm);
376
            simulation.setScheduler(parsimsynchronizer);
377

    
378
            // initialize them
379
            parsimcomm->init();
380
#else
381
            throw cRuntimeError("Parallel simulation is turned on in the ini file, but OMNeT++ was compiled without parallel simulation support (WITH_PARSIM=no)");
382
#endif
383
        }
384

    
385
        // load NED files from folders on the NED path. Note: NED path is taken
386
        // from the "-n" command-line option or the NEDPATH variable ("-n" takes
387
        // precedence), and the "ned-path=" config entry gets appended to it.
388
        // If the result is still empty, we fall back to "." -- this is needed
389
        // for single-directory models to work
390
        const char *nedpath1 = args->optionValue('n',0);
391
        if (!nedpath1)
392
            nedpath1 = getenv("NEDPATH");
393
        std::string nedpath2 = getConfig()->getAsPath(CFGID_NED_PATH);
394
        std::string nedpath = opp_join(";", nedpath1, nedpath2.c_str());
395
        if (nedpath.empty())
396
            nedpath = ".";
397

    
398
        StringTokenizer tokenizer(nedpath.c_str(), PATH_SEPARATOR);
399
        std::set<std::string> foldersloaded;
400
        while (tokenizer.hasMoreTokens())
401
        {
402
            const char *folder = tokenizer.nextToken();
403
            if (foldersloaded.find(folder)==foldersloaded.end())
404
            {
405
                ev.printf("Loading NED files from %s:", folder); ev.flush();
406
                int count = simulation.loadNedSourceFolder(folder);
407
                ev.printf(" %d\n", count);
408
                foldersloaded.insert(folder);
409
            }
410
        }
411
        simulation.doneLoadingNedFiles();
412
    }
413
    catch (std::exception& e)
414
    {
415
        displayException(e);
416
        return false; // don't run the app
417
    }
418
    return true;
419
}
420

    
421
void EnvirBase::printHelp()
422
{
423
    ev << "\n";
424
    ev << "Command line options:\n";
425
    ev << "  <inifile> or -f <inifile>\n";
426
    ev << "                Use the given ini file instead of omnetpp.ini. More than one\n";
427
    ev << "                ini files can be loaded this way.\n";
428
    ev << "  -u <ui>       Selects the user interface. Standard choices are Cmdenv\n";
429
    ev << "                and Tkenv. To make a user interface available, you need\n";
430
    ev << "                to link the simulation executable with the Cmdenv/Tkenv\n";
431
    ev << "                library, or load it as shared library via the -l option.\n";
432
    ev << "  -n <nedpath>  When present, overrides the NEDPATH environment variable.\n";
433
    ev << "  -l <library>  Load the specified shared library (.so or .dll) on startup.\n";
434
    ev << "                The file name should be given without the .so or .dll suffix\n";
435
    ev << "                (it will be appended automatically.) The loaded module may\n";
436
    ev << "                contain simple modules, plugins, etc. Multiple -l options\n";
437
    ev << "                can be present.\n";
438
    ev << "  --<configuration-key>=<value>\n";
439
    ev << "                Any configuration option can be specified on the command\n";
440
    ev << "                line, and it takes precedence over settings specified in the\n";
441
    ev << "                ini file(s). Examples:\n";
442
    ev << "                      --debug-on-errors=true\n";
443
    ev << "                      --record-eventlog=true\n";
444
    ev << "                      --sim-time-limit=1000s\n";
445
    ev << "  -v            Print version and build info.\n";
446
    ev << "  -h            Print this help and exit.\n";
447
    ev << "  -h <category> Lists registered components:\n";
448
    ev << "    -h config         Prints the list of available config options\n";
449
    ev << "    -h configdetails  Prints the list of available config options, with\n";
450
    ev << "                      their documentation\n";
451
    ev << "    -h userinterfaces Lists available user interfaces (see -u option)\n";
452
    ev << "    -h classes        Lists registered C++ classes (including module classes)\n";
453
    ev << "    -h classdesc      Lists C++ classes that have associated reflection\n";
454
    ev << "                      information (needed for Tkenv inspectors)\n";
455
    ev << "    -h nedfunctions   Lists registered NED functions\n";
456
    ev << "    -h neddecls       Lists built-in NED component declarations\n";
457
    ev << "    -h units          Lists recognized physical units\n";
458
    ev << "    -h enums          Lists registered enums\n";
459
    ev << "    -h resultfilters  Lists result filters\n";
460
    ev << "    -h resultrecorders Lists result recorders\n";
461
    ev << "    -h all            Union of all the above\n";
462
    ev << "\n";
463

    
464
    // print specific help for each user interface
465
    cRegistrationList *table = omnetapps.getInstance();
466
    table->sort();
467
    for (int i=0; i<table->size(); i++)
468
    {
469
        // instantiate the ui, call printUISpecificHelp(), then dispose.
470
        // note: their ctors are not supposed to do anything but trivial member initializations
471
        cOmnetAppRegistration *appreg = check_and_cast<cOmnetAppRegistration *>(table->get(i));
472
        cEnvir *app = appreg->createOne();
473
        if (dynamic_cast<EnvirBase *>(app))
474
            ((EnvirBase *)app)->printUISpecificHelp();
475
        delete app;
476
    }
477
}
478

    
479
void EnvirBase::dumpComponentList(const char *category)
480
{
481
    bool wantAll = !strcmp(category, "all");
482
    bool processed = false;
483
    if (wantAll || !strcmp(category, "config") || !strcmp(category, "configdetails"))
484
    {
485
        processed = true;
486
        ev << "Supported configuration options:\n";
487
        bool printDescriptions = strcmp(category, "configdetails")==0;
488

    
489
        cRegistrationList *table = configOptions.getInstance();
490
        table->sort();
491
        for (int i=0; i<table->size(); i++)
492
        {
493
            cConfigOption *obj = dynamic_cast<cConfigOption *>(table->get(i));
494
            ASSERT(obj);
495
            if (!printDescriptions) ev << "  ";
496
            if (obj->isPerObject()) ev << "<object-full-path>.";
497
            ev << obj->getName() << "=";
498
            ev << "<" << cConfigOption::getTypeName(obj->getType()) << ">";
499
            if (obj->getUnit())
500
                ev << ", unit=\"" << obj->getUnit() << "\"";
501
            if (obj->getDefaultValue())
502
                ev << ", default:" << obj->getDefaultValue() << "";
503
            ev << "; " << (obj->isGlobal() ? "global" : obj->isPerObject() ? "per-object" : "per-run") << " setting";
504
            ev << "\n";
505
            if (printDescriptions && !opp_isempty(obj->getDescription()))
506
                ev << opp_indentlines(opp_breaklines(obj->getDescription(),75).c_str(), "    ") << "\n";
507
            if (printDescriptions) ev << "\n";
508
        }
509
        ev << "\n";
510

    
511
        ev << "Predefined variables that can be used in config values:\n";
512
        std::vector<const char *> v = getConfigEx()->getPredefinedVariableNames();
513
        for (int i=0; i<(int)v.size(); i++)
514
        {
515
            if (!printDescriptions) ev << "  ";
516
            ev << "${" << v[i] << "}\n";
517
            const char *desc = getConfigEx()->getVariableDescription(v[i]);
518
            if (printDescriptions && !opp_isempty(desc))
519
                ev << opp_indentlines(opp_breaklines(desc,75).c_str(), "    ") << "\n";
520
        }
521
        ev << "\n";
522
    }
523
    if (!strcmp(category, "jconfig")) // internal undocumented option, for maintenance purposes
524
    {
525
        // generate Java code for ConfigurationRegistry.java in the IDE
526
        processed = true;
527
        ev << "Supported configuration options (as Java code):\n";
528
        cRegistrationList *table = configOptions.getInstance();
529
        table->sort();
530
        for (int i=0; i<table->size(); i++)
531
        {
532
            cConfigOption *key = dynamic_cast<cConfigOption *>(table->get(i));
533
            ASSERT(key);
534

    
535
            std::string id = "CFGID_";
536
            for (const char *s = key->getName(); *s; s++)
537
                id.append(1, opp_isalpha(*s) ? opp_toupper(*s) : *s=='-' ? '_' : *s=='%' ? 'n' : *s);
538
            const char *method = key->isGlobal() ? "addGlobalOption" :
539
                                 !key->isPerObject() ? "addPerRunOption" :
540
                                 "addPerObjectOption";
541
            #define CASE(X)  case cConfigOption::X: typestring = #X; break;
542
            const char *typestring;
543
            switch (key->getType()) {
544
                CASE(CFG_BOOL)
545
                CASE(CFG_INT)
546
                CASE(CFG_DOUBLE)
547
                CASE(CFG_STRING)
548
                CASE(CFG_FILENAME)
549
                CASE(CFG_FILENAMES)
550
                CASE(CFG_PATH)
551
                CASE(CFG_CUSTOM)
552
            }
553
            #undef CASE
554

    
555
            ev << "    public static final ConfigOption " << id << " = ";
556
            ev << method << (key->getUnit() ? "U" : "") << "(\n";
557
            ev << "        \"" << key->getName() << "\", ";
558
            if (!key->getUnit())
559
                ev << typestring << ", ";
560
            else
561
                ev << "\"" << key->getUnit() << "\", ";
562
            if (!key->getDefaultValue())
563
                ev << "null";
564
            else
565
                ev << "\"" << opp_replacesubstring(key->getDefaultValue(), "\"", "\\\"", true) << "\"";
566
            ev << ",\n";
567

    
568
            std::string desc = key->getDescription();
569
            desc = opp_replacesubstring(desc.c_str(), "\n", "\\n\n", true); // keep explicit line breaks
570
            desc = opp_breaklines(desc.c_str(), 75);  // break long lines
571
            desc = opp_replacesubstring(desc.c_str(), "\"", "\\\"", true);
572
            desc = opp_replacesubstring(desc.c_str(), "\n", " \" +\n\"", true);
573
            desc = opp_replacesubstring(desc.c_str(), "\\n \"", "\\n\"", true); // remove bogus space after explicit line breaks
574
            desc = "\"" + desc + "\"";
575

    
576
            ev << opp_indentlines(desc.c_str(), "        ") << ");\n";
577
        }
578
        ev << "\n";
579

    
580
        std::vector<const char *> vars = getConfigEx()->getPredefinedVariableNames();
581
        for (int i=0; i<(int)vars.size(); i++)
582
        {
583
            opp_string id = vars[i];
584
            opp_strupr(id.buffer());
585
            const char *desc = getConfigEx()->getVariableDescription(vars[i]);
586
            ev << "    public static final String CFGVAR_" << id << " = addConfigVariable(";
587
            ev << "\"" << vars[i] << "\", \"" << opp_replacesubstring(desc, "\"", "\\\"", true) << "\");\n";
588
        }
589
        ev << "\n";
590
    }
591
    if (wantAll || !strcmp(category, "classes"))
592
    {
593
        processed = true;
594
        ev << "Registered C++ classes, including modules, channels and messages:\n";
595
        cRegistrationList *table = classes.getInstance();
596
        table->sort();
597
        for (int i=0; i<table->size(); i++)
598
        {
599
            cObject *obj = table->get(i);
600
            ev << "  class " << obj->getFullName() << "\n";
601
        }
602
        ev << "Note: if your class is not listed, it needs to be registered in the\n";
603
        ev << "C++ code using Define_Module(), Define_Channel() or Register_Class().\n";
604
        ev << "\n";
605
    }
606
    if (wantAll || !strcmp(category, "classdesc"))
607
    {
608
        processed = true;
609
        ev << "Classes that have associated reflection information (needed for Tkenv inspectors):\n";
610
        cRegistrationList *table = classDescriptors.getInstance();
611
        table->sort();
612
        for (int i=0; i<table->size(); i++)
613
        {
614
            cObject *obj = table->get(i);
615
            ev << "  class " << obj->getFullName() << "\n";
616
        }
617
        ev << "\n";
618
    }
619
    if (wantAll || !strcmp(category, "nedfunctions"))
620
    {
621
        processed = true;
622
        ev << "Functions that can be used in NED expressions and in omnetpp.ini:\n";
623
        cRegistrationList *table = nedFunctions.getInstance();
624
        table->sort();
625
        std::set<std::string> categories;
626
        for (int i=0; i<table->size(); i++)
627
        {
628
            cNEDFunction *nf = dynamic_cast<cNEDFunction *>(table->get(i));
629
            cMathFunction *mf = dynamic_cast<cMathFunction *>(table->get(i));
630
            categories.insert(nf ? nf->getCategory() : mf ? mf->getCategory() : "???");
631
        }
632
        for (std::set<std::string>::iterator ci=categories.begin(); ci!=categories.end(); ++ci)
633
        {
634
            std::string category = (*ci);
635
            ev << "\n Category \"" << category << "\":\n";
636
            for (int i=0; i<table->size(); i++)
637
            {
638
                cObject *obj = table->get(i);
639
                cNEDFunction *nf = dynamic_cast<cNEDFunction *>(table->get(i));
640
                cMathFunction *mf = dynamic_cast<cMathFunction *>(table->get(i));
641
                const char *fcat = nf ? nf->getCategory() : mf ? mf->getCategory() : "???";
642
                const char *desc = nf ? nf->getDescription() : mf ? mf->getDescription() : "???";
643
                if (fcat==category)
644
                {
645
                    ev << "  " << obj->getFullName() << " : " << obj->info() << "\n";
646
                    if (desc)
647
                        ev << "    " << desc << "\n";
648
                }
649
            }
650
        }
651
        ev << "\n";
652
    }
653
    if (wantAll || !strcmp(category, "neddecls"))
654
    {
655
        processed = true;
656
        ev << "Built-in NED declarations:\n\n";
657
        ev << "---START---\n";
658
        ev << NEDParser::getBuiltInDeclarations();
659
        ev << "---END---\n";
660
        ev << "\n";
661
    }
662
    if (wantAll || !strcmp(category, "units"))
663
    {
664
        processed = true;
665
        ev << "Recognized physical units (note: other units can be used as well, only\n";
666
        ev << "no automatic conversion will be available for them):\n";
667
        std::vector<const char *> units = UnitConversion::getAllUnits();
668
        for (int i=0; i<(int)units.size(); i++)
669
        {
670
            const char *u = units[i];
671
            const char *bu = UnitConversion::getBaseUnit(u);
672
            ev << "  " << u << "\t" << UnitConversion::getLongName(u);
673
            if (opp_strcmp(u,bu)!=0)
674
                ev << "\t" << UnitConversion::convertUnit(1,u,bu) << bu;
675
            ev << "\n";
676
        }
677
        ev << "\n";
678
    }
679
    if (wantAll || !strcmp(category, "enums"))
680
    {
681
        processed = true;
682
        ev << "Enums defined in .msg files\n";
683
        cRegistrationList *table = enums.getInstance();
684
        table->sort();
685
        for (int i=0; i<table->size(); i++)
686
        {
687
            cObject *obj = table->get(i);
688
            ev << "  " << obj->getFullName() << " : " << obj->info() << "\n";
689
        }
690
        ev << "\n";
691
    }
692
    if (wantAll || !strcmp(category, "userinterfaces"))
693
    {
694
        processed = true;
695
        ev << "User interfaces loaded:\n";
696
        cRegistrationList *table = omnetapps.getInstance();
697
        table->sort();
698
        for (int i=0; i<table->size(); i++)
699
        {
700
            cObject *obj = table->get(i);
701
            ev << "  " << obj->getFullName() << " : " << obj->info() << "\n";
702
        }
703
    }
704

    
705
    if (wantAll || !strcmp(category, "resultfilters"))
706
    {
707
        processed = true;
708
        ev << "Result filters that can be used in @statistic properties:\n";
709
        cRegistrationList *table = resultFilters.getInstance();
710
        table->sort();
711
        for (int i=0; i<table->size(); i++)
712
        {
713
            cObject *obj = table->get(i);
714
            ev << "  " << obj->getFullName() << " : " << obj->info() << "\n";
715
        }
716
    }
717

    
718
    if (wantAll || !strcmp(category, "resultrecorders"))
719
    {
720
        processed = true;
721
        ev << "Result recorders that can be used in @statistic properties:\n";
722
        cRegistrationList *table = resultRecorders.getInstance();
723
        table->sort();
724
        for (int i=0; i<table->size(); i++)
725
        {
726
            cObject *obj = table->get(i);
727
            ev << "  " << obj->getFullName() << " : " << obj->info() << "\n";
728
        }
729
    }
730

    
731
    if (!processed)
732
        throw cRuntimeError("Unrecognized category for '-h' option: %s", category);
733
}
734

    
735
int EnvirBase::getParsimProcId() const
736
{
737
#ifdef WITH_PARSIM
738
    return parsimcomm ? parsimcomm->getProcId() : 0;
739
#else
740
    return 0;
741
#endif
742
}
743

    
744
int EnvirBase::getParsimNumPartitions() const
745
{
746
#ifdef WITH_PARSIM
747
    return parsimcomm ? parsimcomm->getNumPartitions() : 0;
748
#else
749
    return 0;
750
#endif
751
}
752

    
753

    
754
void EnvirBase::shutdown()
755
{
756
    try
757
    {
758
        simulation.deleteNetwork();
759
#ifdef WITH_PARSIM
760
        if (opt_parsim && parsimpartition)
761
            parsimpartition->shutdown();
762
#endif
763
    }
764
    catch (std::exception& e)
765
    {
766
        displayException(e);
767
    }
768
}
769

    
770
void EnvirBase::startRun()
771
{
772
    resetClock();
773
    outvectormgr->startRun();
774
    outscalarmgr->startRun();
775
    snapshotmgr->startRun();
776
    if (record_eventlog)
777
        eventlogmgr->startRun();
778
    if (opt_parsim)
779
    {
780
#ifdef WITH_PARSIM
781
        parsimpartition->startRun();
782
#endif
783
    }
784
    simulation.startRun();
785
    flushLastLine();
786
}
787

    
788
void EnvirBase::endRun()
789
{
790
    // reverse order as startRun()
791
    simulation.endRun();
792
    if (opt_parsim)
793
    {
794
#ifdef WITH_PARSIM
795
        parsimpartition->endRun();
796
#endif
797
    }
798
    if (record_eventlog) {
799
        eventlogmgr->endRun();
800
        delete eventlogmgr;
801
        eventlogmgr = NULL;
802
        record_eventlog = false;
803
    }
804
    snapshotmgr->endRun();
805
    outscalarmgr->endRun();
806
    outvectormgr->endRun();
807
}
808

    
809
//-------------------------------------------------------------
810

    
811
void EnvirBase::configure(cComponent *component)
812
{
813
    addResultRecorders(component);
814
}
815

    
816
static int search_(std::vector<std::string>& v, const char *s)
817
{
818
    for (int i=0; i<(int)v.size(); i++)
819
        if (strcmp(v[i].c_str(), s)==0)
820
            return i;
821
    return -1;
822
}
823

    
824
inline void addIfNotContains_(std::vector<std::string>& v, const char *s)
825
{
826
    if (search_(v,s)==-1)
827
        v.push_back(s);
828
}
829

    
830
inline void addIfNotContains_(std::vector<std::string>& v, const std::string& s)
831
{
832
    if (search_(v,s.c_str())==-1)
833
        v.push_back(s);
834
}
835

    
836
void EnvirBase::addResultRecorders(cComponent *component)
837
{
838
    std::vector<const char *> statisticNames = component->getProperties()->getIndicesFor("statistic");
839
    std::string componentFullPath;
840
    for (int i = 0; i < (int)statisticNames.size(); i++)
841
    {
842
        const char *statisticName = statisticNames[i];
843
        if (componentFullPath.empty())
844
            componentFullPath = component->getFullPath();
845
        std::string statisticFullPath = componentFullPath + "." + statisticName;
846

    
847
        bool scalarsEnabled = ev.getConfig()->getAsBool(statisticFullPath.c_str(), CFGID_SCALAR_RECORDING);
848
        bool vectorsEnabled = ev.getConfig()->getAsBool(statisticFullPath.c_str(), CFGID_VECTOR_RECORDING);
849
        if (!scalarsEnabled && !vectorsEnabled)
850
            continue;
851

    
852
        cProperty *statisticProperty = component->getProperties()->get("statistic", statisticName);
853
        ASSERT(statisticProperty!=NULL);
854

    
855
        // collect the list of result recorders
856
        std::string modesOption = ev.getConfig()->getAsString(statisticFullPath.c_str(), CFGID_RESULT_RECORDING_MODES, "");
857
        std::vector<std::string> modes = extractRecorderList(modesOption.c_str(), statisticProperty);
858

    
859
        // if there are result recorders, add source filters and recorders
860
        if (!modes.empty())
861
        {
862
            // add source
863
            bool hasSourceKey = statisticProperty->getNumValues("source") > 0;
864
            const char *sourceSpec = hasSourceKey ? statisticProperty->getValue("source",0) : statisticName;
865
            SignalSource source = doStatisticSource(component, statisticName, sourceSpec, opt_warmupperiod!=0);
866

    
867
            // add result recorders
868
            for (int j = 0; j < (int)modes.size(); j++)
869
                doResultRecorder(source, modes[j].c_str(), scalarsEnabled, vectorsEnabled, component, statisticName);
870
        }
871
    }
872

    
873
    if (opt_debug_statistics_recording)
874
        dumpResultRecorders(component);
875
}
876

    
877
std::vector<std::string> EnvirBase::extractRecorderList(const char *modesOption, cProperty *statisticProperty)
878
{
879
    // "-" means "none"
880
    if (!modesOption[0] || (modesOption[0]=='-' && !modesOption[1]))
881
        return std::vector<std::string>();
882

    
883
    std::vector<std::string> modes; // the result
884

    
885
    // if first configured mode starts with '+' or '-', assume "default" as base
886
    if (modesOption[0]=='-' || modesOption[0]=='+')
887
    {
888
        // collect the mandatory record= items from @statistic (those not ending in '?')
889
        int n = statisticProperty->getNumValues("record");
890
        for (int i = 0; i < n; i++) {
891
            const char *m = statisticProperty->getValue("record", i);
892
            if (m[strlen(m)-1] != '?')
893
                addIfNotContains_(modes, m);
894
        }
895
    }
896

    
897
    // loop through all modes
898
    StringTokenizer tokenizer(modesOption, ","); //XXX we should ignore commas within parens
899
    while (tokenizer.hasMoreTokens())
900
    {
901
        const char *mode = tokenizer.nextToken();
902
        if (!strcmp(mode, "default"))
903
        {
904
            // collect the mandatory record= items from @statistic (those not ending in '?')
905
            int n = statisticProperty->getNumValues("record");
906
            for (int i = 0; i < n; i++) {
907
                const char *m = statisticProperty->getValue("record", i);
908
                if (m[strlen(m)-1] != '?')
909
                    addIfNotContains_(modes, m);
910
            }
911
        }
912
        else if (!strcmp(mode, "all"))
913
        {
914
            // collect all record= items from @statistic (strip trailing '?' if present)
915
            int n = statisticProperty->getNumValues("record");
916
            for (int i = 0; i < n; i++) {
917
                const char *m = statisticProperty->getValue("record", i);
918
                if (m[strlen(m)-1] != '?')
919
                    addIfNotContains_(modes, m);
920
                else
921
                    addIfNotContains_(modes, std::string(m, strlen(m)-1));
922
            }
923
        }
924
        else if (mode[0] == '-')
925
        {
926
            // remove from modes
927
            int k = search_(modes, mode+1);
928
            if (k != -1)
929
                modes.erase(modes.begin()+k);
930
        }
931
        else {
932
            // add to modes
933
            addIfNotContains_(modes, mode[0]=='+' ? mode+1 : mode);
934
        }
935
    }
936
    return modes;
937
}
938

    
939
static bool opp_isidentifier(const char *s)
940
{
941
    if (!opp_isalpha(s[0]) && s[0]!='_')
942
        return false;
943
    while (*++s)
944
        if (!opp_isalnum(*s))
945
            return false;
946
    return true;
947
}
948

    
949
SignalSource EnvirBase::doStatisticSource(cComponent *component, const char *statisticName, const char *sourceSpec, bool needWarmupFilter)
950
{
951
    try
952
    {
953
        if (opp_isidentifier(sourceSpec))
954
        {
955
            // simple case: just a signal name
956
            simsignal_t signalID = cComponent::registerSignal(sourceSpec);
957
            if (!needWarmupFilter)
958
                return SignalSource(component, signalID);
959
            else
960
            {
961
                WarmupPeriodFilter *warmupFilter = new WarmupPeriodFilter();
962
                component->subscribe(signalID, warmupFilter);
963
                return SignalSource(warmupFilter);
964
            }
965
        }
966
        else
967
        {
968
            StatisticSourceParser parser;
969
            return parser.parse(component, statisticName, sourceSpec, needWarmupFilter);
970
        }
971
    }
972
    catch (std::exception& e)
973
    {
974
        throw cRuntimeError("Error adding statistic '%s' to module %s (NED type: %s): error in source=%s: %s",
975
            statisticName, component->getFullPath().c_str(), component->getNedTypeName(), sourceSpec, e.what());
976
    }
977
}
978

    
979
void EnvirBase::doResultRecorder(const SignalSource& source, const char *recordingMode, bool scalarsEnabled, bool vectorsEnabled, cComponent *component, const char *statisticName)
980
{
981
    try
982
    {
983
        if (opp_isidentifier(recordingMode))
984
        {
985
            // simple case: just a plain recorder
986
            bool recordsVector = !strcmp(recordingMode, "vector");  // the only vector recorder is "vector"
987
            if (recordsVector ? !vectorsEnabled : !scalarsEnabled)
988
                return; // no point in creating if recording is disabled
989

    
990
            ResultRecorder *recorder = ResultRecorderDescriptor::get(recordingMode)->create();
991
            recorder->init(component, statisticName, recordingMode);
992
            source.subscribe(recorder);
993
        }
994
        else
995
        {
996
            // something more complicated: use parser
997
            StatisticRecorderParser parser;
998
            parser.parse(source, recordingMode, scalarsEnabled, vectorsEnabled, component, statisticName);
999
        }
1000
    }
1001
    catch (std::exception& e)
1002
    {
1003
        throw cRuntimeError("Error adding statistic '%s' to module %s (NED type: %s): bad recording mode '%s': %s",
1004
            statisticName, component->getFullPath().c_str(), component->getNedTypeName(), recordingMode, e.what());
1005
    }
1006
}
1007

    
1008
void EnvirBase::dumpResultRecorders(cComponent *component)
1009
{
1010
    bool componentPathPrinted = false;
1011
    std::vector<simsignal_t> signals = component->getLocalListenedSignals();
1012
    for (unsigned int i = 0; i < signals.size(); i++)
1013
    {
1014
        bool signalNamePrinted = false;
1015
        simsignal_t signalID = signals[i];
1016
        std::vector<cIListener*> listeners = component->getLocalSignalListeners(signalID);
1017
        for (unsigned int j = 0; j < listeners.size(); j++) {
1018
            if (dynamic_cast<ResultListener*>(listeners[j])) {
1019
                if (!componentPathPrinted) {
1020
                    ev << component->getFullPath() << " (" << component->getNedTypeName() << "):\n";
1021
                    componentPathPrinted = true;
1022
                }
1023
                if (!signalNamePrinted) {
1024
                    ev << "    \"" << cComponent::getSignalName(signalID) << "\" (signalID="  << signalID << "):\n";
1025
                    signalNamePrinted = true;
1026
                }
1027
                dumpResultRecorderChain((ResultListener *)listeners[j], 0);
1028
            }
1029
        }
1030
    }
1031
}
1032

    
1033
void EnvirBase::dumpResultRecorderChain(ResultListener *listener, int depth)
1034
{
1035
    for (int i = 0; i < depth+2; i++)
1036
        ev << "    ";
1037
    ev << listener->str();
1038
    if (dynamic_cast<ResultRecorder*>(listener))
1039
        ev << " ==> " << ((ResultRecorder*)listener)->getResultName();
1040
    ev << "\n";
1041

    
1042
    if (dynamic_cast<ResultFilter *>(listener))
1043
    {
1044
        std::vector<ResultListener *> delegates = ((ResultFilter*)listener)->getDelegates();
1045
        for (unsigned int i=0; i < delegates.size(); i++)
1046
            dumpResultRecorderChain(delegates[i], depth+1);
1047
    }
1048
}
1049

    
1050
void EnvirBase::readParameter(cPar *par)
1051
{
1052
    ASSERT(!par->isSet());  // must be unset at this point
1053

    
1054
    // get it from the ini file
1055
    std::string moduleFullPath = par->getOwner()->getFullPath();
1056
    const char *str = getConfigEx()->getParameterValue(moduleFullPath.c_str(), par->getName(), par->containsValue());
1057

    
1058
/* XXX hack to use base directory for resolving xml files location has been commented out
1059
 * FIXME a solution needs to be worked out!
1060
    if (str[0]=='x' && !strncmp(str,"xmldoc",6) && !opp_isalnum(str[6]))
1061
    {
1062
        // Make XML file location relative to the ini file in which it occurs.
1063
        // Find substring between first two quotes (that is, the XML filename),
1064
        // and prefix it with the directory.
1065
        const char *begQuote = strchr(str+6,'"');
1066
        if (!begQuote)
1067
            return std::string(str);
1068
        const char *endQuote = strchr(begQuote+1,'"');
1069
        while (endQuote && *(endQuote-1)=='\\' && *(endQuote-2)!='\\')
1070
            endQuote = strchr(endQuote+1,'"');
1071
        if (!endQuote)
1072
            return std::string(str);
1073
        std::string fname(begQuote+1, endQuote-begQuote-1);
1074
        const char *baseDir = getConfig()->getBaseDirectoryFor(NULL, "Parameters", parname);
1075
        fname = tidyFilename(concatDirAndFile(baseDir, fname.c_str()).c_str(),true);
1076
        std::string ret = std::string(str, begQuote-str+1) + fname + endQuote;
1077
        //XXX use "ret" further!!!
1078
    }
1079
*/
1080

    
1081
    if (opp_strcmp(str, "default")==0)
1082
    {
1083
        ASSERT(par->containsValue());  // cConfiguration should not return "=default" lines for params that have no default value
1084
        par->acceptDefault();
1085
    }
1086
    else if (opp_strcmp(str, "ask")==0)
1087
    {
1088
        askParameter(par, false);
1089
    }
1090
    else if (!opp_isempty(str))
1091
    {
1092
        par->parse(str);
1093
    }
1094
    else
1095
    {
1096
        // str empty: no value in the ini file
1097
        if (par->containsValue())
1098
            par->acceptDefault();
1099
        else
1100
            askParameter(par, true);
1101
    }
1102
}
1103

    
1104
bool EnvirBase::isModuleLocal(cModule *parentmod, const char *modname, int index)
1105
{
1106
#ifdef WITH_PARSIM
1107
    if (!opt_parsim)
1108
       return true;
1109

    
1110
    // toplevel module is local everywhere
1111
    if (!parentmod)
1112
       return true;
1113

    
1114
    // find out if this module is (or has any submodules that are) on this partition
1115
    char parname[MAX_OBJECTFULLPATH];
1116
    if (index<0)
1117
        sprintf(parname,"%s.%s", parentmod->getFullPath().c_str(), modname);
1118
    else
1119
        sprintf(parname,"%s.%s[%d]", parentmod->getFullPath().c_str(), modname, index); //FIXME this is incorrectly chosen for non-vector modules too!
1120
    std::string procIds = getConfig()->getAsString(parname, CFGID_PARTITION_ID, "");
1121
    if (procIds.empty())
1122
    {
1123
        // modules inherit the setting from their parents, except when the parent is the system module (the network) itself
1124
        if (!parentmod->getParentModule())
1125
            throw cRuntimeError("incomplete partitioning: missing value for '%s'",parname);
1126
        // "true" means "inherit", because an ancestor which answered "false" doesn't get recursed into
1127
        return true;
1128
    }
1129
    else if (strcmp(procIds.c_str(), "*") == 0)
1130
    {
1131
        // present on all partitions (provided that ancestors have "*" set as well)
1132
        return true;
1133
    }
1134
    else
1135
    {
1136
        // we expect a partition Id (or partition Ids, separated by commas) where this
1137
        // module needs to be instantiated. So we return true if any of the numbers
1138
        // is the Id of the local partition, otherwise false.
1139
        EnumStringIterator procIdIter(procIds.c_str());
1140
        if (procIdIter.hasError())
1141
            throw cRuntimeError("wrong partitioning: syntax error in value '%s' for '%s' "
1142
                                "(allowed syntax: '', '*', '1', '0,3,5-7')",
1143
                                procIds.c_str(), parname);
1144
        int numPartitions = parsimcomm->getNumPartitions();
1145
        int myProcId = parsimcomm->getProcId();
1146
        for (; procIdIter()!=-1; procIdIter++)
1147
        {
1148
            if (procIdIter() >= numPartitions)
1149
                throw cRuntimeError("wrong partitioning: value %d too large for '%s' (total partitions=%d)",
1150
                                    procIdIter(), parname, numPartitions);
1151
            if (procIdIter() == myProcId)
1152
                return true;
1153
        }
1154
        return false;
1155
    }
1156
#else
1157
    return true;
1158
#endif
1159
}
1160

    
1161
cXMLElement *EnvirBase::getXMLDocument(const char *filename, const char *path)
1162
{
1163
    cXMLElement *documentnode = xmlcache->getDocument(filename);
1164
    assert(documentnode);
1165
    if (path)
1166
    {
1167
        ModNameParamResolver resolver(simulation.getContextModule()); // resolves $MODULE_NAME etc in XPath expr.
1168
        return cXMLElement::getDocumentElementByPath(documentnode, path, &resolver);
1169
    }
1170
    else
1171
    {
1172
        // returns the root element (child of the document node)
1173
        return documentnode->getFirstChild();
1174
    }
1175
}
1176

    
1177
void EnvirBase::forgetXMLDocument(const char *filename)
1178
{
1179
    xmlcache->forgetDocument(filename);
1180
}
1181

    
1182
void EnvirBase::flushXMLDocumentCache()
1183
{
1184
    xmlcache->flushCache();
1185
}
1186

    
1187
cConfiguration *EnvirBase::getConfig()
1188
{
1189
    return cfg;
1190
}
1191

    
1192
cConfigurationEx *EnvirBase::getConfigEx()
1193
{
1194
    return cfg;
1195
}
1196

    
1197
//-------------------------------------------------------------
1198

    
1199
void EnvirBase::bubble(cComponent *component, const char *text)
1200
{
1201
    if (record_eventlog)
1202
        eventlogmgr->bubble(component, text);
1203
}
1204

    
1205
void EnvirBase::objectDeleted(cObject *object)
1206
{
1207
    // TODO?
1208
}
1209

    
1210
void EnvirBase::simulationEvent(cMessage *msg)
1211
{
1212
    if (record_eventlog)
1213
        eventlogmgr->simulationEvent(msg);
1214
}
1215

    
1216
void EnvirBase::beginSend(cMessage *msg)
1217
{
1218
    if (record_eventlog)
1219
        eventlogmgr->beginSend(msg);
1220
}
1221

    
1222
void EnvirBase::messageScheduled(cMessage *msg)
1223
{
1224
    if (record_eventlog)
1225
        eventlogmgr->messageScheduled(msg);
1226
}
1227

    
1228
void EnvirBase::messageCancelled(cMessage *msg)
1229
{
1230
    if (record_eventlog)
1231
        eventlogmgr->messageCancelled(msg);
1232
}
1233

    
1234
void EnvirBase::messageSendDirect(cMessage *msg, cGate *toGate, simtime_t propagationDelay, simtime_t transmissionDelay)
1235
{
1236
    if (record_eventlog)
1237
        eventlogmgr->messageSendDirect(msg, toGate, propagationDelay, transmissionDelay);
1238
}
1239

    
1240
void EnvirBase::messageSendHop(cMessage *msg, cGate *srcGate)
1241
{
1242
    if (record_eventlog)
1243
        eventlogmgr->messageSendHop(msg, srcGate);
1244
}
1245

    
1246
void EnvirBase::messageSendHop(cMessage *msg, cGate *srcGate, simtime_t propagationDelay, simtime_t transmissionDelay)
1247
{
1248
    if (record_eventlog)
1249
        eventlogmgr->messageSendHop(msg, srcGate, propagationDelay, transmissionDelay);
1250
}
1251

    
1252
void EnvirBase::endSend(cMessage *msg)
1253
{
1254
    if (record_eventlog)
1255
        eventlogmgr->endSend(msg);
1256
}
1257

    
1258
void EnvirBase::messageDeleted(cMessage *msg)
1259
{
1260
    if (record_eventlog)
1261
        eventlogmgr->messageDeleted(msg);
1262
}
1263

    
1264
void EnvirBase::componentMethodBegin(cComponent *from, cComponent *to, const char *methodFmt, va_list va, bool silent)
1265
{
1266
    if (record_eventlog)
1267
        eventlogmgr->componentMethodBegin(from, to, methodFmt, va);
1268
}
1269

    
1270
void EnvirBase::componentMethodEnd()
1271
{
1272
    if (record_eventlog)
1273
        eventlogmgr->componentMethodEnd();
1274
}
1275

    
1276
void EnvirBase::moduleCreated(cModule *newmodule)
1277
{
1278
    if (record_eventlog)
1279
        eventlogmgr->moduleCreated(newmodule);
1280
}
1281

    
1282
void EnvirBase::moduleDeleted(cModule *module)
1283
{
1284
    if (record_eventlog)
1285
        eventlogmgr->moduleDeleted(module);
1286
}
1287

    
1288
void EnvirBase::moduleReparented(cModule *module, cModule *oldparent)
1289
{
1290
    if (record_eventlog)
1291
        eventlogmgr->moduleReparented(module, oldparent);
1292
}
1293

    
1294
void EnvirBase::gateCreated(cGate *newgate)
1295
{
1296
    if (record_eventlog)
1297
        eventlogmgr->gateCreated(newgate);
1298
}
1299

    
1300
void EnvirBase::gateDeleted(cGate *gate)
1301
{
1302
    if (record_eventlog)
1303
        eventlogmgr->gateDeleted(gate);
1304
}
1305

    
1306
void EnvirBase::connectionCreated(cGate *srcgate)
1307
{
1308
    if (record_eventlog)
1309
        eventlogmgr->connectionCreated(srcgate);
1310
}
1311

    
1312
void EnvirBase::connectionDeleted(cGate *srcgate)
1313
{
1314
    if (record_eventlog)
1315
        eventlogmgr->connectionDeleted(srcgate);
1316
}
1317

    
1318
void EnvirBase::displayStringChanged(cComponent *component)
1319
{
1320
    if (record_eventlog)
1321
        eventlogmgr->displayStringChanged(component);
1322
}
1323

    
1324
void EnvirBase::sputn(const char *s, int n)
1325
{
1326
    if (record_eventlog)
1327
        eventlogmgr->sputn(s, n);
1328
}
1329

    
1330
void EnvirBase::undisposedObject(cObject *obj)
1331
{
1332
    if (opt_print_undisposed)
1333
        ::printf("undisposed object: (%s) %s -- check module destructor\n", obj->getClassName(), obj->getFullPath().c_str());
1334
}
1335

    
1336
//-------------------------------------------------------------
1337

    
1338
void EnvirBase::processFileName(opp_string& fname)
1339
{
1340
    std::string text = fname.c_str();
1341

    
1342
    // insert ".<hostname>.<pid>" if requested before file extension
1343
    // (note: parsimProcId cannot be appended because of initialization order)
1344
    if (opt_fname_append_host)
1345
    {
1346
        std::string extension = "";
1347
        std::string::size_type index = text.rfind('.');
1348
        if (index != std::string::npos) {
1349
          extension = std::string(text, index);
1350
          text.erase(index);
1351
        }
1352

    
1353
        const char *hostname=getenv("HOST");
1354
        if (!hostname)
1355
            hostname=getenv("HOSTNAME");
1356
        if (!hostname)
1357
            hostname=getenv("COMPUTERNAME");
1358
        if (!hostname)
1359
            throw cRuntimeError("Cannot append hostname to file name `%s': no HOST, HOSTNAME "
1360
                                "or COMPUTERNAME (Windows) environment variable",
1361
                                fname.c_str());
1362
        int pid = getpid();
1363

    
1364
        // append
1365
        text += opp_stringf(".%s.%d%s", hostname, pid, extension.c_str());
1366
    }
1367
    fname = text.c_str();
1368
}
1369

    
1370
void EnvirBase::readOptions()
1371
{
1372
    cConfiguration *cfg = getConfig();
1373

    
1374
    opt_total_stack = (size_t) cfg->getAsDouble(CFGID_TOTAL_STACK, TOTAL_STACK_SIZE);
1375
    opt_parsim = cfg->getAsBool(CFGID_PARALLEL_SIMULATION);
1376
    if (!opt_parsim)
1377
    {
1378
        opt_scheduler_class = cfg->getAsString(CFGID_SCHEDULER_CLASS);
1379
    }
1380
    else
1381
    {
1382
#ifdef WITH_PARSIM
1383
        opt_parsimcomm_class = cfg->getAsString(CFGID_PARSIM_COMMUNICATIONS_CLASS);
1384
        opt_parsimsynch_class = cfg->getAsString(CFGID_PARSIM_SYNCHRONIZATION_CLASS);
1385
#else
1386
        throw cRuntimeError("Parallel simulation is turned on in the ini file, but OMNeT++ was compiled without parallel simulation support (WITH_PARSIM=no)");
1387
#endif
1388
    }
1389

    
1390
    opt_outputvectormanager_class = cfg->getAsString(CFGID_OUTPUTVECTORMANAGER_CLASS);
1391
    opt_outputscalarmanager_class = cfg->getAsString(CFGID_OUTPUTSCALARMANAGER_CLASS);
1392
    opt_snapshotmanager_class = cfg->getAsString(CFGID_SNAPSHOTMANAGER_CLASS);
1393

    
1394
    opt_fname_append_host = cfg->getAsBool(CFGID_FNAME_APPEND_HOST, opt_parsim);
1395

    
1396
    ev.debug_on_errors = cfg->getAsBool(CFGID_DEBUG_ON_ERRORS);
1397
    opt_print_undisposed = cfg->getAsBool(CFGID_PRINT_UNDISPOSED);
1398

    
1399
    int scaleexp = (int) cfg->getAsInt(CFGID_SIMTIME_SCALE);
1400
    SimTime::setScaleExp(scaleexp);
1401

    
1402
    // note: this is read per run as well, but Tkenv needs its value on startup too
1403
    opt_inifile_network_dir = cfg->getConfigEntry(CFGID_NETWORK->getName()).getBaseDirectory();
1404

    
1405
    // other options are read on per-run basis
1406
}
1407

    
1408
void EnvirBase::readPerRunOptions()
1409
{
1410
    cConfiguration *cfg = getConfig();
1411

    
1412
    // get options from ini file
1413
    opt_network_name = cfg->getAsString(CFGID_NETWORK);
1414
    opt_inifile_network_dir = cfg->getConfigEntry(CFGID_NETWORK->getName()).getBaseDirectory();
1415
    opt_warnings = cfg->getAsBool(CFGID_WARNINGS);
1416
    opt_simtimelimit = cfg->getAsDouble(CFGID_SIM_TIME_LIMIT);
1417
    opt_cputimelimit = (long) cfg->getAsDouble(CFGID_CPU_TIME_LIMIT);
1418
    opt_warmupperiod = cfg->getAsDouble(CFGID_WARMUP_PERIOD);
1419
    opt_fingerprint = cfg->getAsString(CFGID_FINGERPRINT);
1420
    opt_num_rngs = cfg->getAsInt(CFGID_NUM_RNGS);
1421
    opt_rng_class = cfg->getAsString(CFGID_RNG_CLASS);
1422
    opt_seedset = cfg->getAsInt(CFGID_SEED_SET);
1423
    opt_debug_statistics_recording = cfg->getAsBool(CFGID_DEBUG_STATISTICS_RECORDING);
1424

    
1425
    simulation.setWarmupPeriod(opt_warmupperiod);
1426

    
1427
    // install hasher object
1428
    if (!opt_fingerprint.empty())
1429
        simulation.setHasher(new cHasher());
1430
    else
1431
        simulation.setHasher(NULL);
1432

    
1433
    // run RNG self-test on RNG class selected for this run
1434
    cRNG *testrng;
1435
    CREATE_BY_CLASSNAME(testrng, opt_rng_class.c_str(), cRNG, "random number generator");
1436
    testrng->selfTest();
1437
    delete testrng;
1438

    
1439
    // set up RNGs
1440
    int i;
1441
    for (i=0; i<num_rngs; i++)
1442
         delete rngs[i];
1443
    delete [] rngs;
1444

    
1445
    num_rngs = opt_num_rngs;
1446
    rngs = new cRNG *[num_rngs];
1447
    for (i=0; i<num_rngs; i++)
1448
    {
1449
        cRNG *rng;
1450
        CREATE_BY_CLASSNAME(rng, opt_rng_class.c_str(), cRNG, "random number generator");
1451
        rngs[i] = rng;
1452
        rngs[i]->initialize(opt_seedset, i, num_rngs, getParsimProcId(), getParsimNumPartitions(), getConfig());
1453
    }
1454

    
1455
    // and  finally the private seed generator for all local generators of cAsyncModule
1456
         CREATE_BY_CLASSNAME(seedGenerator, opt_rng_class.c_str(), cRNG, "random number generator");
1457
         seedGenerator->initializeAsMaster(opt_seedset, num_rngs+1, num_rngs, getParsimProcId(), getParsimNumPartitions(), getConfig());
1458

    
1459
    // init nextuniquenumber -- startRun() is too late because simple module ctors have run by then
1460
    nextuniquenumber = 0;
1461
#ifdef WITH_PARSIM
1462
    if (opt_parsim)
1463
        nextuniquenumber = (unsigned)parsimcomm->getProcId() * ((~0UL) / (unsigned)parsimcomm->getNumPartitions());
1464
#endif
1465

    
1466
    // open message log file. Note: in startRun() it would be too late,
1467
    // because modules have already been created by then
1468
    record_eventlog = cfg->getAsBool(CFGID_RECORD_EVENTLOG);
1469
    if (record_eventlog) {
1470
        eventlogmgr = new EventlogFileManager();
1471
        eventlogmgr->configure();
1472
        eventlogmgr->open();
1473
    }
1474
}
1475

    
1476
void EnvirBase::setEventlogRecording(bool enabled)
1477
{
1478
    // NOTE: eventlogmgr must be non-NULL when record_eventlog is true
1479
    if (enabled && !eventlogmgr) {
1480
        eventlogmgr = new EventlogFileManager();
1481
        eventlogmgr->configure();
1482
        eventlogmgr->open();
1483
        eventlogmgr->recordSimulation();
1484
    }
1485
    record_eventlog = enabled;
1486
}
1487

    
1488
bool EnvirBase::hasEventlogRecordingIntervals() const
1489
{
1490
    return eventlogmgr && eventlogmgr->hasRecordingIntervals();
1491
}
1492

    
1493
void EnvirBase::clearEventlogRecordingIntervals()
1494
{
1495
    if (eventlogmgr)
1496
        eventlogmgr->clearRecordingIntervals();
1497
}
1498

    
1499
//-------------------------------------------------------------
1500

    
1501
int EnvirBase::getNumRNGs() const
1502
{
1503
    return num_rngs;
1504
}
1505

    
1506
cRNG *EnvirBase::getRNG(int k)
1507
{
1508
    if (k<0 || k>=num_rngs)
1509
        throw cRuntimeError("RNG index %d is out of range (num-rngs=%d, check the configuration)", k, num_rngs);
1510
    return rngs[k];
1511
}
1512

    
1513
void EnvirBase::getRNGMappingFor(cComponent *component)
1514
{
1515
    cConfigurationEx *cfg = getConfigEx();
1516
    std::string componentFullPath = component->getFullPath();
1517
    std::vector<const char *> suffixes = cfg->getMatchingPerObjectConfigKeySuffixes(componentFullPath.c_str(), "rng-*"); // CFGID_RNG_K
1518
    if (suffixes.size()==0)
1519
        return;
1520

    
1521
    // extract into tmpmap[]
1522
    int mapsize=0;
1523
    int tmpmap[100];
1524
    for (int i=0; i<(int)suffixes.size(); i++)
1525
    {
1526
        const char *suffix = suffixes[i];  // contains "rng-1", "rng-4" or whichever has been found in the config for this module/channel
1527
        const char *value = cfg->getPerObjectConfigValue(componentFullPath.c_str(), suffix);
1528
        ASSERT(value!=NULL);
1529
        char *s1, *s2;
1530
        int modRng = strtol(suffix+strlen("rng-"), &s1, 10);
1531
        int physRng = strtol(value, &s2, 10);
1532
        if (*s1!='\0' || *s2!='\0')
1533
            throw cRuntimeError("Configuration error: %s=%s for module/channel %s: "
1534
                                "numeric RNG indices expected",
1535
                                suffix, value, component->getFullPath().c_str());
1536

    
1537
        if (physRng>getNumRNGs())
1538
            throw cRuntimeError("Configuration error: rng-%d=%d of module/channel %s: "
1539
                                "RNG index out of range (num-rngs=%d)",
1540
                                modRng, physRng, component->getFullPath().c_str(), getNumRNGs());
1541
        if (modRng>=mapsize)
1542
        {
1543
            if (modRng>=100)
1544
                throw cRuntimeError("Configuration error: rng-%d=... of module/channel %s: "
1545
                                    "local RNG index out of supported range 0..99",
1546
                                    modRng, component->getFullPath().c_str());
1547
            while (mapsize<=modRng)
1548
            {
1549
                tmpmap[mapsize] = mapsize;
1550
                mapsize++;
1551
            }
1552
        }
1553
        tmpmap[modRng] = physRng;
1554
    }
1555

    
1556
    // install map into the module
1557
    if (mapsize>0)
1558
    {
1559
        int *map = new int[mapsize];
1560
        memcpy(map, tmpmap, mapsize*sizeof(int));
1561
        component->setRNGMap(mapsize, map);
1562
    }
1563
}
1564

    
1565
//-------------------------------------------------------------
1566

    
1567
void *EnvirBase::registerOutputVector(const char *modulename, const char *vectorname)
1568
{
1569
    assert(outvectormgr);
1570
    return outvectormgr->registerVector(modulename, vectorname);
1571
}
1572

    
1573
void EnvirBase::deregisterOutputVector(void *vechandle)
1574
{
1575
    assert(outvectormgr);
1576
    outvectormgr->deregisterVector(vechandle);
1577
}
1578

    
1579
void EnvirBase::setVectorAttribute(void *vechandle, const char *name, const char *value)
1580
{
1581
    assert(outvectormgr);
1582
    outvectormgr->setVectorAttribute(vechandle, name, value);
1583
}
1584

    
1585
bool EnvirBase::recordInOutputVector(void *vechandle, simtime_t t, double value)
1586
{
1587
    assert(outvectormgr);
1588
    return outvectormgr->record(vechandle, t, value);
1589
}
1590

    
1591
//-------------------------------------------------------------
1592

    
1593
void EnvirBase::recordScalar(cComponent *component, const char *name, double value, opp_string_map *attributes)
1594
{
1595
    assert(outscalarmgr);
1596
    outscalarmgr->recordScalar(component, name, value, attributes);
1597
}
1598

    
1599
void EnvirBase::recordStatistic(cComponent *component, const char *name, cStatistic *statistic, opp_string_map *attributes)
1600
{
1601
    assert(outscalarmgr);
1602
    outscalarmgr->recordStatistic(component, name, statistic, attributes);
1603
}
1604

    
1605
//-------------------------------------------------------------
1606

    
1607
std::ostream *EnvirBase::getStreamForSnapshot()
1608
{
1609
    return snapshotmgr->getStreamForSnapshot();
1610
}
1611

    
1612
void EnvirBase::releaseStreamForSnapshot(std::ostream *os)
1613
{
1614
    snapshotmgr->releaseStreamForSnapshot(os);
1615
}
1616

    
1617
//-------------------------------------------------------------
1618

    
1619
unsigned long EnvirBase::getUniqueNumber()
1620
{
1621
    // TBD check for overflow
1622
    return nextuniquenumber++;
1623
}
1624

    
1625
void EnvirBase::displayException(std::exception& ex)
1626
{
1627
    cException *e = dynamic_cast<cException *>(&ex);
1628
    if (!e)
1629
        ev.printfmsg("Error: %s.", ex.what());
1630
    else
1631
        ev.printfmsg("%s", e->getFormattedMessage().c_str());
1632
}
1633

    
1634
bool EnvirBase::idle()
1635
{
1636
    return false;
1637
}
1638

    
1639
int EnvirBase::getArgCount() const
1640
{
1641
    return args->getArgCount();
1642
}
1643

    
1644
char **EnvirBase::getArgVector() const
1645
{
1646
    return args->getArgVector();
1647
}
1648

    
1649
//-------------------------------------------------------------
1650

    
1651
void EnvirBase::resetClock()
1652
{
1653
    timeval now;
1654
    gettimeofday(&now, NULL);
1655
    laststarted = simendtime = simbegtime = now;
1656
    elapsedtime.tv_sec = elapsedtime.tv_usec = 0;
1657
}
1658

    
1659
void EnvirBase::startClock()
1660
{
1661
    gettimeofday(&laststarted, NULL);
1662
}
1663

    
1664
void EnvirBase::stopClock()
1665
{
1666
    gettimeofday(&simendtime, NULL);
1667
    elapsedtime = elapsedtime + simendtime - laststarted;
1668
    simulatedtime = simulation.getSimTime();
1669
}
1670

    
1671
timeval EnvirBase::totalElapsed()
1672
{
1673
    timeval now;
1674
    gettimeofday(&now, NULL);
1675
    return now - laststarted + elapsedtime;
1676
}
1677

    
1678
void EnvirBase::checkTimeLimits()
1679
{
1680
    if (opt_simtimelimit!=0 && simulation.getSimTime()>=opt_simtimelimit)
1681
         throw cTerminationException(eSIMTIME);
1682
    if (opt_cputimelimit==0) // no limit
1683
         return;
1684
    if (disable_tracing && (simulation.getEventNumber()&0xFF)!=0) // optimize: in Express mode, don't call gettimeofday() on every event
1685
         return;
1686
    timeval now;
1687
    gettimeofday(&now, NULL);
1688
    long elapsedsecs = now.tv_sec - laststarted.tv_sec + elapsedtime.tv_sec;
1689
    if (elapsedsecs>=opt_cputimelimit)
1690
         throw cTerminationException(eREALTIME);
1691
}
1692

    
1693
void EnvirBase::stoppedWithTerminationException(cTerminationException& e)
1694
{
1695
    // if we're running in parallel and this exception is NOT one we received
1696
    // from other partitions, then notify other partitions
1697
#ifdef WITH_PARSIM
1698
    if (opt_parsim && !dynamic_cast<cReceivedTerminationException *>(&e))
1699
        parsimpartition->broadcastTerminationException(e);
1700
#endif
1701
}
1702

    
1703
void EnvirBase::stoppedWithException(std::exception& e)
1704
{
1705
    // if we're running in parallel and this exception is NOT one we received
1706
    // from other partitions, then notify other partitions
1707
#ifdef WITH_PARSIM
1708
    if (opt_parsim && !dynamic_cast<cReceivedException *>(&e))
1709
        parsimpartition->broadcastException(e);
1710
#endif
1711
}
1712

    
1713
void EnvirBase::checkFingerprint()
1714
{
1715
    if (opt_fingerprint.empty() || !simulation.getHasher())
1716
        return;
1717

    
1718
    int k = 0;
1719
    StringTokenizer tokenizer(opt_fingerprint.c_str());
1720
    while (tokenizer.hasMoreTokens())
1721
    {
1722
        const char *fingerprint = tokenizer.nextToken();
1723
        if (simulation.getHasher()->equals(fingerprint))
1724
        {
1725
            printfmsg("Fingerprint successfully verified: %s", fingerprint);
1726
            return;
1727
        }
1728
        k++;
1729
    }
1730

    
1731
    printfmsg("Fingerprint mismatch! calculated: %s, expected: %s%s",
1732
              simulation.getHasher()->str().c_str(),
1733
              (k>=2 ? "one of: " : ""), opt_fingerprint.c_str());
1734
}
1735

    
1736
cModuleType *EnvirBase::resolveNetwork(const char *networkname)
1737
{
1738
    cModuleType *network = NULL;
1739
    std::string inifilePackage = simulation.getNedPackageForFolder(opt_inifile_network_dir.c_str());
1740

    
1741
    bool hasInifilePackage = !inifilePackage.empty() && strcmp(inifilePackage.c_str(),"-")!=0;
1742
    if (hasInifilePackage)
1743
        network = cModuleType::find((inifilePackage+"."+networkname).c_str());
1744
    if (!network)
1745
        network = cModuleType::find(networkname);
1746
    if (!network) {
1747
        if (hasInifilePackage)
1748
            throw cRuntimeError("Network `%s' or `%s' not found, check .ini and .ned files",
1749
                                networkname, (inifilePackage+"."+networkname).c_str());
1750
        else
1751
            throw cRuntimeError("Network `%s' not found, check .ini and .ned files", networkname);
1752
    }
1753
    if (!network->isNetwork())
1754
        throw cRuntimeError("Module type `%s' is not a network", network->getFullName());
1755
    return network;
1756
}
1757

    
1758