Statistics
| Branch: | Revision:

root / src / envir / envirbase.cc @ 68da4f12

History | View | Annotate | Download (66.3 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
Register_GlobalConfigOption(CFGID_WRITE_RUNTIME_TO_FILE, "write-runtime-to-file", CFG_BOOL, "false", "Enables or disables writing of the simulation runtime to file.");
110
Register_GlobalConfigOption(CFGID_WRITE_RUNTIME_TO_FILENAME, "write-runtime-to-filename", CFG_STRING, "runtimes.dat", "Defines the name of the file in which the runtimes are stored.");
111

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

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

    
132

    
133
#define STRINGIZE0(x) #x
134
#define STRINGIZE(x) STRINGIZE0(x)
135

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

    
148
static const char *buildInfoFormat =
149
    "%d-bit"
150

    
151
    #ifdef NDEBUG
152
    " RELEASE"
153
    #else
154
    " DEBUG"
155
    #endif
156

    
157
    " simtime_t=%s"
158
    " large-file-support=%s"
159
    ;
160

    
161
static const char *buildOptions = ""
162
    #ifdef USE_DOUBLE_SIMTIME
163
    " USE_DOUBLE_SIMTIME"
164
    #endif
165

    
166
    #ifdef WITHOUT_CPACKET
167
    " WITHOUT_CPACKET"
168
    #endif
169

    
170
    #ifdef WITH_PARSIM
171
    " WITH_PARSIM"
172
    #endif
173

    
174
    #ifdef WITH_MPI
175
    " WITH_MPI"
176
    #endif
177

    
178
    #ifdef WITH_NETBUILDER
179
    " WITH_NETBUILDER"
180
    #endif
181

    
182
    #ifdef WITH_AKAROA
183
    " WITH_AKAROA"
184
    #endif
185

    
186
    #ifdef XMLPARSER
187
    " XMLPARSER=" STRINGIZE(XMLPARSER)
188
    #endif
189
    ;
190

    
191

    
192
EnvirBase::EnvirBase()
193
{
194
    args = NULL;
195
    cfg = NULL;
196
    xmlcache = NULL;
197

    
198
    record_eventlog = false;
199
    eventlogmgr = NULL;
200
    outvectormgr = NULL;
201
    outscalarmgr = NULL;
202
    snapshotmgr = NULL;
203

    
204
    num_rngs = 0;
205
    rngs = NULL;
206

    
207
#ifdef WITH_PARSIM
208
    parsimcomm = NULL;
209
    parsimpartition = NULL;
210
#endif
211

    
212
    exitcode = 0;
213
}
214

    
215
EnvirBase::~EnvirBase()
216
{
217
    delete args;
218
    delete cfg;
219
    delete xmlcache;
220

    
221
    delete outvectormgr;
222
    delete outscalarmgr;
223
    delete snapshotmgr;
224

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

    
229
#ifdef WITH_PARSIM
230
    delete parsimcomm;
231
    delete parsimpartition;
232
#endif
233
}
234

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

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

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

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

    
279
    cConfigurationEx *cfg = getConfigEx();
280

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

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

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

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

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

    
325
    return true;
326
}
327

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

    
335
        // set opt_* variables from ini file(s)
336
        readOptions();
337

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

    
346
        // install XML document cache
347
        xmlcache = new cXMLDocCache();
348

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
755

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

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

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

    
811
//-------------------------------------------------------------
812

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

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

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

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

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

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

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

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

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

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

    
875
    if (opt_debug_statistics_recording)
876
        dumpResultRecorders(component);
877
}
878

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

    
885
    std::vector<std::string> modes; // the result
