]> git.uio.no Git - u/mrichter/AliRoot.git/blame - MUON/AliMUONPainterDataSourceFrame.cxx
AliMUONTrackerIO:
[u/mrichter/AliRoot.git] / MUON / AliMUONPainterDataSourceFrame.cxx
CommitLineData
0145e89a 1/**************************************************************************
2* Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
3* *
4* Author: The ALICE Off-line Project. *
5* Contributors are mentioned in the code where appropriate. *
6* *
7* Permission to use, copy, modify and distribute this software and its *
8* documentation strictly for non-commercial purposes is hereby granted *
9* without fee, provided that the above copyright notice appears in all *
10* copies and that both the copyright notice and this permission notice *
11* appear in the supporting documentation. The authors make no claims *
12* about the suitability of this software for any purpose. It is *
13* provided "as is" without express or implied warranty. *
14**************************************************************************/
15
16// $Id$
17
18#include "AliMUONPainterDataSourceFrame.h"
19
8741815f 20
21#include "AliLog.h"
0145e89a 22#include "AliMUONPainterDataSourceItem.h"
23#include "AliMUONPainterEnv.h"
24#include "AliMUONPainterHelper.h"
25#include "AliMUONPainterRegistry.h"
8741815f 26#include "AliMUONTrackerCalibratedDataMaker.h"
0145e89a 27#include "AliMUONTrackerOCDBDataMaker.h"
28#include "AliMUONTrackerRawDataMaker.h"
0145e89a 29#include "AliRawReaderDate.h"
30#include "AliRawReaderRoot.h"
31#include <TGButton.h>
32#include <TGComboBox.h>
33#include <TGFileDialog.h>
34#include <TGNumberEntry.h>
35#include <TGTextEntry.h>
36#include <TGrid.h>
37#include <TObjArray.h>
38#include <TObjString.h>
39#include <TRegexp.h>
40#include <TString.h>
41#include <TSystem.h>
42
43///\class AliMUONPainterDataSourceFrame
44///
45/// A complete frame to select and display various data sources to
46/// be displayed : either raw data or OCDB data.
47/// Later on we might add digits and clusters for instance.
48///
49///\author Laurent Aphecetche, Subatech
50
51const char* AliMUONPainterDataSourceFrame::fgkNumberOfDataSourcesKey = "NumberOfDataSources";
52const char* AliMUONPainterDataSourceFrame::fgkDataSourceURIKey = "DataSourceURI.%d";
53
54///\cond CLASSIMP
55ClassImp(AliMUONPainterDataSourceFrame)
56///\endcond
57
58//_____________________________________________________________________________
59AliMUONPainterDataSourceFrame::AliMUONPainterDataSourceFrame(const TGWindow* p, UInt_t w, UInt_t h)
60: TGCompositeFrame(p,w,h,kVerticalFrame),
10eb3d17 61 fRecentSourceSelector(new TGGroupFrame(this,"Recent sources",kHorizontalFrame)),
62 fRawSelector(new TGGroupFrame(this,"Raw file URI",kHorizontalFrame)),
8741815f 63 fRawSelector2(new TGCompositeFrame(fRawSelector,w,h,kVerticalFrame)),
64 fRawSelector21(new TGCompositeFrame(fRawSelector2,w,h,kHorizontalFrame)),
65 fRawSelector22(new TGCompositeFrame(fRawSelector2,w,h,kHorizontalFrame)),
66 fRawSelector23(new TGCompositeFrame(fRawSelector2,w,h,kHorizontalFrame)),
10eb3d17 67 fCalibrateNoGain(new TGCheckButton(fRawSelector22,"Zero suppress")),
68 fCalibrateGainConstantCapa(new TGCheckButton(fRawSelector22,"Zero suppress + gain (capa cste)")),
69 fCalibrateGain(new TGCheckButton(fRawSelector22,"Full calib (zero suppress + gain with capa)")),
8741815f 70 fHistogramButton(new TGCheckButton(fRawSelector23,"Histogram")),
10eb3d17 71 fHistoMin(new TGNumberEntry(fRawSelector23,0)),
72 fHistoMax(new TGNumberEntry(fRawSelector23,4096)),
8741815f 73 fRawOCDBPath(new TGTextEntry(fRawSelector22,"")),
10eb3d17 74 fOCDBSelector(new TGGroupFrame(this,"OCDB Path",kHorizontalFrame)),
75 fDataReaders(new TGGroupFrame(this,"Data sources")),
8741815f 76 fFilePath(new TGTextEntry(fRawSelector21,"")),
0145e89a 77 fOCDBPath(new TGTextEntry(fOCDBSelector,"")),
78 fRunSelector(new TGNumberEntry(fOCDBSelector,0)),
79 fOCDBTypes(new TGComboBox(fOCDBSelector)),
80 fRecentSources(new TGComboBox(fRecentSourceSelector)),
81 fItems(new TObjArray)
82{
83 /// Ctor
84
85 AliMUONPainterRegistry* reg = AliMUONPainterRegistry::Instance();
86
87 reg->Connect("DataReaderWasRegistered(AliMUONVTrackerDataMaker*)",
88 "AliMUONPainterDataSourceFrame",
89 this,
90 "DataReaderWasRegistered(AliMUONVTrackerDataMaker*)");
91
92 reg->Connect("DataReaderWasUnregistered(AliMUONVTrackerDataMaker*)",
93 "AliMUONPainterDataSourceFrame",
94 this,
95 "DataReaderWasUnregistered(AliMUONVTrackerDataMaker*)");
96
97 fItems->SetOwner(kFALSE);
98
99 /// Recent source selection
100
101 AliMUONPainterEnv* env = AliMUONPainterHelper::Instance()->Env();
102
103 Int_t nsources = env->Integer(fgkNumberOfDataSourcesKey);
104
105 for ( Int_t i = 0; i < nsources; ++i )
106 {
107 AddRecentSource(env->String(Form(fgkDataSourceURIKey,i)));
108 }
109
110 fRecentSources->Resize(100,20);
111
112 TGButton* createRecentButton = new TGTextButton(fRecentSourceSelector,"Create data source");
113 createRecentButton->Connect("Clicked()",
114 "AliMUONPainterDataSourceFrame",
115 this,
116 "OpenRecentSource()");
117
118 fRecentSourceSelector->AddFrame(fRecentSources,new TGLayoutHints(kLHintsExpandX | kLHintsTop,5,5,5,5));
119 fRecentSourceSelector->AddFrame(createRecentButton,new TGLayoutHints(kLHintsTop,5,5,5,5));
120
121 /// Raw file selection
122
8741815f 123 TGButton* openButton = new TGPictureButton(fRawSelector21,
0145e89a 124 gClient->GetPicture("fileopen.xpm"));
125 openButton->SetToolTipText("Click to open file dialog");
126
8741815f 127 fRawSelector2->AddFrame(fRawSelector21, new TGLayoutHints(kLHintsExpandX,5,5,5,5));
128 fRawSelector2->AddFrame(fRawSelector22, new TGLayoutHints(kLHintsExpandX,5,5,5,5));
129 fRawSelector2->AddFrame(fRawSelector23, new TGLayoutHints(kLHintsExpandX,5,5,5,5));
130
131 fRawSelector21->AddFrame(openButton,new TGLayoutHints(kLHintsTop,5,5,5,5));
132 fRawSelector21->AddFrame(fFilePath, new TGLayoutHints(kLHintsExpandX | kLHintsTop,5,5,5,5));
133
10eb3d17 134 fRawSelector22->AddFrame(fCalibrateNoGain, new TGLayoutHints(kLHintsTop,5,5,5,5));
135 fRawSelector22->AddFrame(fCalibrateGainConstantCapa, new TGLayoutHints(kLHintsTop,5,5,5,5));
136 fRawSelector22->AddFrame(fCalibrateGain, new TGLayoutHints(kLHintsTop,5,5,5,5));
8741815f 137 fRawSelector22->AddFrame(fRawOCDBPath, new TGLayoutHints(kLHintsExpandX | kLHintsTop,5,5,5,5));
138 fRawOCDBPath->SetEnabled(kFALSE);
0145e89a 139
8741815f 140 fRawSelector23->AddFrame(fHistogramButton,new TGLayoutHints(kLHintsTop,5,5,5,5));
10eb3d17 141
142 fHistogramButton->Connect("Clicked()","AliMUONPainterDataSourceFrame",this,"HistogramButtonClicked()");
143
144 fHistoMin->SetState(kFALSE);
145 fHistoMax->SetState(kFALSE);
146
147 fRawSelector23->AddFrame(fHistoMin,new TGLayoutHints(kLHintsTop,5,5,5,5));
148 fRawSelector23->AddFrame(fHistoMax,new TGLayoutHints(kLHintsTop,5,5,5,5));
149
8741815f 150 TGButton* createRawButton = new TGTextButton(fRawSelector,"Create data source");
0145e89a 151
8741815f 152 fRawSelector->AddFrame(fRawSelector2, new TGLayoutHints(kLHintsExpandX | kLHintsTop,5,5,5,5));
153 fRawSelector->AddFrame(createRawButton, new TGLayoutHints(kLHintsCenterY,5,5,5,5));
154
10eb3d17 155 fCalibrateNoGain->Connect("Clicked()","AliMUONPainterDataSourceFrame",this,"CalibrateButtonClicked()");
156 fCalibrateGainConstantCapa->Connect("Clicked()","AliMUONPainterDataSourceFrame",this,"CalibrateButtonClicked()");
157 fCalibrateGain->Connect("Clicked()","AliMUONPainterDataSourceFrame",this,"CalibrateButtonClicked()");
8741815f 158
0145e89a 159 openButton->Connect("Clicked()",
160 "AliMUONPainterDataSourceFrame",
161 this,
162 "OpenFileDialog()");
163
164 createRawButton->Connect("Clicked()",
165 "AliMUONPainterDataSourceFrame",
166 this,
167 "CreateRawDataSource()");
168
169 /// OCDB selection
170
171 fOCDBTypes->AddEntry("Pedestals",0);
172 fOCDBTypes->AddEntry("Gains",1);
173 fOCDBTypes->AddEntry("Capacitances",2);
174 fOCDBTypes->Select(0);
175 fOCDBTypes->Resize(100,20);
176
177 TGButton* createOCDBButton = new TGTextButton(fOCDBSelector,"Create data source");
178 createOCDBButton->Connect("Clicked()",
179 "AliMUONPainterDataSourceFrame",
180 this,
181 "CreateOCDBDataSource()");
182
183
184 fOCDBSelector->AddFrame(fOCDBPath,new TGLayoutHints(kLHintsExpandX | kLHintsTop,5,5,5,5));
185 fOCDBSelector->AddFrame(fRunSelector,new TGLayoutHints(kLHintsTop,5,5,5,5));
186 fOCDBSelector->AddFrame(fOCDBTypes,new TGLayoutHints(kLHintsExpandX | kLHintsTop,5,5,5,5));
187 fOCDBSelector->AddFrame(createOCDBButton,new TGLayoutHints(kLHintsTop,5,5,5,5));
188
189 AddFrame(fRecentSourceSelector,new TGLayoutHints(kLHintsExpandX,10,10,10,10));
190
191 AddFrame(fRawSelector,new TGLayoutHints(kLHintsExpandX,10,10,10,10));
192
193 AddFrame(fOCDBSelector,new TGLayoutHints(kLHintsExpandX,10,10,10,10));
194
195 AddFrame(fDataReaders, new TGLayoutHints(kLHintsExpandX,10,10,10,10));
196
197}
198
199//_____________________________________________________________________________
200AliMUONPainterDataSourceFrame::~AliMUONPainterDataSourceFrame()
201{
202 /// dtor
203
204 delete fItems;
205}
206
207//_____________________________________________________________________________
208void
209AliMUONPainterDataSourceFrame::AddRecentSource(const char* name)
210{
211 /// Add a source to the list of recently used sources
212
213 TGListBox* lb = fRecentSources->GetListBox();
214
215 for ( Int_t i = 0; i < lb->GetNumberOfEntries(); ++i )
216 {
217 TGTextLBEntry* t = (TGTextLBEntry*)lb->GetEntry(i);
218 TString s(t->GetText()->GetString());
219 if ( s == name )
220 {
221 return;
222 }
223 }
224
225 fRecentSources->AddEntry(name,lb->GetNumberOfEntries());
226 fRecentSources->MapSubwindows();
227 fRecentSources->Layout();
228}
229
8741815f 230//_____________________________________________________________________________
231void
232AliMUONPainterDataSourceFrame::CalibrateButtonClicked()
233{
234 /// Calibrate button was clicked.
235
10eb3d17 236 if ( fCalibrateNoGain->IsOn() ||
237 fCalibrateGainConstantCapa->IsOn() ||
238 fCalibrateGain->IsOn() )
8741815f 239 {
240 fRawOCDBPath->SetEnabled(kTRUE);
241 fRawOCDBPath->SetFocus();
8741815f 242 }
243 else
244 {
245 fRawOCDBPath->SetEnabled(kFALSE);
10eb3d17 246 }
247}
248
249//_____________________________________________________________________________
250void
251AliMUONPainterDataSourceFrame::HistogramButtonClicked()
252{
253 /// Histogram button was clicked.
254
255 if ( fHistogramButton->IsOn() )
256 {
257 fHistoMin->SetState(kTRUE);
258 fHistoMax->SetState(kTRUE);
259 }
260 else
261 {
262 fHistoMin->SetState(kFALSE);
263 fHistoMax->SetState(kFALSE);
8741815f 264 }
265}
266
0145e89a 267//_____________________________________________________________________________
268void
269AliMUONPainterDataSourceFrame::CreateOCDBDataSource()
270{
271 /// Create an OCDB data source (using information from the widgets)
272
273 TString cdbPath = fOCDBPath->GetText();
274 Int_t runNumber = fRunSelector->GetIntNumber();
275 TGTextLBEntry* t = static_cast<TGTextLBEntry*>(fOCDBTypes->GetSelectedEntry());
276 TString type = t->GetText()->GetString();
277
278 CreateOCDBDataSource(cdbPath,runNumber,type);
279
280 fOCDBPath->SetText("");
281 fRunSelector->SetNumber(0);
282}
283
284//_____________________________________________________________________________
285void
286AliMUONPainterDataSourceFrame::CreateOCDBDataSource(const TString& uri)
287{
288 /// Create an OCDB data source, given it's URI
289
290 TObjArray* a = uri.Tokenize(";");
291 TString cdbPath = static_cast<TObjString*>(a->At(1))->String();
292 TString srun = static_cast<TObjString*>(a->At(2))->String();
293 TString type = static_cast<TObjString*>(a->At(3))->String();
294
295 CreateOCDBDataSource(cdbPath,atoi(srun.Data()),type);
296
297 delete a;
298}
299
300//_____________________________________________________________________________
301void
302AliMUONPainterDataSourceFrame::CreateOCDBDataSource(const TString& cdbPath,
303 Int_t runNumber,
304 const TString& type)
305{
306 /// Create an OCDB data source for a given (path,runnumber,type) triplet
307
308 AliMUONVTrackerDataMaker* reader = new AliMUONTrackerOCDBDataMaker(cdbPath.Data(),
309 runNumber,
310 type.Data());
311
312 if ( reader->IsValid() )
313 {
314 AliMUONPainterRegistry::Instance()->Register(reader);
315
316 AliMUONPainterEnv* env = AliMUONPainterHelper::Instance()->Env();
317
318 Int_t n = env->Integer(fgkNumberOfDataSourcesKey);
319
320 env->Set(fgkNumberOfDataSourcesKey,n+1);
321
322 TString ds(Form("OCDB;%s;%d;%s",cdbPath.Data(),runNumber,type.Data()));
323
324 env->Set(Form(fgkDataSourceURIKey,n),ds.Data());
325
326 env->Save();
327
328 AddRecentSource(ds.Data());
329 }
330}
331
332//_____________________________________________________________________________
333void
334AliMUONPainterDataSourceFrame::CreateRawDataSource()
335{
336 /// Create a new raw data source (using info from the widgets)
337
338 TString uri(gSystem->ExpandPathName(fFilePath->GetText()));
339
340 if ( gSystem->AccessPathName(uri.Data()) )
341 {
342 AliError(Form("File %s does not exist",uri.Data()));
343 fFilePath->SetText("");
344 return;
345 }
346
10eb3d17 347 TString calibMode("");
348 TString name("RAW");
349
350 if ( fCalibrateGain->IsOn() )
351 {
352 calibMode = "GAIN";
353 name = "CALC";
354 }
355
356 if ( fCalibrateGainConstantCapa->IsOn() )
357 {
358 calibMode = "GAINCONSTANTCAPA";
359 name = "CALG";
360 }
361
362 if ( fCalibrateNoGain->IsOn() )
363 {
364 calibMode = "NOGAIN";
365 name = "CALZ";
366 }
367
368 uri = Form("%s%s;%s;%s;%s;%s;%s",
369 ( fHistogramButton->IsOn() ? "H":""),
370 name.Data(),uri.Data(),
371 ( strlen(fRawOCDBPath->GetText()) > 0 ? fRawOCDBPath->GetText() : " "),
372 ( calibMode.Length() > 0 ? calibMode.Data() : " "),
373 Form("%e",fHistoMin->GetNumber()),
374 Form("%e",fHistoMax->GetNumber()));
0145e89a 375
376 if ( CreateRawDataSource(uri) )
377 {
378 fFilePath->SetText("");
379 fRawOCDBPath->SetText("");
380 }
381}
382
383//_____________________________________________________________________________
384Bool_t
385AliMUONPainterDataSourceFrame::CreateRawDataSource(const TString& uri)
386{
387 /// Create a new raw data source, given its URI
388
389 TString filename;
390 TString ocdbPath;
10eb3d17 391 TString calibMode;
392 TString sxmin("0.0");
393 TString sxmax("4096.0");
0145e89a 394
395 TObjArray* a = uri.Tokenize(";");
396
397 filename = static_cast<TObjString*>(a->At(1))->String();
398
399 if ( a->GetLast() > 1 )
400 {
401 ocdbPath = static_cast<TObjString*>(a->At(2))->String();
10eb3d17 402 if ( ocdbPath == " " ) ocdbPath = "";
403 }
404
405 if ( a->GetLast() > 2 )
406 {
407 calibMode = static_cast<TObjString*>(a->At(3))->String();
408 if ( calibMode == " " ) calibMode = "";
409 }
410
411 if ( a->GetLast() > 3 )
412 {
413 sxmin = static_cast<TObjString*>(a->At(4))->String();
414 }
415
416 if ( a->GetLast() > 4 )
417 {
418 sxmax = static_cast<TObjString*>(a->At(5))->String();
0145e89a 419 }
420
421 AliRawReader* rawReader = 0x0;
422
423 if ( filename.Contains(TRegexp(".root$")) )
424 {
425 AliDebug(1,"Using RawReaderRoot");
426 if ( filename.Contains(TRegexp("^alien")) )
427 {
428 // insure we've initialized the grid...
429 if (!gGrid)
430 {
431 TGrid::Connect("alien://");
432 }
433 }
434
435 rawReader = new AliRawReaderRoot(filename.Data());
436 }
10eb3d17 437 else
0145e89a 438 {
10eb3d17 439 /// Anything not .root is supposed to be DATE file
0145e89a 440 AliDebug(1,"Using RawReaderDate");
441 rawReader = new AliRawReaderDate(filename.Data());
442 }
10eb3d17 443
444 /// Basic test to see if the file is correct
445 Bool_t ok = rawReader->NextEvent();
446 if (!ok)
0145e89a 447 {
10eb3d17 448 AliError(Form("File %s does not seem to be a raw data file",filename.Data()));
449 fFilePath->SetText("");
0145e89a 450 return kFALSE;
451 }
452
10eb3d17 453 rawReader->RewindEvents();
454
8741815f 455 AliMUONVTrackerDataMaker* reader(0x0);
456 Bool_t histogram(kFALSE);
457
10eb3d17 458 if ( uri.Contains(TRegexp("^H")) ) histogram = kTRUE;
459
8741815f 460 if ( ocdbPath.Length() > 0 )
461 {
10eb3d17 462 reader = new AliMUONTrackerCalibratedDataMaker(rawReader,ocdbPath.Data(),
463 calibMode.Data(),
464 histogram,
465 sxmin.Atof(),
466 sxmax.Atof());
8741815f 467 }
468 else
469 {
8741815f 470 reader = new AliMUONTrackerRawDataMaker(rawReader,histogram);
471 }
0145e89a 472
473 reader->SetSource(filename.Data());
474
475 AliMUONPainterRegistry::Instance()->Register(reader);
476
477 AliMUONPainterEnv* env = AliMUONPainterHelper::Instance()->Env();
478
479 Int_t n = env->Integer(fgkNumberOfDataSourcesKey);
480
481 env->Set(fgkNumberOfDataSourcesKey,n+1);
482
10eb3d17 483 env->Set(Form(fgkDataSourceURIKey,n),uri.Data());
0145e89a 484
10eb3d17 485 AddRecentSource(uri.Data());
0145e89a 486
487 env->Save();
488
489 return kTRUE;
490}
491
492//_____________________________________________________________________________
493void
494AliMUONPainterDataSourceFrame::DataReaderWasRegistered(AliMUONVTrackerDataMaker* reader)
495{
496 /// Update ourselves as a new data reader was created
497
0145e89a 498 AliMUONPainterDataSourceItem* item = new AliMUONPainterDataSourceItem(fDataReaders,100,20,reader);
499
10eb3d17 500 item->Connect("StartRunning()",
501 "AliMUONPainterDataSourceFrame",
502 this,
503 Form("StartRunning(=(AliMUONPainterDataSourceItem*)(0x%x))",item));
504
505 item->Connect("StopRunning()",
506 "AliMUONPainterDataSourceFrame",
507 this,
508 Form("StopRunning(=(AliMUONPainterDataSourceItem*)(0x%x))",item));
509
0145e89a 510 fDataReaders->AddFrame(item);
511
512 fItems->Add(item);
513
514 fDataReaders->MapSubwindows();
515 fDataReaders->Resize();
516}
517
518//_____________________________________________________________________________
519void
10eb3d17 520AliMUONPainterDataSourceFrame::DataReaderWasUnregistered(AliMUONVTrackerDataMaker* /*reader*/)
0145e89a 521{
522 /// Update ourselves as a new data reader was deleted
523
10eb3d17 524 // here we should find which (if any) datasourceitem has this reader, and remove it
525
0145e89a 526}
527
528//_____________________________________________________________________________
529void
530AliMUONPainterDataSourceFrame::OpenFileDialog()
531{
532 /// Open a file dialog to select a file to be read
533
534 TGFileInfo fileInfo;
535
10eb3d17 536 const char* fileTypes[] = {
537 "ROOT files","*.root",
538 "DATE files","*.raw",
539 "All files","*",
540 0,0 };
541
542 fileInfo.fFileTypes = fileTypes;
543
0145e89a 544 new TGFileDialog(gClient->GetRoot(),gClient->GetRoot(),
545 kFDOpen,&fileInfo);
546
547
548 fFilePath->SetText(gSystem->ExpandPathName(Form("%s",fileInfo.fFilename)));
549}
550
551
552//_____________________________________________________________________________
553void
554AliMUONPainterDataSourceFrame::OpenRecentSource()
555{
556 /// Open one source from the recently used ones
557
558 TGTextLBEntry* t = (TGTextLBEntry*)fRecentSources->GetSelectedEntry();
559
560 TString uri(t->GetText()->GetString());
561
10eb3d17 562 if ( uri.Contains(TRegexp("^RAW")) || uri.Contains(TRegexp("^HRAW")) ||
563 uri.Contains(TRegexp("^CAL")) || uri.Contains(TRegexp("^HCAL")) )
0145e89a 564 {
565 CreateRawDataSource(uri);
566 }
567 else if ( uri.Contains(TRegexp("^OCDB")) )
568 {
569 CreateOCDBDataSource(uri);
570 }
571
572 fRecentSources->Select(-1);
573}
574
10eb3d17 575//_____________________________________________________________________________
576void
577AliMUONPainterDataSourceFrame::StartRunning(AliMUONPainterDataSourceItem* item)
578{
579 /// One data source starts running. Disable the Run button of the other ones
580 TIter next(fItems);
581 AliMUONPainterDataSourceItem* o;
582 while ( ( o = static_cast<AliMUONPainterDataSourceItem*>(next()) ) )
583 {
584 if ( o != item )
585 {
586 o->DisableRun();
587 }
588 }
589}
590
591//_____________________________________________________________________________
592void
593AliMUONPainterDataSourceFrame::StopRunning(AliMUONPainterDataSourceItem* /*item*/)
594{
595 /// One data source stops running. Enable the Run button of all items
596 TIter next(fItems);
597 AliMUONPainterDataSourceItem* o;
598 while ( ( o = static_cast<AliMUONPainterDataSourceItem*>(next()) ) )
599 {
600 o->EnableRun();
601 }
602}
603