Project

General

Profile

Statistics
| Branch: | Revision:

root / src / tkenv / statinsp.cc @ 3e29b8a0

History | View | Annotate | Download (19.3 KB)

1 01873262 Georg Kunz
//==========================================================================
2
//  STATINSP.CC - part of
3
//
4
//                     OMNeT++/OMNEST
5
//            Discrete System Simulation in C++
6
//
7
//==========================================================================
8
9
/*--------------------------------------------------------------*
10
  Copyright (C) 1992-2008 Andras Varga
11
  Copyright (C) 2006-2008 OpenSim Ltd.
12

13
  This file is distributed WITHOUT ANY WARRANTY. See the file
14
  `license' for details on this and other legal matters.
15
*--------------------------------------------------------------*/
16
17
#include <string.h>
18
#include <math.h>
19
20
#include "cmodule.h"
21
#include "cmessage.h"
22
#include "cpar.h"
23
#include "carray.h"
24
#include "coutvector.h"
25
#include "cstatistic.h"
26
#include "cdensityestbase.h"
27
28
#include "tkenv.h"
29
#include "tklib.h"
30
#include "inspfactory.h"
31
#include "statinsp.h"
32
33
USING_NAMESPACE
34
35
36
37
void _dummy_for_statinsp() {}
38
39
//===============================================================
40
41
//
42
// class TStatisticInspectorFactory : public cInspectorFactory
43
// {
44
//   public:
45
//     TStatisticInspectorFactory(const char *name) : cInspectorFactory(name) {}
46
//
47
//     bool supportsObject(cObject *obj) {return dynamic_cast<cStatistic *>(obj)!=NULL;}
48
//     int inspectorType() {return INSP_OBJECT;}
49
//     double qualityAsDefault(cObject *object) {return 2.0;}
50
//
51
//     TInspector *createInspectorFor(cObject *object,int type,const char *geom,void *data) {
52
//         return new TStatisticInspector(object, type, geom, data);
53
//     }
54
// };
55
//
56
// Register_InspectorFactory(TStatisticInspectorFactory);
57
//
58
//
59
// TStatisticInspector::TStatisticInspector(cObject *obj,int typ,const char *geom,void *dat) :
60
//     TInspector(obj,typ,geom,dat)
61
// {
62
// }
63
//
64
// void TStatisticInspector::createWindow()
65
// {
66
//    TInspector::createWindow(); // create window name etc.
67
//
68
//    // create inspector window by calling the specified proc with
69
//    // the object's pointer. Window name will be like ".ptr80003a9d-1"
70
//    Tcl_Interp *interp = getTkenv()->getInterp();
71
//    CHK(Tcl_VarEval(interp, "create_statisticinspector ", windowname, " \"", geometry, "\"", NULL ));
72
// }
73
//
74
// void TStatisticInspector::update()
75
// {
76
//    TInspector::update();
77
//
78
//    cStatistic *stat = static_cast<cStatistic *>(object);
79
//
80
//    setLabel(".nb.info.count.e", (double) stat->getCount() );
81
//    setLabel(".nb.info.mean.e", stat->getMean() );
82
//    setLabel(".nb.info.stddev.e", stat->getStddev() );
83
//    setLabel(".nb.info.min.e", stat->getMin() );
84
//    setLabel(".nb.info.max.e", stat->getMax() );
85
// }
86
//
87
88
//=======================================================================
89
class THistogramWindowFactory : public cInspectorFactory
90
{
91
  public:
92
    THistogramWindowFactory(const char *name) : cInspectorFactory(name) {}
93
94
    bool supportsObject(cObject *obj) {return dynamic_cast<cDensityEstBase *>(obj)!=NULL;}
95
    int inspectorType() {return INSP_GRAPHICAL;}
96
    double qualityAsDefault(cObject *object) {return 3.0;}
97
98
    TInspector *createInspectorFor(cObject *object,int type,const char *geom,void *data) {
99
        return new THistogramWindow(object, type, geom, data);
100
    }
101
};
102
103
Register_InspectorFactory(THistogramWindowFactory);
104
105
106
THistogramWindow::THistogramWindow(cObject *obj,int typ,const char *geom,void *dat) :
107
    TInspector(obj,typ,geom,dat)