886

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1189
cConfiguration *EnvirBase::getConfig()
1190
{
1191
    return cfg;
1192
}
1193

    
1194
cConfigurationEx *EnvirBase::getConfigEx()
1195
{
1196
    return cfg;
1197
}
1198

    
1199
//-------------------------------------------------------------
1200

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

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

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

    
1218
void EnvirBase::simulationEventEnd(double complexity)
1219
{
1220
    if (record_eventlog)
1221
        eventlogmgr->simulationEventEnd(complexity);
1222
}
1223

    
1224

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1345
//-------------------------------------------------------------
1346

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

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

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

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

    
1379
void EnvirBase::readOptions()
1380
{
1381
    cConfiguration *cfg = getConfig();
1382

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

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

    
1403
    opt_fname_append_host = cfg->getAsBool(CFGID_FNAME_APPEND_HOST, opt_parsim);
1404

    
1405
    ev.debug_on_errors = cfg->getAsBool(CFGID_DEBUG_ON_ERRORS);
1406
    opt_print_undisposed = cfg->getAsBool(CFGID_PRINT_UNDISPOSED);
1407

    
1408
    int scaleexp = (int) cfg->getAsInt(CFGID_SIMTIME_SCALE);
1409
    SimTime::setScaleExp(scaleexp);
1410

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

    
1414
    // other options are read on per-run basis
1415
}
1416

    
1417
void EnvirBase::readPerRunOptions()
1418
{
1419
    cConfiguration *cfg = getConfig();
1420

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

    
1434
    simulation.setWarmupPeriod(opt_warmupperiod);
1435

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

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

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

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

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

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

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

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

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

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

    
1508
//-------------------------------------------------------------
1509

    
1510
int EnvirBase::getNumRNGs() const
1511
{
1512
    return num_rngs;
1513
}
1514

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

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

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

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

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

    
1574
//-------------------------------------------------------------
1575

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

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

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

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

    
1600
//-------------------------------------------------------------
1601

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

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

    
1614
//-------------------------------------------------------------
1615

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

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

    
1626
//-------------------------------------------------------------
1627

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

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

    
1643
bool EnvirBase::idle()
1644
{
1645
    return false;
1646
}
1647

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

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

    
1658
//-------------------------------------------------------------
1659

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

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

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

    
1679
    bool storeRuntimes = getConfig()->getAsBool(CFGID_WRITE_RUNTIME_TO_FILE);
1680
    if (storeRuntimes)
1681
    {
1682
        std::string filename = getConfig()->getAsString(CFGID_WRITE_RUNTIME_TO_FILENAME);
1683
        std::ofstream outfile;
1684
        outfile.open(filename.c_str(), std::ios::out | std::ios::app);
1685
        outfile << elapsedtime.tv_sec << "." << (elapsedtime.tv_usec/1000) << endl;
1686
        outfile.close();
1687
    }
1688
}
1689

    
1690
timeval EnvirBase::totalElapsed()
1691
{
1692
    timeval now;
1693
    gettimeofday(&now, NULL);
1694
    return now - laststarted + elapsedtime;
1695
}
1696

    
1697
void EnvirBase::checkTimeLimits()
1698
{
1699
    if (opt_simtimelimit!=0 && simulation.getSimTime()>=opt_simtimelimit)
1700
         throw cTerminationException(eSIMTIME);
1701
    if (opt_cputimelimit==0) // no limit
1702
         return;
1703
    if (disable_tracing && (simulation.getEventNumber()&0xFF)!=0) // optimize: in Express mode, don't call gettimeofday() on every event
1704
         return;
1705
    timeval now;
1706
    gettimeofday(&now, NULL);
1707
    long elapsedsecs = now.tv_sec - laststarted.tv_sec + elapsedtime.tv_sec;
1708
    if (elapsedsecs>=opt_cputimelimit)
1709
         throw cTerminationException(eREALTIME);
1710
}
1711

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

    
1722
void EnvirBase::stoppedWithException(std::exception& e)
1723
{
1724
    // if we're running in parallel and this exception is NOT one we received
1725
    // from other partitions, then notify other partitions
1726
#ifdef WITH_PARSIM
1727
    if (opt_parsim && !dynamic_cast<cReceivedException *>(&e))
1728
        parsimpartition->broadcastException(e);
1729
#endif
1730
}
1731

    
1732
void EnvirBase::checkFingerprint()
1733
{
1734
    if (opt_fingerprint.empty() || !simulation.getHasher())
1735
        return;
1736

    
1737
    int k = 0;
1738
    StringTokenizer tokenizer(opt_fingerprint.c_str());
1739
    while (tokenizer.hasMoreTokens())
1740
    {
1741
        const char *fingerprint = tokenizer.nextToken();
1742
        if (simulation.getHasher()->equals(fingerprint))
1743
        {
1744
            printfmsg("Fingerprint successfully verified: %s", fingerprint);
1745
            return;
1746
        }
1747
        k++;
1748
    }
1749

    
1750
    printfmsg("Fingerprint mismatch! calculated: %s, expected: %s%s",
1751
              simulation.getHasher()->str().c_str(),
1752
              (k>=2 ? "one of: " : ""), opt_fingerprint.c_str());
1753
}
1754

    
1755
cModuleType *EnvirBase::resolveNetwork(const char *networkname)
1756
{
1757
    cModuleType *network = NULL;
1758
    std::string inifilePackage = simulation.getNedPackageForFolder(opt_inifile_network_dir.c_str());
1759

    
1760
    bool hasInifilePackage = !inifilePackage.empty() && strcmp(inifilePackage.c_str(),"-")!=0;
1761
    if (hasInifilePackage)
1762
        network = cModuleType::find((inifilePackage+"."+networkname).c_str());
1763
    if (!network)
1764
        network = cModuleType::find(networkname);
1765
    if (!network) {
1766
        if (hasInifilePackage)
1767
            throw cRuntimeError("Network `%s' or `%s' not found, check .ini and .ned files",
1768
                                networkname, (inifilePackage+"."+networkname).c_str());
1769
        else
1770
            throw cRuntimeError("Network `%s' not found, check .ini and .ned files", networkname);
1771
    }
1772
    if (!network->isNetwork())
1773
        throw cRuntimeError("Module type `%s' is not a network", network->getFullName());
1774
    return network;
1775
}
1776

    
1777