Statistics
| Branch: | Revision:

root / src / envir / envirbase.cc @ 2f5cc443

History | View | Annotate | Download (65.5 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::simulationEventEnd(double complexity)
1217
{
1218
    if (record_eventlog)
1219
        eventlogmgr->simulationEventEnd(complexity);
1220
}
1221

    
1222

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

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

    
1235
void EnvirBase::messageCancelled(cMessage *msg)
1236
{
1237
    if (record_eventlog)
1238
        eventlogmgr->messageCancelled(msg);
1239
}
1240

    
1241
void EnvirBase::messageSendDirect(cMessage *msg, cGate *toGate, simtime_t propagationDelay, simtime_t transmissionDelay)
1242
{
1243
    if (record_eventlog)
1244
        eventlogmgr->messageSendDirect(msg, toGate, propagationDelay, transmissionDelay);
1245
}
1246

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

    
1253
void EnvirBase::messageSendHop(cMessage *msg, cGate *srcGate, simtime_t propagationDelay, simtime_t transmissionDelay)
1254
{
1255
    if (record_eventlog)
1256
        eventlogmgr->messageSendHop(msg, srcGate, propagationDelay, transmissionDelay);
1257
}
1258

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

    
1265
void EnvirBase::messageDeleted(cMessage *msg)
1266
{
1267
    if (record_eventlog)
1268
        eventlogmgr->messageDeleted(msg);
1269
}
1270

    
1271
void EnvirBase::componentMethodBegin(cComponent *from, cComponent *to, const char *methodFmt, va_list va, bool silent)
1272
{
1273
    if (record_eventlog)
1274
        eventlogmgr->componentMethodBegin(from, to, methodFmt, va);
1275
}
1276

    
1277
void EnvirBase::componentMethodEnd()
1278
{
1279
    if (record_eventlog)
1280
        eventlogmgr->componentMethodEnd();
1281
}
1282

    
1283
void EnvirBase::moduleCreated(cModule *newmodule)
1284
{
1285
    if (record_eventlog)
1286
        eventlogmgr->moduleCreated(newmodule);
1287
}
1288

    
1289
void EnvirBase::moduleDeleted(cModule *module)
1290
{
1291
    if (record_eventlog)
1292
        eventlogmgr->moduleDeleted(module);
1293
}
1294

    
1295
void EnvirBase::moduleReparented(cModule *module, cModule *oldparent)
1296
{
1297
    if (record_eventlog)
1298
        eventlogmgr->moduleReparented(module, oldparent);
1299
}
1300

    
1301
void EnvirBase::gateCreated(cGate *newgate)
1302
{
1303
    if (record_eventlog)
1304
        eventlogmgr->gateCreated(newgate);
1305
}
1306

    
1307
void EnvirBase::gateDeleted(cGate *gate)
1308
{
1309
    if (record_eventlog)
1310
        eventlogmgr->gateDeleted(gate);
1311
}
1312

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

    
1319
void EnvirBase::connectionDeleted(cGate *srcgate)
1320
{
1321
    if (record_eventlog)
1322
        eventlogmgr->connectionDeleted(srcgate);
1323
}
1324

    
1325
void EnvirBase::displayStringChanged(cComponent *component)
1326
{
1327
    if (record_eventlog)
1328
        eventlogmgr->displayStringChanged(component);
1329
}
1330

    
1331
void EnvirBase::sputn(const char *s, int n)
1332
{
1333
    if (record_eventlog)
1334
        eventlogmgr->sputn(s, n);
1335
}
1336

    
1337
void EnvirBase::undisposedObject(cObject *obj)
1338
{
1339
    if (opt_print_undisposed)
1340
        ::printf("undisposed object: (%s) %s -- check module destructor\n", obj->getClassName(), obj->getFullPath().c_str());
1341
}
1342

    
1343
//-------------------------------------------------------------
1344

    
1345
void EnvirBase::processFileName(opp_string& fname)
1346
{
1347
    std::string text = fname.c_str();
1348

    
1349
    // insert ".<hostname>.<pid>" if requested before file extension
1350
    // (note: parsimProcId cannot be appended because of initialization order)
1351
    if (opt_fname_append_host)
1352
    {
1353
        std::string extension = "";
1354
        std::string::size_type index = text.rfind('.');
1355
        if (index != std::string::npos) {
1356
          extension = std::string(text, index);
1357
          text.erase(index);
1358
        }
1359

    
1360
        const char *hostname=getenv("HOST");
1361
        if (!hostname)
1362
            hostname=getenv("HOSTNAME");
1363
        if (!hostname)
1364
            hostname=getenv("COMPUTERNAME");
1365
        if (!hostname)
1366
            throw cRuntimeError("Cannot append hostname to file name `%s': no HOST, HOSTNAME "
1367
                                "or COMPUTERNAME (Windows) environment variable",
1368
                                fname.c_str());
1369
        int pid = getpid();
1370

    
1371
        // append
1372
        text += opp_stringf(".%s.%d%s", hostname, pid, extension.c_str());
1373
    }
1374
    fname = text.c_str();
1375
}
1376

    
1377
void EnvirBase::readOptions()
1378
{
1379
    cConfiguration *cfg = getConfig();
1380

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

    
1397
    opt_outputvectormanager_class = cfg->getAsString(CFGID_OUTPUTVECTORMANAGER_CLASS);
1398
    opt_outputscalarmanager_class = cfg->getAsString(CFGID_OUTPUTSCALARMANAGER_CLASS);
1399
    opt_snapshotmanager_class = cfg->getAsString(CFGID_SNAPSHOTMANAGER_CLASS);
1400

    
1401
    opt_fname_append_host = cfg->getAsBool(CFGID_FNAME_APPEND_HOST, opt_parsim);
1402

    
1403
    ev.debug_on_errors = cfg->getAsBool(CFGID_DEBUG_ON_ERRORS);
1404
    opt_print_undisposed = cfg->getAsBool(CFGID_PRINT_UNDISPOSED);
1405

    
1406
    int scaleexp = (int) cfg->getAsInt(CFGID_SIMTIME_SCALE);
1407
    SimTime::setScaleExp(scaleexp);
1408

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

    
1412
    // other options are read on per-run basis
1413
}
1414

    
1415
void EnvirBase::readPerRunOptions()
1416
{
1417
    cConfiguration *cfg = getConfig();
1418

    
1419
    // get options from ini file
1420
    opt_network_name = cfg->getAsString(CFGID_NETWORK);
1421
    opt_inifile_network_dir = cfg->getConfigEntry(CFGID_NETWORK->getName()).getBaseDirectory();
1422
    opt_warnings = cfg->getAsBool(CFGID_WARNINGS);
1423
    opt_simtimelimit = cfg->getAsDouble(CFGID_SIM_TIME_LIMIT);
1424
    opt_cputimelimit = (long) cfg->getAsDouble(CFGID_CPU_TIME_LIMIT);
1425
    opt_warmupperiod = cfg->getAsDouble(CFGID_WARMUP_PERIOD);
1426
    opt_fingerprint = cfg->getAsString(CFGID_FINGERPRINT);
1427
    opt_num_rngs = cfg->getAsInt(CFGID_NUM_RNGS);
1428
    opt_rng_class = cfg->getAsString(CFGID_RNG_CLASS);
1429
    opt_seedset = cfg->getAsInt(CFGID_SEED_SET);
1430
    opt_debug_statistics_recording = cfg->getAsBool(CFGID_DEBUG_STATISTICS_RECORDING);
1431

    
1432
    simulation.setWarmupPeriod(opt_warmupperiod);
1433

    
1434
    // install hasher object
1435
    if (!opt_fingerprint.empty())
1436
        simulation.setHasher(new cHasher());
1437
    else
1438
        simulation.setHasher(NULL);
1439

    
1440
    // run RNG self-test on RNG class selected for this run
1441
    cRNG *testrng;
1442
    CREATE_BY_CLASSNAME(testrng, opt_rng_class.c_str(), cRNG, "random number generator");
1443
    testrng->selfTest();
1444
    delete testrng;
1445

    
1446
    // set up RNGs
1447
    int i;
1448
    for (i=0; i<num_rngs; i++)
1449
         delete rngs[i];
1450
    delete [] rngs;
1451

    
1452
    num_rngs = opt_num_rngs;
1453
    rngs = new cRNG *[num_rngs];
1454
    for (i=0; i<num_rngs; i++)
1455
    {
1456
        cRNG *rng;
1457
        CREATE_BY_CLASSNAME(rng, opt_rng_class.c_str(), cRNG, "random number generator");
1458
        rngs[i] = rng;
1459
        rngs[i]->initialize(opt_seedset, i, num_rngs, getParsimProcId(), getParsimNumPartitions(), getConfig());
1460
    }
1461

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

    
1466
    // init nextuniquenumber -- startRun() is too late because simple module ctors have run by then
1467
    nextuniquenumber = 0;
1468
#ifdef WITH_PARSIM
1469
    if (opt_parsim)
1470
        nextuniquenumber = (unsigned)parsimcomm->getProcId() * ((~0UL) / (unsigned)parsimcomm->getNumPartitions());
1471
#endif
1472

    
1473
    // open message log file. Note: in startRun() it would be too late,
1474
    // because modules have already been created by then
1475
    record_eventlog = cfg->getAsBool(CFGID_RECORD_EVENTLOG);
1476
    if (record_eventlog) {
1477
        eventlogmgr = new EventlogFileManager();
1478
        eventlogmgr->configure();
1479
        eventlogmgr->open();
1480
    }
1481
}
1482

    
1483
void EnvirBase::setEventlogRecording(bool enabled)
1484
{
1485
    // NOTE: eventlogmgr must be non-NULL when record_eventlog is true
1486
    if (enabled && !eventlogmgr) {
1487
        eventlogmgr = new EventlogFileManager();
1488
        eventlogmgr->configure();
1489
        eventlogmgr->open();
1490
        eventlogmgr->recordSimulation();
1491
    }
1492
    record_eventlog = enabled;
1493
}
1494

    
1495
bool EnvirBase::hasEventlogRecordingIntervals() const
1496
{
1497
    return eventlogmgr && eventlogmgr->hasRecordingIntervals();
1498
}
1499

    
1500
void EnvirBase::clearEventlogRecordingIntervals()
1501
{
1502
    if (eventlogmgr)
1503
        eventlogmgr->clearRecordingIntervals();
1504
}
1505

    
1506
//-------------------------------------------------------------
1507

    
1508
int EnvirBase::getNumRNGs() const
1509
{
1510
    return num_rngs;
1511
}
1512

    
1513
cRNG *EnvirBase::getRNG(int k)
1514
{
1515
    if (k<0 || k>=num_rngs)
1516
        throw cRuntimeError("RNG index %d is out of range (num-rngs=%d, check the configuration)", k, num_rngs);
1517
    return rngs[k];
1518
}
1519

    
1520
void EnvirBase::getRNGMappingFor(cComponent *component)
1521
{
1522
    cConfigurationEx *cfg = getConfigEx();
1523
    std::string componentFullPath = component->getFullPath();
1524
    std::vector<const char *> suffixes = cfg->getMatchingPerObjectConfigKeySuffixes(componentFullPath.c_str(), "rng-*"); // CFGID_RNG_K
1525
    if (suffixes.size()==0)
1526
        return;
1527

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

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

    
1563
    // install map into the module
1564
    if (mapsize>0)
1565
    {
1566
        int *map = new int[mapsize];
1567
        memcpy(map, tmpmap, mapsize*sizeof(int));
1568
        component->setRNGMap(mapsize, map);
1569
    }
1570
}
1571

    
1572
//-------------------------------------------------------------
1573

    
1574
void *EnvirBase::registerOutputVector(const char *modulename, const char *vectorname)
1575
{
1576
    assert(outvectormgr);
1577
    return outvectormgr->registerVector(modulename, vectorname);
1578
}
1579

    
1580
void EnvirBase::deregisterOutputVector(void *vechandle)
1581
{
1582
    assert(outvectormgr);
1583
    outvectormgr->deregisterVector(vechandle);
1584
}
1585

    
1586
void EnvirBase::setVectorAttribute(void *vechandle, const char *name, const char *value)
1587
{
1588
    assert(outvectormgr);
1589
    outvectormgr->setVectorAttribute(vechandle, name, value);
1590
}
1591

    
1592
bool EnvirBase::recordInOutputVector(void *vechandle, simtime_t t, double value)
1593
{
1594
    assert(outvectormgr);
1595
    return outvectormgr->record(vechandle, t, value);
1596
}
1597

    
1598
//-------------------------------------------------------------
1599

    
1600
void EnvirBase::recordScalar(cComponent *component, const char *name, double value, opp_string_map *attributes)
1601
{
1602
    assert(outscalarmgr);
1603
    outscalarmgr->recordScalar(component, name, value, attributes);
1604
}
1605

    
1606
void EnvirBase::recordStatistic(cComponent *component, const char *name, cStatistic *statistic, opp_string_map *attributes)
1607
{
1608
    assert(outscalarmgr);
1609
    outscalarmgr->recordStatistic(component, name, statistic, attributes);
1610
}
1611

    
1612
//-------------------------------------------------------------
1613

    
1614
std::ostream *EnvirBase::getStreamForSnapshot()
1615
{
1616
    return snapshotmgr->getStreamForSnapshot();
1617
}
1618

    
1619
void EnvirBase::releaseStreamForSnapshot(std::ostream *os)
1620
{
1621
    snapshotmgr->releaseStreamForSnapshot(os);
1622
}
1623

    
1624
//-------------------------------------------------------------
1625

    
1626
unsigned long EnvirBase::getUniqueNumber()
1627
{
1628
    // TBD check for overflow
1629
    return nextuniquenumber++;
1630
}
1631

    
1632
void EnvirBase::displayException(std::exception& ex)
1633
{
1634
    cException *e = dynamic_cast<cException *>(&ex);
1635
    if (!e)
1636
        ev.printfmsg("Error: %s.", ex.what());
1637
    else
1638
        ev.printfmsg("%s", e->getFormattedMessage().c_str());
1639
}
1640

    
1641
bool EnvirBase::idle()
1642
{
1643
    return false;
1644
}
1645

    
1646
int EnvirBase::getArgCount() const
1647
{
1648
    return args->getArgCount();
1649
}
1650

    
1651
char **EnvirBase::getArgVector() const
1652
{
1653
    return args->getArgVector();
1654
}
1655

    
1656
//-------------------------------------------------------------
1657

    
1658
void EnvirBase::resetClock()
1659
{
1660
    timeval now;
1661
    gettimeofday(&now, NULL);
1662
    laststarted = simendtime = simbegtime = now;
1663
    elapsedtime.tv_sec = elapsedtime.tv_usec = 0;
1664
}
1665

    
1666
void EnvirBase::startClock()
1667
{
1668
    gettimeofday(&laststarted, NULL);
1669
}
1670

    
1671
void EnvirBase::stopClock()
1672
{
1673
    gettimeofday(&simendtime, NULL);
1674
    elapsedtime = elapsedtime + simendtime - laststarted;
1675
    simulatedtime = simulation.getSimTime();
1676
}
1677

    
1678
timeval EnvirBase::totalElapsed()
1679
{
1680
    timeval now;
1681
    gettimeofday(&now, NULL);
1682
    return now - laststarted + elapsedtime;
1683
}
1684

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

    
1700
void EnvirBase::stoppedWithTerminationException(cTerminationException& e)
1701
{
1702
    // if we're running in parallel and this exception is NOT one we received
1703
    // from other partitions, then notify other partitions
1704
#ifdef WITH_PARSIM
1705
    if (opt_parsim && !dynamic_cast<cReceivedTerminationException *>(&e))
1706
        parsimpartition->broadcastTerminationException(e);
1707
#endif
1708
}
1709

    
1710
void EnvirBase::stoppedWithException(std::exception& e)
1711
{
1712
    // if we're running in parallel and this exception is NOT one we received
1713
    // from other partitions, then notify other partitions
1714
#ifdef WITH_PARSIM
1715
    if (opt_parsim && !dynamic_cast<cReceivedException *>(&e))
1716
        parsimpartition->broadcastException(e);
1717
#endif
1718
}
1719

    
1720
void EnvirBase::checkFingerprint()
1721
{
1722
    if (opt_fingerprint.empty() || !simulation.getHasher())
1723
        return;
1724

    
1725
    int k = 0;
1726
    StringTokenizer tokenizer(opt_fingerprint.c_str());
1727
    while (tokenizer.hasMoreTokens())
1728
    {
1729
        const char *fingerprint = tokenizer.nextToken();
1730
        if (simulation.getHasher()->equals(fingerprint))
1731
        {
1732
            printfmsg("Fingerprint successfully verified: %s", fingerprint);
1733
            return;
1734
        }
1735
        k++;
1736
    }
1737

    
1738
    printfmsg("Fingerprint mismatch! calculated: %s, expected: %s%s",
1739
              simulation.getHasher()->str().c_str(),
1740
              (k>=2 ? "one of: " : ""), opt_fingerprint.c_str());
1741
}
1742

    
1743
cModuleType *EnvirBase::resolveNetwork(const char *networkname)
1744
{
1745
    cModuleType *network = NULL;
1746
    std::string inifilePackage = simulation.getNedPackageForFolder(opt_inifile_network_dir.c_str());
1747

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

    
1765