108
{
109
}
110
111
void THistogramWindow::createWindow()
112
{
113
   TInspector::createWindow(); // create window name etc.
114
   strcpy(canvas,windowname); strcat(canvas,".main.canvas");
115
116
   // create inspector window by calling the specified proc with
117
   // the object's pointer. Window name will be like ".ptr80003a9d-1"
118
   Tcl_Interp *interp = getTkenv()->getInterp();
119
   CHK(Tcl_VarEval(interp, "create_histogramwindow ", windowname, " \"", geometry, "\"", NULL ));
120
}
121
122
void THistogramWindow::update()
123
{
124
   TInspector::update();
125
126
   Tcl_Interp *interp = getTkenv()->getInterp();
127
   cDensityEstBase *distr = static_cast<cDensityEstBase *>(object);
128
129
   char buf[80];
130
   generalInfo( buf );
131
   CHK(Tcl_VarEval(interp, windowname,".bot.info config -text {",buf,"}",NULL));
132
133
   // can we draw anything at all?
134
   if (!distr->isTransformed() || distr->getNumCells()==0) return;
135
136
   long num_vals = distr->getCount();
137
   int basepts = distr->getNumCells()+1;
138
   int cell;
139
   double cell_lower, cell_upper;
140
141
   double xmin = distr->getBasepoint(0);
142
   double xrange = distr->getBasepoint(basepts-1) - xmin;
143
144
   // determine maximum height (will be used for y scaling)
145
   double ymax = -1.0; // a good start because all y values are >=0
146
   cell_upper = distr->getBasepoint(0);
147
   for (cell=0; cell<basepts-1; cell++)
148
   {
149
       // get cell
150
       cell_lower = cell_upper;
151
       cell_upper = distr->getBasepoint(cell+1);
152
       // calculate height
153
       double y = distr->getCellValue(cell) / (double)(num_vals) / (cell_upper-cell_lower);
154
       if (y>ymax) ymax=y;
155
   }
156
157
   // get canvas size
158
   CHK(Tcl_VarEval(interp, "winfo width ",canvas, NULL));
159
   int canvaswidth = atoi( Tcl_GetStringResult(interp) );
160
   CHK(Tcl_VarEval(interp, "winfo height ", canvas, NULL));
161
   int canvasheight = atoi( Tcl_GetStringResult(interp) );
162
163
   // temporarily define X() and Y() coordinate translation macros
164
#define X(x)   (int)(10+((x)-xmin)*((long)canvaswidth-20)/xrange)
165
#define Y(y)   (int)(canvasheight-10-(y)*((long)canvasheight-20)/ymax)
166
167
   // delete previous drawing
168
   CHK(Tcl_VarEval(interp, canvas," delete all", NULL));
169
170
   // draw the histogram
171
   cell_upper = distr->getBasepoint(0);
172
   for (cell=0; cell<basepts-1; cell++)
173
   {
174
       char tag[16];
175
       sprintf(tag,"cell%d",cell);
176
177
       // get cell
178
       cell_lower = cell_upper;
179
       cell_upper = distr->getBasepoint(cell+1);
180
       // calculate height
181
       double y = distr->getCellValue(cell) / (double)(num_vals) / (cell_upper-cell_lower);
182
       // prepare rectangle coordinates
183
       char coords[64];
184
       sprintf(coords,"%d %d %d %d", X(cell_lower), Y(0), X(cell_upper), Y(y));
185
       // draw rectangle
186
       CHK(Tcl_VarEval(interp, canvas,
187
                               " create rect ", coords," -tag ",tag,
188
                               " -fill black -outline black", NULL));
189
   }
190
#undef X
191
#undef Y
192
}
193
194
void THistogramWindow::generalInfo( char *buf )
195
{
196
   cDensityEstBase *d = static_cast<cDensityEstBase *>(object);
197
   if (!d->isTransformed())
198
       sprintf( buf, "(collecting initial values, N=%ld)", d->getCount());
199
   else
200
       sprintf( buf, "Histogram: (%g...%g)  N=%ld  #cells=%d",
201
                 d->getBasepoint(0), d->getBasepoint(d->getNumCells()),
202
                 d->getCount(),
203
                 d->getNumCells()
204
              );
205
}
206
207
void THistogramWindow::getCellInfo( char *buf, int cell )
208
{
209
   cDensityEstBase *d = static_cast<cDensityEstBase *>(object);
210
   double count = d->getCellValue(cell);
211
   double cell_lower = d->getBasepoint(cell);
212
   double cell_upper = d->getBasepoint(cell+1);
213
   sprintf( buf, "Cell #%d:  (%g...%g)  n=%g  PDF=%g",
214
                 cell,
215
                 cell_lower, cell_upper,
216
                 count,
217
                 count / (double)(d->getCount()) / (cell_upper-cell_lower)
218
          );
219
}
220
221
int THistogramWindow::inspectorCommand(Tcl_Interp *interp, int argc, const char **argv)
222
{
223
   if (argc<1) {Tcl_SetResult(interp, TCLCONST("wrong argcount"), TCL_STATIC); return TCL_ERROR;}
224
225
   if (strcmp(argv[0],"cell")==0)   // 'opp_inspectorcommand <inspector> cell ...'
226
   {
227
      if (argc>2) {Tcl_SetResult(interp, TCLCONST("wrong argcount"), TCL_STATIC); return TCL_ERROR;}
228
229
      char buf[128];
230
      if (argc==1)
231
         generalInfo(buf);
232
      else
233
         getCellInfo(buf, atoi(argv[1]) );
234
      Tcl_SetResult(interp,buf,TCL_VOLATILE);
235
      return TCL_OK;
236
   }
237
   return TCL_ERROR;
238
}
239
240
//=======================================================================
241
class TOutVectorWindowFactory : public cInspectorFactory
242
{
243
  public:
244
    TOutVectorWindowFactory(const char *name) : cInspectorFactory(name) {}
245
246
    bool supportsObject(cObject *obj) {return dynamic_cast<cOutVector *>(obj)!=NULL;}
247
    int inspectorType() {return INSP_GRAPHICAL;}
248
    double qualityAsDefault(cObject *object) {return 3.0;}
249
250
    TInspector *createInspectorFor(cObject *object,int type,const char *geom,void *data) {
251
        return new TOutVectorWindow(object, type, geom, data);
252
    }
253
};
254
255
Register_InspectorFactory(TOutVectorWindowFactory);
256
257
258
CircBuffer::CircBuffer(int size)
259
{
260
   siz = size;
261
   buf = new CBEntry[siz];
262
   n = 0;
263
   head = 0;
264
}
265
266
CircBuffer::~CircBuffer()
267
{
268
   delete [] buf;
269
}
270
271
void CircBuffer::add(simtime_t t, double value1, double value2)
272
{
273
   head = (head+1)%siz;
274
   CBEntry& p = buf[head];
275
   p.t = t;
276
   p.value1 = value1;
277
   p.value2 = value2;
278
   if (n<siz) n++;
279
}
280
281
static void record_in_insp(void *data, simtime_t t, double val1, double val2)
282
{
283
   TOutVectorWindow *insp = (TOutVectorWindow *) data;
284
   insp->circbuf.add(t,val1,val2);
285
}
286
287
TOutVectorWindow::TOutVectorWindow(cObject *obj,int typ,const char *geom,void *dat,int size) :
288
    TInspector(obj,typ,geom,dat), circbuf(size)
