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 |
|