Statistics
| Branch: | Revision:

root / src / cmdenv / cmdenv.cc @ 2f5cc443

History | View | Annotate | Download (23.9 KB)

1
//==========================================================================
2
//  CMDENV.CC - part of
3
//                     OMNeT++/OMNEST
4
//            Discrete System Simulation in C++
5
//
6
//
7
//  Author: Andras Varga
8
//
9
//==========================================================================
10

    
11
/*--------------------------------------------------------------*
12
  Copyright (C) 1992-2008 Andras Varga
13
  Copyright (C) 2006-2008 OpenSim Ltd.
14

15
  This file is distributed WITHOUT ANY WARRANTY. See the file
16
  `license' for details on this and other legal matters.
17
*--------------------------------------------------------------*/
18

    
19
#include <stdio.h>
20
#include <stdlib.h>
21
#include <string.h>
22
#include <signal.h>
23

    
24
#include "opp_ctype.h"
25
#include "cmddefs.h"
26
#include "cmdenv.h"
27
#include "enumstr.h"
28
#include "appreg.h"
29
#include "csimplemodule.h"
30
#include "ccomponenttype.h"
31
#include "cmessage.h"
32
#include "args.h"
33
#include "speedometer.h"
34
#include "timeutil.h"
35
#include "stringutil.h"
36
#include "cconfigoption.h"
37
#include "cproperties.h"
38
#include "cproperty.h"
39

    
40
USING_NAMESPACE
41

    
42
Register_GlobalConfigOption(CFGID_CONFIG_NAME, "cmdenv-config-name", CFG_STRING, NULL, "Specifies the name of the configuration to be run (for a value `Foo', section [Config Foo] will be used from the ini file). See also cmdenv-runs-to-execute=. The -c command line option overrides this setting.")
43
Register_GlobalConfigOption(CFGID_RUNS_TO_EXECUTE, "cmdenv-runs-to-execute", CFG_STRING, NULL, "Specifies which runs to execute from the selected configuration (see cmdenv-config-name=). It accepts a comma-separated list of run numbers or run number ranges, e.g. 1,3..4,7..9. If the value is missing, Cmdenv executes all runs in the selected configuration. The -r command line option overrides this setting.")
44
Register_GlobalConfigOptionU(CFGID_CMDENV_EXTRA_STACK, "cmdenv-extra-stack", "B",  "8KB", "Specifies the extra amount of stack that is reserved for each activity() simple module when the simulation is run under Cmdenv.")
45
Register_GlobalConfigOption(CFGID_CMDENV_INTERACTIVE, "cmdenv-interactive", CFG_BOOL,  "false", "Defines what Cmdenv should do when the model contains unassigned parameters. In interactive mode, it asks the user. In non-interactive mode (which is more suitable for batch execution), Cmdenv stops with an error.")
46
Register_GlobalConfigOption(CFGID_OUTPUT_FILE, "cmdenv-output-file", CFG_FILENAME, NULL, "When a filename is specified, Cmdenv redirects standard output into the given file. This is especially useful with parallel simulation. See the `fname-append-host' option as well.")
47
Register_PerRunConfigOption(CFGID_EXPRESS_MODE, "cmdenv-express-mode", CFG_BOOL, "true", "Selects ``normal'' (debug/trace) or ``express'' mode.")
48
Register_PerRunConfigOption(CFGID_AUTOFLUSH, "cmdenv-autoflush", CFG_BOOL, "false", "Call fflush(stdout) after each event banner or status update; affects both express and normal mode. Turning on autoflush may have a performance penalty, but it can be useful with printf-style debugging for tracking down program crashes.")
49
Register_PerRunConfigOption(CFGID_MODULE_MESSAGES, "cmdenv-module-messages", CFG_BOOL, "true", "When cmdenv-express-mode=false: turns printing module ev<< output on/off.")
50
Register_PerRunConfigOption(CFGID_EVENT_BANNERS, "cmdenv-event-banners", CFG_BOOL, "true", "When cmdenv-express-mode=false: turns printing event banners on/off.")
51
Register_PerRunConfigOption(CFGID_EVENT_BANNER_DETAILS, "cmdenv-event-banner-details", CFG_BOOL, "false", "When cmdenv-express-mode=false: print extra information after event banners.")
52
Register_PerRunConfigOption(CFGID_MESSAGE_TRACE, "cmdenv-message-trace", CFG_BOOL, "false", "When cmdenv-express-mode=false: print a line per message sending (by send(),scheduleAt(), etc) and delivery on the standard output.")
53
Register_PerRunConfigOptionU(CFGID_STATUS_FREQUENCY, "cmdenv-status-frequency", "s", "2s", "When cmdenv-express-mode=true: print status update every n seconds.")
54
Register_PerRunConfigOption(CFGID_PERFORMANCE_DISPLAY, "cmdenv-performance-display", CFG_BOOL, "true", "When cmdenv-express-mode=true: print detailed performance information. Turning it on results in a 3-line entry printed on each update, containing ev/sec, simsec/sec, ev/simsec, number of messages created/still present/currently scheduled in FES.")
55

    
56
Register_PerObjectConfigOption(CFGID_CMDENV_EV_OUTPUT, "cmdenv-ev-output", CFG_BOOL, "true", "When cmdenv-express-mode=false: whether Cmdenv should print debug messages (ev<<) from the selected modules.")
57

    
58

    
59
//
60
// Register the Cmdenv user interface
61
//
62
Register_OmnetApp("Cmdenv", Cmdenv, 10, "command-line user interface");
63

    
64
//
65
// The following function can be used to force linking with Cmdenv; specify
66
// -u _cmdenv_lib (gcc) or /include:_cmdenv_lib (vc++) in the link command.
67
//
68
extern "C" CMDENV_API void cmdenv_lib() {}
69
// on some compilers (e.g. linux gcc 4.2) the functions are generated without _
70
extern "C" CMDENV_API void _cmdenv_lib() {}
71

    
72

    
73
#define LL  INT64_PRINTF_FORMAT
74

    
75
static char buffer[1024];
76

    
77
bool Cmdenv::sigint_received;
78

    
79

    
80
// utility function for printing elapsed time
81
char *timeToStr(timeval t, char *buf=NULL)
82
{
83
   static char buf2[64];
84
   char *b = buf ? buf : buf2;
85

    
86
   if (t.tv_sec<3600)
87
       sprintf(b,"%ld.%.3ds (%dm %02ds)", (long)t.tv_sec, (int)(t.tv_usec/1000), int(t.tv_sec/60L), int(t.tv_sec%60L));
88
   else if (t.tv_sec<86400)
89
       sprintf(b,"%ld.%.3ds (%dh %02dm)", (long)t.tv_sec, (int)(t.tv_usec/1000), int(t.tv_sec/3600L), int((t.tv_sec%3600L)/60L));
90
   else
91
       sprintf(b,"%ld.%.3ds (%dd %02dh)", (long)t.tv_sec, (int)(t.tv_usec/1000), int(t.tv_sec/86400L), int((t.tv_sec%86400L)/3600L));
92

    
93
   return b;
94
}
95

    
96

    
97
Cmdenv::Cmdenv()
98
{
99
    // Note: ctor should only contain trivial initializations, because
100
    // the class may be instantiated only for the purpose of calling
101
    // printUISpecificHelp() on it
102

    
103
    // initialize fout to stdout, then we'll replace it if redirection is
104
    // requested in the ini file
105
    fout = stdout;
106

    
107
    // init config variables that are used even before readOptions()
108
    opt_autoflush = true;
109
}
110

    
111
Cmdenv::~Cmdenv()
112
{
113
}
114

    
115
void Cmdenv::readOptions()
116
{
117
    EnvirBase::readOptions();
118

    
119
    cConfiguration *cfg = getConfig();
120

    
121
    // note: configname and runstoexec will possibly be overwritten
122
    // with the -c, -r command-line options in our setup() method
123
    opt_configname = cfg->getAsString(CFGID_CONFIG_NAME);
124
    opt_runstoexec = cfg->getAsString(CFGID_RUNS_TO_EXECUTE);
125

    
126
    opt_extrastack = (size_t) cfg->getAsDouble(CFGID_CMDENV_EXTRA_STACK);
127
    opt_outputfile = cfg->getAsFilename(CFGID_OUTPUT_FILE).c_str();
128

    
129
    if (!opt_outputfile.empty())
130
    {
131
        processFileName(opt_outputfile);
132
        ::printf("Cmdenv: redirecting output to file `%s'...\n",opt_outputfile.c_str());
133
        FILE *out = fopen(opt_outputfile.c_str(), "w");
134
        if (!out)
135
            throw cRuntimeError("Cannot open output redirection file `%s'",opt_outputfile.c_str());
136
        fout = out;
137
    }
138
}
139

    
140
void Cmdenv::readPerRunOptions()
141
{
142
    EnvirBase::readPerRunOptions();
143

    
144
    cConfiguration *cfg = getConfig();
145
    opt_expressmode = cfg->getAsBool(CFGID_EXPRESS_MODE);
146
    opt_interactive = cfg->getAsBool(CFGID_CMDENV_INTERACTIVE);
147
    opt_autoflush = cfg->getAsBool(CFGID_AUTOFLUSH);
148
    opt_modulemsgs = cfg->getAsBool(CFGID_MODULE_MESSAGES);
149
    opt_eventbanners = cfg->getAsBool(CFGID_EVENT_BANNERS);
150
    opt_eventbanner_details = cfg->getAsBool(CFGID_EVENT_BANNER_DETAILS);
151
    opt_messagetrace = cfg->getAsBool(CFGID_MESSAGE_TRACE);
152
    opt_status_frequency_ms = 1000*cfg->getAsDouble(CFGID_STATUS_FREQUENCY);
153
    opt_perfdisplay = cfg->getAsBool(CFGID_PERFORMANCE_DISPLAY);
154
}
155

    
156
void Cmdenv::askParameter(cPar *par, bool unassigned)
157
{
158
    bool success = false;
159
    while (!success)
160
    {
161
        cProperties *props = par->getProperties();
162
        cProperty *prop = props->get("prompt");
163
        std::string prompt = prop ? prop->getValue(cProperty::DEFAULTKEY) : "";
164
        std::string reply;
165

    
166
        // ask the user. note: gets() will signal "cancel" by throwing an exception
167
        if (!prompt.empty())
168
            reply = this->gets(prompt.c_str(), par->str().c_str());
169
        else
170
            // DO NOT change the "Enter parameter" string. The IDE launcher plugin matches
171
            // against this string for detecting user input
172
            reply = this->gets((std::string("Enter parameter `")+par->getFullPath()+"' ("+(unassigned?"unassigned":"ask")+"):").c_str(), par->str().c_str());
173

    
174
        try
175
        {
176
            par->parse(reply.c_str());
177
            success = true;
178
        }
179
        catch (std::exception& e)
180
        {
181
            ev.printfmsg("%s -- please try again.", e.what());
182
        }
183
    }
184
}
185

    
186
void Cmdenv::run()
187
{
188
    // '-c' and '-r' option: configuration to activate, and run numbers to run.
189
    // Both command-line options take precedence over inifile settings.
190
    // (NOTE: inifile settings *already* got read at this point! as EnvirBase::setup()
191
    // invokes readOptions()).
192

    
193
    const char *configname = args->optionValue('c');
194
    if (configname)
195
        opt_configname = configname;
196
    if (opt_configname.empty())
197
        opt_configname = "General";
198

    
199
    const char *runstoexec = args->optionValue('r');
200
    if (runstoexec)
201
        opt_runstoexec = runstoexec;
202

    
203
    // if the list of runs is not given explicitly, must execute all runs
204
    if (opt_runstoexec.empty())
205
    {
206
        int n = cfg->getNumRunsInConfig(opt_configname.c_str());  //note: may throw exception
207
        if (n==0) {
208
            ev.printfmsg("Error: No such configuration `%s', or it generates 0 runs", opt_configname.c_str());
209
            exitcode = 1;
210
            return;
211
        }
212

    
213
        char buf[32];
214
        sprintf(buf, (n==0 ? "" : n==1 ? "%d" : "0..%d"), n-1);
215
        opt_runstoexec = buf;
216
    }
217

    
218
    EnumStringIterator runiterator(opt_runstoexec.c_str());
219
    if (runiterator.hasError())
220
    {
221
        ev.printfmsg("Error parsing list of runs to execute: `%s'", opt_runstoexec.c_str());
222
        exitcode = 1;
223
        return;
224
    }
225

    
226
    // we'll return nonzero exitcode if any run was terminated with error
227
    bool had_error = false;
228

    
229
    for (; runiterator()!=-1; runiterator++)
230
    {
231
        int runnumber = runiterator();
232
        bool setupnetwork_done = false;
233
        bool startrun_done = false;
234
        try
235
        {
236
            ::fprintf(fout, "\nPreparing for running configuration %s, run #%d...\n", opt_configname.c_str(), runnumber);
237
            ::fflush(fout);
238

    
239
            cfg->activateConfig(opt_configname.c_str(), runnumber);
240

    
241
            const char *itervars = cfg->getVariable(CFGVAR_ITERATIONVARS2);
242
            if (itervars && strlen(itervars)>0)
243
                ::fprintf(fout, "Scenario: %s\n", itervars);
244
            ::fprintf(fout, "Assigned runID=%s\n", cfg->getVariable(CFGVAR_RUNID));
245

    
246
            //cfg->dump();
247

    
248
            readPerRunOptions();
249

    
250
            // find network
251
            cModuleType *network = resolveNetwork(opt_network_name.c_str());
252
            ASSERT(network);
253

    
254
            // set up network
255
            ::fprintf(fout, "Setting up network `%s'...\n", opt_network_name.c_str());
256
            ::fflush(fout);
257

    
258
            simulation.setupNetwork(network);
259
            setupnetwork_done = true;
260

    
261
            // prepare for simulation run
262
            ::fprintf(fout, "Initializing...\n");
263
            ::fflush(fout);
264

    
265
            startRun();
266
            startrun_done = true;
267

    
268
            // run the simulation
269
            ::fprintf(fout, "\nRunning simulation...\n");
270
            ::fflush(fout);
271

    
272
            // simulate() should only throw exception if error occurred and
273
            // finish() should not be called.
274
            simulate();
275

    
276
            ::fprintf(fout, "\nCalling finish() at end of Run #%d...\n", runnumber);
277
            ::fflush(fout);
278
            simulation.callFinish();
279
            flushLastLine();
280

    
281
            checkFingerprint();
282
        }
283
        catch (std::exception& e)
284
        {
285
            had_error = true;
286
            stoppedWithException(e);
287
            displayException(e);
288
        }
289

    
290
        // call endRun()
291
        if (startrun_done)
292
        {
293
            try
294
            {
295
                endRun();
296
            }
297
            catch (std::exception& e)
298
            {
299
                had_error = true;
300
                displayException(e);
301
            }
302
        }
303

    
304
        // delete network
305
        if (setupnetwork_done)
306
        {
307
            try
308
            {
309
                simulation.deleteNetwork();
310
            }
311
            catch (std::exception& e)
312
            {
313
                had_error = true;
314
                displayException(e);
315
            }
316
        }
317

    
318
        // skip further runs if signal was caught
319
        if (sigint_received)
320
            break;
321
    }
322

    
323
    ::fflush(fout);
324

    
325
    exitcode = had_error ? 1 : sigint_received ? 2 : 0;
326
}
327

    
328
// note: also updates "since" (sets it to the current time) if answer is "true"
329
inline bool elapsed(long millis, struct timeval& since)
330
{
331
    struct timeval now;
332
    gettimeofday(&now, NULL);
333
    bool ret = timeval_diff_usec(now, since) > 1000*millis;
334
    if (ret)
335
        since = now;
336
    return ret;
337
}
338

    
339
void Cmdenv::simulate()
340
{
341
    // implement graceful exit when Ctrl-C is hit during simulation. We want
342
    // to finish the current event, then normally exit via callFinish() etc
343
    // so that simulation results are not lost.
344
    installSignalHandler();
345

    
346
    startClock();
347
    sigint_received = false;
348

    
349
    Speedometer speedometer;  // only used by Express mode, but we need it in catch blocks too
350

    
351
    try
352
    {
353
        if (!opt_expressmode)
354
        {
355
            disable_tracing = false;
356
            while (true)
357
            {
358
                cMessage* msg = simulation.getScheduler()->getNextEvent();
359
                cSimpleModule* mod = (cSimpleModule*) simulation.getModule(msg->getArrivalModuleId());
360

    
361
                if (!mod)
362
                    throw cTerminationException("scheduler interrupted while waiting");
363

    
364
                // print event banner if neccessary
365
                if (opt_eventbanners && mod->isEvEnabled())
366
                    printEventBanner(mod);
367

    
368
                // flush *between* printing event banner and event processing, so that
369
                // if event processing crashes, it can be seen which event it was
370
                if (opt_autoflush)
371
                    ::fflush(fout);
372

    
373
                // execute event
374
                //simulation.doOneEvent(mod);
375
                simulation.doOneEvent(msg);
376

    
377
                // flush so that output from different modules don't get mixed
378
                flushLastLine();
379

    
380
                checkTimeLimits();
381
                if (sigint_received)
382
                    throw cTerminationException("SIGINT or SIGTERM received, exiting");
383
            }
384
        }
385
        else
386
        {
387
            disable_tracing = true;
388
            speedometer.start(simulation.getSimTime());
389

    
390
            struct timeval last_update;
391
            gettimeofday(&last_update, NULL);
392

    
393
            doStatusUpdate(speedometer);
394

    
395
            while (true)
396
            {
397
                cMessage* msg = simulation.getScheduler()->getNextEvent();
398
                cSimpleModule* mod = (cSimpleModule*) simulation.getModule(msg->getArrivalModuleId());
399
                if (!mod)
400
                    throw cTerminationException("scheduler interrupted while waiting");
401

    
402
                speedometer.addEvent(simulation.getSimTime()); //XXX potential performance hog
403

    
404
                // print event banner from time to time
405
                if ((simulation.getEventNumber()&0xff)==0 && elapsed(opt_status_frequency_ms, last_update))
406
                    doStatusUpdate(speedometer);
407

    
408
                // execute event
409
                //simulation.doOneEvent(mod);
410
                simulation.doOneEvent(msg);
411

    
412
                checkTimeLimits();  //XXX potential performance hog (maybe check every 256 events, unless "cmdenv-strict-limits" is on?)
413
                if (sigint_received)
414
                    throw cTerminationException("SIGINT or SIGTERM received, exiting");
415
            }
416
        }
417
    }
418
    catch (cTerminationException& e)
419
    {
420
        if (opt_expressmode)
421
            doStatusUpdate(speedometer);
422
        disable_tracing = false;
423
        stopClock();
424
        deinstallSignalHandler();
425

    
426
        stoppedWithTerminationException(e);
427
        displayException(e);
428
        return;
429
    }
430
    catch (std::exception& e)
431
    {
432
        if (opt_expressmode)
433
            doStatusUpdate(speedometer);
434
        disable_tracing = false;
435
        stopClock();
436
        deinstallSignalHandler();
437
        throw;
438
    }
439

    
440
    // note: C++ lacks "finally": lines below need to be manually kept in sync with catch{...} blocks above!
441
    if (opt_expressmode)
442
        doStatusUpdate(speedometer);
443
    disable_tracing = false;
444
    stopClock();
445
    deinstallSignalHandler();
446
}
447

    
448
void Cmdenv::printEventBanner(cSimpleModule *mod)
449
{
450
    ::fprintf(fout, "** Event #%"LL"d  T=%s%s   %s (%s, id=%d)\n",
451
            simulation.getEventNumber(),
452
            SIMTIME_STR(simulation.getSimTime()),
453
            progressPercentage(), // note: IDE launcher uses this to track progress
454
            mod->getFullPath().c_str(),
455
            mod->getComponentType()->getName(),
456
            mod->getId()
457
          );
458
    if (opt_eventbanner_details)
459
    {
460
        ::fprintf(fout, "   Elapsed: %s   Messages: created: %ld  present: %ld  in FES: %d\n",
461
                timeToStr(totalElapsed()),
462
                cMessage::getTotalMessageCount(),
463
                cMessage::getLiveMessageCount(),
464
                simulation.msgQueue.getLength());
465
    }
466
}
467

    
468
void Cmdenv::doStatusUpdate(Speedometer& speedometer)
469
{
470
    speedometer.beginNewInterval();
471

    
472
    if (opt_perfdisplay)
473
    {
474
        ::fprintf(fout, "** Event #%"LL"d   T=%s   Elapsed: %s%s\n",
475
                simulation.getEventNumber(),
476
                SIMTIME_STR(simulation.getSimTime()),
477
                timeToStr(totalElapsed()),
478
                progressPercentage()); // note: IDE launcher uses this to track progress
479
        ::fprintf(fout, "     Speed:     ev/sec=%g   simsec/sec=%g   ev/simsec=%g\n",
480
                speedometer.getEventsPerSec(),
481
                speedometer.getSimSecPerSec(),
482
                speedometer.getEventsPerSimSec());
483

    
484
        ::fprintf(fout, "     Messages:  created: %ld   present: %ld   in FES: %d\n",
485
                cMessage::getTotalMessageCount(),
486
                cMessage::getLiveMessageCount(),
487
                simulation.msgQueue.getLength());
488
    }
489
    else
490
    {
491
        ::fprintf(fout, "** Event #%"LL"d   T=%s   Elapsed: %s%s   ev/sec=%g\n",
492
                simulation.getEventNumber(),
493
                SIMTIME_STR(simulation.getSimTime()),
494
                timeToStr(totalElapsed()),
495
                progressPercentage(), // note: IDE launcher uses this to track progress
496
                speedometer.getEventsPerSec());
497
    }
498

    
499
    // status update is always autoflushed (not only if opt_autoflush is on)
500
    ::fflush(fout);
501
}
502

    
503
const char *Cmdenv::progressPercentage()
504
{
505
    double simtimeRatio = -1;
506
    if (opt_simtimelimit!=0)
507
         simtimeRatio = simulation.getSimTime() / opt_simtimelimit;
508

    
509
    double cputimeRatio = -1;
510
    if (opt_cputimelimit!=0) {
511
        timeval now;
512
        gettimeofday(&now, NULL);
513
        long elapsedsecs = now.tv_sec - laststarted.tv_sec + elapsedtime.tv_sec;
514
        cputimeRatio = elapsedsecs / (double)opt_cputimelimit;
515
    }
516

    
517
    double ratio = std::max(simtimeRatio, cputimeRatio);
518
    if (ratio == -1)
519
        return "";
520
    else {
521
        static char buf[32];
522
        // DO NOT change the "% completed" string. The IDE launcher plugin matches
523
        // against this string for detecting user input
524
        sprintf(buf, "  %d%% completed", (int)(100*ratio));
525
        return buf;
526
    }
527
}
528

    
529
void Cmdenv::componentInitBegin(cComponent *component, int stage)
530
{
531
    if (!opt_expressmode)
532
        ::fprintf(fout, "Initializing %s %s, stage %d\n",
533
                component->isModule() ? "module" : "channel", component->getFullPath().c_str(), stage);
534
}
535

    
536
void Cmdenv::signalHandler(int signum)
537
{
538
    if (signum == SIGINT || signum == SIGTERM)
539
        sigint_received = true;
540
}
541

    
542
void Cmdenv::installSignalHandler()
543
{
544
    signal(SIGINT, signalHandler);
545
    signal(SIGTERM, signalHandler);
546
}
547

    
548
void Cmdenv::deinstallSignalHandler()
549
{
550
    signal(SIGINT, SIG_DFL);
551
    signal(SIGTERM, SIG_DFL);
552
}
553

    
554
//-----------------------------------------------------
555

    
556
void Cmdenv::putsmsg(const char *s)
557
{
558
    ::fprintf(fout, "\n<!> %s\n\n", s);
559
    ::fflush(fout);
560
}
561

    
562
void Cmdenv::sputn(const char *s, int n)
563
{
564
    EnvirBase::sputn(s, n);
565

    
566
    if (disable_tracing)
567
        return;
568

    
569
    cComponent *ctx = simulation.getContext();
570
    if (!ctx || (opt_modulemsgs && ctx->isEvEnabled()) || simulation.getContextType()==CTX_FINISH)
571
    {
572
        ::fwrite(s,1,n,fout);
573
        if (opt_autoflush)
574
            ::fflush(fout);
575
    }
576
}
577

    
578
cEnvir& Cmdenv::flush()
579
{
580
    ::fflush(fout);
581
    return *this;
582
}
583

    
584
std::string Cmdenv::gets(const char *prompt, const char *defaultReply)
585
{
586
    if (!opt_interactive)
587
    {
588
        throw cRuntimeError("The simulation wanted to ask a question, set cmdenv-interactive=true to allow it: \"%s\"", prompt);
589
    }
590
    else
591
    {
592
        ::fprintf(fout, "%s", prompt);
593
        if (!opp_isempty(defaultReply))
594
            ::fprintf(fout, "(default: %s) ", defaultReply);
595
        ::fflush(fout);
596

    
597
        ::fgets(buffer, 512, stdin);
598
        buffer[strlen(buffer)-1] = '\0'; // chop LF
599

    
600
        if (buffer[0]=='\x1b') // ESC?
601
           throw cRuntimeError(eCANCEL);
602

    
603
        return std::string(buffer);
604
    }
605
}
606

    
607
bool Cmdenv::askyesno(const char *question)
608
{
609
    if (!opt_interactive)
610
    {
611
        throw cRuntimeError("Simulation needs user input in non-interactive mode (prompt text: \"%s (y/n)\")", question);
612
    }
613
    else
614
    {
615
        // should also return -1 (==CANCEL)
616
        for(;;)
617
        {
618
            ::fprintf(fout, "%s (y/n) ", question);
619
            ::fflush(fout);
620
            ::fgets(buffer, 512, stdin);
621
            buffer[strlen(buffer)-1] = '\0'; // chop LF
622
            if (opp_toupper(buffer[0])=='Y' && !buffer[1])
623
                return true;
624
            else if (opp_toupper(buffer[0])=='N' && !buffer[1])
625
                return false;
626
            else
627
                putsmsg("Please type 'y' or 'n'!\n");
628
        }
629
    }
630
}
631

    
632
bool Cmdenv::idle()
633
{
634
    return sigint_received;
635
}
636

    
637
void Cmdenv::moduleCreated(cModule *mod)
638
{
639
    EnvirBase::moduleCreated(mod);
640

    
641
    bool ev_enabled = getConfig()->getAsBool(mod->getFullPath().c_str(), CFGID_CMDENV_EV_OUTPUT);
642
    mod->setEvEnabled(ev_enabled);
643
}
644

    
645
void Cmdenv::messageSent_OBSOLETE(cMessage *msg, cGate *)
646
{
647
    if (!opt_expressmode && opt_messagetrace)
648
    {
649
        ::fprintf(fout, "SENT:   %s\n", msg->info().c_str());
650
        if (opt_autoflush)
651
            ::fflush(fout);
652
    }
653
}
654

    
655
void Cmdenv::simulationEvent(cMessage *msg)
656
{
657
    EnvirBase::simulationEvent(msg);
658

    
659
    if (!opt_expressmode && opt_messagetrace)
660
    {
661
        ::fprintf(fout, "DELIVD: %s\n", msg->info().c_str());  //TODO can go out!
662
        if (opt_autoflush)
663
            ::fflush(fout);
664
    }
665
}
666

    
667
void Cmdenv::simulationEventEnd(double complexity)
668
{
669
    EnvirBase::simulationEventEnd(complexity);
670
}
671

    
672
void Cmdenv::printUISpecificHelp()
673
{
674
    ev << "Cmdenv-specific options:\n";
675
    ev << "  -c <configname>\n";
676
    ev << "                Select a given configuration for execution. With inifile-based\n";
677
    ev << "                configuration database, this selects the [Config <configname>]\n";
678
    ev << "                section; the default is the [General] section.\n";
679
    ev << "                See also: -r.\n";
680
    ev << "  -r <runs>     Execute the specified runs in the configuration selected with the\n";
681
    ev << "                -c option. <runs> is a comma-separated list of run numbers or\n";
682
    ev << "                run number ranges, for example 1,2,5-10. When not present, all\n" ;
683
    ev << "                runs of that configuration will be executed.\n" ;
684
    ev << "  -a            Print all config names and number of runs it them, and exit.\n";
685
    ev << "  -x <configname>\n";
686
    ev << "                Print the number of runs in the given configuration, and exit.\n";
687
    ev << "  -g, -G        Make -x verbose: print the unrolled configuration, iteration\n";
688
    ev << "                variables, etc. -G provides more details than -g.\n";
689
}
690

    
691
unsigned Cmdenv::getExtraStackForEnvir() const
692
{
693
    return opt_extrastack;
694
}
695

    
696