289
{
290
   // make inspected outvector to call us back when it gets data to write out
291
   cOutVector *ov = static_cast<cOutVector *>(object);
292
   ov->setCallback(record_in_insp, (void *)this);
293
294
   autoscale = true;
295
   drawing_mode = DRAW_LINES;
296
   miny = 0; maxy = 10;
297
   time_factor = 1;   // x scaling
298
   moving_tline = 0;
299
}
300
301
TOutVectorWindow::~TOutVectorWindow()
302
{
303
   // cancel installed callback in inspected outvector
304
   cOutVector *ov = static_cast<cOutVector *>(object);
305
   ov->setCallback(NULL, NULL);
306
}
307
308
void TOutVectorWindow::createWindow()
309
{
310
   TInspector::createWindow(); // create window name etc.
311
   strcpy(canvas,windowname); strcat(canvas,".main.canvas");
312
313
   // create inspector window by calling the specified proc with
314
   // the object's pointer. Window name will be like ".ptr80003a9d-1"
315
   Tcl_Interp *interp = getTkenv()->getInterp();
316
   CHK(Tcl_VarEval(interp, "create_outvectorwindow ", windowname, " \"", geometry, "\"", NULL ));
317
}
318
319
void TOutVectorWindow::update()
320
{
321
   TInspector::update();
322
323
   Tcl_Interp *interp = getTkenv()->getInterp();
324
325
   char buf[80];
326
   generalInfo( buf );
327
   setLabel(".bot.info",buf);
328
329
   if (circbuf.items()==0) return;
330
331
   // get canvas size
332
   CHK(Tcl_VarEval(interp, "winfo width ",canvas, NULL));
333
   int canvaswidth = atoi( Tcl_GetStringResult(interp) );
334
   if (!canvaswidth)  canvaswidth=1;
335
   CHK(Tcl_VarEval(interp, "winfo height ", canvas, NULL));
336
   int canvasheight = atoi( Tcl_GetStringResult(interp) );
337
   if (!canvasheight) canvasheight=1;
338
339
   simtime_t tbase = simulation.getSimTime();
340
   // simtime_t tbase = circbuf.entry[circbuf.headPos()].t;
341
342
   if (autoscale)
343
   {
344
       // adjust time_factor if it's too far from the optimal value
345
       simtime_t firstt = circbuf.entry(circbuf.tailPos()).t;
346
       double dt = SIMTIME_DBL(tbase - firstt);
347
       if (dt>0)
348
       {
349
          double opt_tf = dt/canvaswidth;
350
351
          // some rounding: keep 2 significant digits
352
          double order = pow(10.0, (int)floor(log10(opt_tf)));
353
          opt_tf = floor(opt_tf/order+.5)*order;
354
355
          // adjust only if it differs >=20% from its optimal value
356
          if (fabs(time_factor-opt_tf) > opt_tf*0.20)
357
              time_factor = opt_tf;
358
       }
359
360
       // determine miny and maxy
361
       int pos = circbuf.headPos();
362
       miny = maxy = circbuf.entry(circbuf.headPos()).value1;
363
       for(int i=0;i<circbuf.items();i++)
364
       {
365
           CircBuffer::CBEntry& p = circbuf.entry(pos);
366
           if (p.value1<miny) miny = p.value1;
367
           if (p.value1>maxy) maxy = p.value1;
368
           pos=(pos-1+circbuf.size())%circbuf.size();
369
       }
370
       if (miny==maxy)
371
       {
372
           if (miny!=0) {miny-=fabs(miny/3); maxy+=fabs(maxy/3);}
373
           else  {miny=-0.5; maxy=0.5;}
374
       }
375
   }
376
   double rangey=maxy-miny;
377
378
   simtime_t tf = time_factor;
379
380
   // temporarily define X() and Y() coordinate translation macros
381
#define X(t)   (int)(canvaswidth-10-(tbase-(t))/tf)
382
#define Y(y)   (int)(canvasheight-20-((y)-miny)*((long)canvasheight-30)/rangey)
383
384
   //printf("cw=%d  ch=%d  tbase=%f trange=%f  miny=%f  maxy=%f rangey=%f\n",
385
   //        canvaswidth, canvasheight,tbase,trange, miny, maxy, rangey);
386
387
   // delete previous drawing
388
   CHK(Tcl_VarEval(interp, canvas," delete all", NULL));
389
390
   // now scan through the whole buffer in time-increasing order
391
   // and draw in the meanwhile
392
393
   int y_zero = Y(0);   // so that we don't have to calculate it each time
394
   int next_x=-1;
395
   int next_y1=0, next_y2=0; // values won't be used (they're there just to prevent warning)
396
   int x, y1, y2;
397
   int pos;
398
   int next_pos = (circbuf.headPos()-circbuf.items()+circbuf.size())%circbuf.size();
399
   for (int i=0;i<=circbuf.items();i++)
400
   {
401
       x  = next_x;
402
       y1 = next_y1;
403
       y2 = next_y2;
404
       pos = next_pos;
405
406
       if (i==circbuf.items())
407
       {
408
           next_x = X(simulation.getSimTime());
409
       }
410
       else
411
       {
412
           next_pos=(next_pos+1)%circbuf.size();
413
           CircBuffer::CBEntry& p = circbuf.entry(next_pos);
414
           next_x =  X(p.t);
415
           next_y1 = Y(p.value1);
416
           next_y2 = Y(p.value2);
417
       }
418
419
       if (x>=0)
420
       {
421
           char tag[16];
422
           sprintf(tag,"value%d",pos);
423
424
           const int d = 2;
425
           char coords[64];
426
           switch (drawing_mode)
427
           {
428
             case DRAW_DOTS:
429
                // draw rectangle 1
430
                sprintf(coords,"%d %d %d %d", x-d, y1-d, x+d, y1+d);
431
                CHK(Tcl_VarEval(interp, canvas," create rect ",coords,
432
                                    " -tag ",tag," -outline red -fill red", NULL));
433
                break;
434
             case DRAW_PINS:
435
                // draw rectangle 1
436
                sprintf(coords,"%d %d %d %d", x, y_zero, x, y1);
437
                CHK(Tcl_VarEval(interp, canvas," create line ",coords,
438
                                    " -tag ",tag," -fill red", NULL));
439
                break;
440
             case DRAW_LINES:
441
                // draw rectangle 1
442
                sprintf(coords,"%d %d %d %d", x, y1, next_x, next_y1);
443
                CHK(Tcl_VarEval(interp, canvas," create line ",coords,
444
                                    " -tag ",tag," -fill red", NULL));
445
                break;
446
             case DRAW_SAMPLEHOLD:
447
                // draw rectangle 1
448
                sprintf(coords,"%d %d %d %d", x, y1, next_x, y1);
449
                CHK(Tcl_VarEval(interp, canvas," create line ",coords,
450
                                    " -tag ",tag," -fill red", NULL));
451
                break;
452
             case DRAW_BARS:
453
                // draw rectangle 1
454
                sprintf(coords,"%d %d %d %d", x, y_zero, next_x, y1);
455
                CHK(Tcl_VarEval(interp, canvas," create rect ",coords,
456
                                    " -tag ",tag," -outline red -fill red", NULL));
457
                break;
458
           }
459
       }
460
   }
461
462
   simtime_t tmin = tbase - tf * (canvaswidth-20);
463
464
   if (moving_tline < tmin)
465
       moving_tline = tbase;
466
467
   double midy = (miny+maxy)/2;
468
469
   // add some basic labeling
470
   char coords[64], value[64];
471
   sprintf(coords,"%d %d %d %d", X(tmin)-2, Y(miny), X(tbase)+2, Y(miny));
472
   CHK(Tcl_VarEval(interp, canvas," create line ",coords," -fill black", NULL));
473
474
   sprintf(coords,"%d %d %d %d", X(tmin)-2, Y(maxy), X(tbase)+2, Y(maxy));
475
   CHK(Tcl_VarEval(interp, canvas," create line ",coords," -fill black", NULL));
476
477
   sprintf(coords,"%d %d %d %d", X(tbase)-2, Y(midy), X(tbase)+2, Y(midy));
478
   CHK(Tcl_VarEval(interp, canvas," create line ",coords," -fill black", NULL));
479
480
   sprintf(coords,"%d %d %d %d", X(tbase), Y(maxy)-2, X(tbase), Y(miny)+2);
481
   CHK(Tcl_VarEval(interp, canvas," create line ",coords," -fill black", NULL));
482
483
   sprintf(coords,"%d %d %d %d", X(tmin), Y(maxy)-2, X(tmin), Y(miny)+2);
484
   CHK(Tcl_VarEval(interp, canvas," create line ",coords," -fill black", NULL));
485
486
   sprintf(coords,"%d %d %d %d", X(moving_tline), Y(maxy)-2, X(moving_tline), Y(miny)+2);
487
   CHK(Tcl_VarEval(interp, canvas," create line ",coords," -fill black", NULL));
488
489
   sprintf(coords,"%d %d", X(tbase)-3, Y(miny));
490
   sprintf(value,"%.9g", miny);
491
   CHK(Tcl_VarEval(interp, canvas," create text ",coords," -text ", value, " -anchor se", NULL));
492
493
   sprintf(coords,"%d %d", X(tbase)-3, Y(maxy));
494
   sprintf(value,"%.9g", maxy);
495
   CHK(Tcl_VarEval(interp, canvas," create text ",coords," -text ", value, " -anchor ne", NULL));
496
497
   sprintf(coords,"%d %d", X(tbase)-3, Y(midy));
498
   sprintf(value,"%.9g", midy);
499
   CHK(Tcl_VarEval(interp, canvas," create text ",coords," -text ", value, " -anchor e", NULL));
500
501
   sprintf(coords,"%d %d", X(tbase)+3, Y(miny));
502
   sprintf(value,"%s", SIMTIME_STR(tbase));
503
   CHK(Tcl_VarEval(interp, canvas," create text ",coords," -text ", value, " -anchor ne", NULL));
504
505
   sprintf(coords,"%d %d", X(tmin)-3, Y(miny));
506
   sprintf(value,"%s", SIMTIME_STR(tmin));
507
   CHK(Tcl_VarEval(interp, canvas," create text ",coords," -text ", value, " -anchor nw", NULL));
508
509
   sprintf(coords,"%d %d", X(moving_tline)-3, Y(miny));
510
   sprintf(value,"%s", SIMTIME_STR(moving_tline));
511
   CHK(Tcl_VarEval(interp, canvas," create text ",coords," -text ", value, " -anchor n", NULL));
512
513
#undef X
514
#undef Y
515
}
516
517
static const char *drawingmodes[] = {
518
      "dots", "bars", "pins", "sample-hold", "lines", NULL
519
};
520
521
void TOutVectorWindow::generalInfo( char *buf )
522
{
523
   if (circbuf.items()==0)
524
   {
525
        strcpy(buf,"(no write since opening window)");
526
        return;
527
   }
528
529
   CircBuffer::CBEntry& p = circbuf.entry(circbuf.headPos());
530
   sprintf(buf, "Last value: t=%s  value=%g", SIMTIME_STR(p.t), p.value1);
531
}
532
533
void TOutVectorWindow::valueInfo( char *buf, int valueindex )
534
{
535
   CircBuffer::CBEntry& p = circbuf.entry(valueindex);
536
   sprintf(buf, "t=%s  value=%g", SIMTIME_STR(p.t), p.value1);
537
538
   moving_tline = SIMTIME_DBL(p.t);
539
}
540
541
void TOutVectorWindow::getConfig( char *buf )
542
{
543
   sprintf(buf,"%d %g %g %g %s", autoscale, time_factor, miny, maxy, drawingmodes[drawing_mode] );
544
}
545
546
void TOutVectorWindow::setConfig( bool autosc, double timefac, double min_y, double max_y, const char *mode)
547
{
548
   // store new parameters
549
   autoscale = autosc;
550
   time_factor = timefac;
551
   miny = min_y;
552
   maxy = max_y;
553
554
   // corrections on y range
555
   if (miny>maxy) maxy=miny;
556
   if (miny==maxy)
557
   {
558
       if (miny!=0) {miny-=fabs(miny/3); maxy+=fabs(maxy/3);}
559
       else  {miny=-0.5; maxy=0.5;}
560
   }
561
562
   // identify drawing style
563
   int i;
564
   for (i=0; i<NUM_DRAWINGMODES; i++)
565
       if (0==strcmp(mode,drawingmodes[i]))
566
           break;
567
   if (i!=NUM_DRAWINGMODES)
568
       drawing_mode = i;
569
}
570
571
int TOutVectorWindow::inspectorCommand(Tcl_Interp *interp, int argc, const char **argv)
572
{
573
   if (argc<1) {Tcl_SetResult(interp, TCLCONST("wrong argcount"), TCL_STATIC); return TCL_ERROR;}
574
575
   if (strcmp(argv[0],"value")==0)   // 'opp_inspectorcommand <inspector> value ...'
576
   {
577
      if (argc>2) {Tcl_SetResult(interp, TCLCONST("wrong argcount"), TCL_STATIC); return TCL_ERROR;}
578
579
      char buf[128];
580
      if (argc==1)
581
         generalInfo(buf);
582
      else
583
         valueInfo(buf, atoi(argv[1]));
584
      Tcl_SetResult(interp,buf,TCL_VOLATILE);
585
      return TCL_OK;
586
   }
587
   else if (strcmp(argv[0],"config")==0)  // 'opp_inspectorcommand <inspector> config ...'
588
   {
589
      if (argc!=6 && argc!=1) {Tcl_SetResult(interp, TCLCONST("wrong argcount"), TCL_STATIC); return TCL_ERROR;}
590
591
      // get/set configuration: "timefactor miny maxy drawingmode"
592
      if (argc==1)
593
      {
594
         char buf[128];
595
         getConfig(buf);
596
         Tcl_SetResult(interp, buf, TCL_VOLATILE);
597
      }
598
      else
599
      {
600
         setConfig( atoi(argv[1]), atof(argv[2]), atof(argv[3]), atof(argv[4]), argv[5] );
601
      }
602
      return TCL_OK;
603
   }
604
   return TCL_ERROR;
605
}
606
607