Statistics
| Branch: | Revision:

root / src / envir / envirbase.cc @ 79b044ca

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 <iomanip>
22
#include <set>
23

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

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

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

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

    
73

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

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

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

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

    
84
#endif
85

    
86

    
87
USING_NAMESPACE
88

    
89
using std::ostream;
90

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

    
97
Register_GlobalConfigOptionU(CFGID_TOTAL_STACK, "total-stack", "B", NULL, "Specifies the maximum memory for activity() simple module stacks. You need to increase this value if you get a ``Cannot allocate coroutine stack'' error.");
98
Register_GlobalConfigOption(CFGID_PARALLEL_SIMULATION, "parallel-simulation", CFG_BOOL, "false", "Enables parallel distributed simulation.");
99
Register_GlobalConfigOption(CFGID_SCHEDULER_CLASS, "scheduler-class", CFG_STRING, "cSequentialScheduler", "Part of the Envir plugin mechanism: selects the scheduler class. This plugin interface allows for implementing real-time, hardware-in-the-loop, distributed and distributed parallel simulation. The class has to implement the cScheduler interface.");
100
Register_GlobalConfigOption(CFGID_PARSIM_COMMUNICATIONS_CLASS, "parsim-communications-class", CFG_STRING, "cFileCommunications", "If parallel-simulation=true, it selects the class that implements communication between partitions. The class must implement the cParsimCommunications interface.");
101
Register_GlobalConfigOption(CFGID_PARSIM_SYNCHRONIZATION_CLASS, "parsim-synchronization-class", CFG_STRING, "cNullMessageProtocol", "If parallel-simulation=true, it selects the parallel simulation algorithm. The class must implement the cParsimSynchronizer interface.");
102
Register_GlobalConfigOption(CFGID_OUTPUTVECTORMANAGER_CLASS, "outputvectormanager-class", CFG_STRING, "cIndexedFileOutputVectorManager", "Part of the Envir plugin mechanism: selects the output vector manager class to be used to record data from output vectors. The class has to implement the cOutputVectorManager interface.");
103
Register_GlobalConfigOption(CFGID_OUTPUTSCALARMANAGER_CLASS, "outputscalarmanager-class", CFG_STRING, "cFileOutputScalarManager", "Part of the Envir plugin mechanism: selects the output scalar manager class to be used to record data passed to recordScalar(). The class has to implement the cOutputScalarManager interface.");
104
Register_GlobalConfigOption(CFGID_SNAPSHOTMANAGER_CLASS, "snapshotmanager-class", CFG_STRING, "cFileSnapshotManager", "Part of the Envir plugin mechanism: selects the class to handle streams to which snapshot() writes its output. The class has to implement the cSnapshotManager interface.");
105
Register_GlobalConfigOption(CFGID_FNAME_APPEND_HOST, "fname-append-host", CFG_BOOL, NULL, "Turning it on will cause the host name and process Id to be appended to the names of output files (e.g. omnetpp.vec, omnetpp.sca). This is especially useful with distributed simulation. The default value is true if parallel simulation is enabled, false otherwise.");
106
Register_GlobalConfigOption(CFGID_DEBUG_ON_ERRORS, "debug-on-errors", CFG_BOOL, "false", "When set to true, runtime errors will cause the simulation program to break into the C++ debugger (if the simulation is running under one, or just-in-time debugging is activated). Once in the debugger, you can view the stack trace or examine variables.");
107
Register_GlobalConfigOption(CFGID_PRINT_UNDISPOSED, "print-undisposed", CFG_BOOL, "true", "Whether to report objects left (that is, not deallocated by simple module destructors) after network cleanup.");
108
Register_GlobalConfigOption(CFGID_SIMTIME_SCALE, "simtime-scale", CFG_INT, "-12", "Sets the scale exponent, and thus the resolution of time for the 64-bit fixed-point simulation time representation. Accepted values are -18..0; for example, -6 selects microsecond resolution. -12 means picosecond resolution, with a maximum simtime of ~110 days.");
109
Register_GlobalConfigOption(CFGID_NED_PATH, "ned-path", CFG_PATH, "", "A semicolon-separated list of directories. The directories will be regarded as roots of the NED package hierarchy, and all NED files will be loaded from their subdirectory trees. This option is normally left empty, as the OMNeT++ IDE sets the NED path automatically, and for simulations started outside the IDE it is more convenient to specify it via a command-line option or the NEDPATH environment variable.");
110
Register_GlobalConfigOption(CFGID_WRITE_RUNTIME_TO_FILE, "write-runtime-to-file", CFG_BOOL, "false", "Enables or disables writing of the simulation runtime to file.");
111
Register_GlobalConfigOption(CFGID_WRITE_RUNTIME_TO_FILENAME, "write-runtime-to-filename", CFG_STRING, "runtimes.dat", "Defines the name of the file in which the runtimes are stored.");
112

    
113
Register_PerRunConfigOption(CFGID_NETWORK, "network", CFG_STRING, NULL, "The name of the network to be simulated.  The package name can be omitted if the ini file is in the same directory as the NED file that contains the network.");
114
Register_PerRunConfigOption(CFGID_WARNINGS, "warnings", CFG_BOOL, "true", "Enables warnings.");
115
Register_PerRunConfigOptionU(CFGID_SIM_TIME_LIMIT, "sim-time-limit", "s", NULL, "Stops the simulation when simulation time reaches the given limit. The default is no limit.");
116
Register_PerRunConfigOptionU(CFGID_CPU_TIME_LIMIT, "cpu-time-limit", "s", NULL, "Stops the simulation when CPU usage has reached the given limit. The default is no limit.");
117
Register_PerRunConfigOptionU(CFGID_WARMUP_PERIOD, "warmup-period", "s", NULL, "Length of the initial warm-up period. When set, results belonging to the first x seconds of the simulation will not be recorded into output vectors, and will not be counted into output scalars (see option **.result-recording-modes). This option is useful for steady-state simulations. The default is 0s (no warmup period). Note that models that compute and record scalar results manually (via recordScalar()) will not automatically obey this setting.");
118
Register_PerRunConfigOption(CFGID_FINGERPRINT, "fingerprint", CFG_STRING, NULL, "The expected fingerprint of the simulation. When provided, a fingerprint will be calculated from the simulation event times and other quantities during simulation, and checked against the given one. Fingerprints are suitable for crude regression tests. As fingerprints occasionally differ across platforms, more than one fingerprint values can be specified here, separated by spaces, and a match with any of them will be accepted. To calculate the initial fingerprint, enter any dummy string (such as \"none\"), and run the simulation.");
119
Register_PerRunConfigOption(CFGID_NUM_RNGS, "num-rngs", CFG_INT, "1", "The number of random number generators.");
120
Register_PerRunConfigOption(CFGID_RNG_CLASS, "rng-class", CFG_STRING, "cMersenneTwister", "The random number generator class to be used. It can be `cMersenneTwister', `cLCG32', `cAkaroaRNG', or you can use your own RNG class (it must be subclassed from cRNG).");
121
Register_PerRunConfigOption(CFGID_SEED_SET, "seed-set", CFG_INT, "${runnumber}", "Selects the kth set of automatic random number seeds for the simulation. Meaningful values include ${repetition} which is the repeat loop counter (see repeat= key), and ${runnumber}.");
122
Register_PerRunConfigOption(CFGID_RESULT_DIR, "result-dir", CFG_STRING, "results", "Value for the ${resultdir} variable, which is used as the default directory for result files (output vector file, output scalar file, eventlog file, etc.)");
123
Register_PerRunConfigOption(CFGID_RECORD_EVENTLOG, "record-eventlog", CFG_BOOL, "false", "Enables recording an eventlog file, which can be later visualized on a sequence chart. See eventlog-file= option too.");
124
Register_PerRunConfigOption(CFGID_DEBUG_STATISTICS_RECORDING, "debug-statistics-recording", CFG_BOOL, "false", "Turns on the printing of debugging information related to statistics recording (@statistic properties)");
125
Register_PerObjectConfigOption(CFGID_PARTITION_ID, "partition-id", CFG_STRING, NULL, "With parallel simulation: in which partition the module should be instantiated. Specify numeric partition ID, or a comma-separated list of partition IDs for compound modules that span across multiple partitions. Ranges (\"5..9\") and \"*\" (=all) are accepted too.");
126
Register_PerObjectConfigOption(CFGID_RNG_K, "rng-%", CFG_INT, "", "Maps a module-local RNG to one of the global RNGs. Example: **.gen.rng-1=3 maps the local RNG 1 of modules matching `**.gen' to the global RNG 3. The default is one-to-one mapping.");
127
Register_PerObjectConfigOption(CFGID_RESULT_RECORDING_MODES, "result-recording-modes", CFG_STRING, "default", "Defines how to calculate results from the @statistic property matched by the wildcard. Special values: default, all: they select the modes listed in the record= key of @statistic; all selects all of them, default selects the non-optional ones (i.e. excludes the ones that end in a question mark). Example values: vector, count, last, sum, mean, min, max, timeavg, stats, histogram. More than one values are accepted, separated by commas. Expressions are allowed. Items prefixed with '-' get removed from the list. Example: **.queueLength.result-recording-modes=default,-vector,+timeavg");
128

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

    
133

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

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

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

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

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

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

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

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

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

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

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

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

    
192

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

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

    
205
    num_rngs = 0;
206
    rngs = NULL;
207

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

    
213
    exitcode = 0;
214
}
215

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

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

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

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

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

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

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

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

    
280
    cConfigurationEx *cfg = getConfigEx();
281

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

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

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

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

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

    
326
    return true;
327
}
328

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
756

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

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

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

    
812
//-------------------------------------------------------------
813

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1200
//-------------------------------------------------------------
1201

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

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

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

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

    
1225

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1346
//-------------------------------------------------------------
1347

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1435
    simulation.setWarmupPeriod(opt_warmupperiod);
1436

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

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

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

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

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

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

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

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

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

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

    
1509
//-------------------------------------------------------------
1510

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

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

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

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

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

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

    
1575
//-------------------------------------------------------------
1576

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

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

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

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

    
1601
//-------------------------------------------------------------
1602

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

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

    
1615
//-------------------------------------------------------------
1616

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

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

    
1627
//-------------------------------------------------------------
1628

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

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

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

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

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

    
1659
//-------------------------------------------------------------
1660

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1778