]> git.uio.no Git - u/mrichter/AliRoot.git/blob - MUON/AliMUONTriggerGUI.cxx
Implemented a new version of cluster (with its store and iterator):
[u/mrichter/AliRoot.git] / MUON / AliMUONTriggerGUI.cxx
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 //-----------------------------------------------------------------------------
19 /// \class AliMUONTriggerGUI
20 /// Graphical User Interface utility class for the MUON trigger detector
21 /// It creates, after initialisation with a data file, a sensitive map
22 /// of the trigger boards
23 /// \author Bogdan Vulpescu, LPC Clermont-Ferrand
24 //-----------------------------------------------------------------------------
25
26 #include "AliMUONTriggerGUI.h"
27 #include "AliMUONTriggerGUIboard.h"
28 #include "AliMUONTriggerGUIdimap.h"
29 #include "AliMUONTriggerGUIbdmap.h"
30
31 #include "AliMpSegmentation.h"
32 #include "AliMpVSegmentation.h"
33 #include "AliMpPad.h"
34 #include "AliMpIntPair.h"
35 #include "AliMUON.h"
36 #include "AliMpDEIterator.h"
37 #include "AliMUONGeometryTransformer.h"
38 #include "AliMUONTriggerCrateStore.h"
39 #include "AliMUONLocalTriggerBoard.h"
40 #include "AliMUONTriggerElectronics.h"
41 #include "AliMUONCalibrationData.h"
42
43 #include "AliRun.h"
44 #include "AliRunLoader.h"
45 #include "AliCDBManager.h"
46
47 #include <TSystem.h>
48 #include <TGLabel.h>
49 #include <TGFrame.h>
50 #include <TApplication.h>
51 #include <TGDimension.h>
52 #include <TString.h>
53 #include <TGMenu.h>
54 #include <TGTextEntry.h>
55 #include <TGButton.h>
56 #include <TFile.h>
57 #include <TGImageMap.h>
58 #include <TGTextBuffer.h>
59
60 /// \cond CLASSIMP
61 ClassImp(AliMUONTriggerGUI)
62 /// \endcond
63
64 //__________________________________________________________________________
65 AliMUONTriggerGUI::AliMUONTriggerGUI()
66   : TObject(),
67     fMain(0),
68     fImageMap(0),
69     fTxtBuffer1(0),
70     fTxtBuffer2(0),
71     fTxtCircuit(0),
72     fRunInput(0),
73     fError(0),
74     fControl(0),
75     fCircuit(0),
76     fSkipToEventTxt(0),
77     fFileName(0),
78     fPath(0),
79     fEvString(0),
80     fChamber(0),
81     fEvent(0),
82     fEventsPerRun(0),
83     fLoader(0),
84     fRunLoader(0),
85     fCDBManager(0),
86     fCalibrationData(0),
87     fBoardsInit(0),
88     fDiMap(0),
89     fTriggerProcessor(0),
90     fBoards(0)
91 {
92   /// main GUI frame of the trigger monitor
93
94   fChamber = 0;
95   fEvent   = 0;
96   fEventsPerRun = 0;
97   fRunLoader = 0;
98   fDiMap = 0;
99
100   fBoards = 0;
101   fFileName = new TString("");
102   fEvString = new TString("");
103   fPath = new TString("");
104
105   fTriggerProcessor = 0;
106
107   fCDBManager = AliCDBManager::Instance();
108   fCDBManager->SetDefaultStorage("local://$ALICE_ROOT");
109   fCalibrationData = new AliMUONCalibrationData(0);  // runnumber zero
110   
111   // Main frame
112
113   fMain = new TGMainFrame(gClient->GetRoot(), 750, 420);
114   fMain->Connect("CloseWindow()", "AliMUONTriggerGUI", this, "CloseWindow()");
115
116   // Menu bar
117   
118   TGMenuBar *menuBar = new TGMenuBar(fMain);
119   
120   // File menu
121
122   TGPopupMenu *menuFile = new TGPopupMenu(gClient->GetRoot());
123   //menuFile->AddLabel("");
124
125   menuFile->AddEntry("Run",     kMFILERUN);
126   menuFile->AddEntry("Control", kMFILECNTRL);
127   menuFile->AddEntry("Exit",    kMFILEEXIT);
128
129   menuFile->Connect("Activated(Int_t)", "AliMUONTriggerGUI", this, "HandleMenu(Int_t)");
130
131   // Circuit menu
132
133   TGPopupMenu *menuCircuit = new TGPopupMenu(gClient->GetRoot());
134   //menuCircuit->AddLabel("");
135
136   menuCircuit->AddEntry("Open",     kMCIRCUITOPEN);
137
138   menuCircuit->Connect("Activated(Int_t)", "AliMUONTriggerGUI", this, "HandleMenu(Int_t)");
139
140   // Digits map menu
141
142   TGPopupMenu *menuMap = new TGPopupMenu(gClient->GetRoot());
143   //menuMap->AddLabel("");
144
145   menuMap->AddEntry("Digits map",   kMMAPDIGITS);
146   menuMap->AddEntry("Reset digits", kMRESETDIGITS);
147
148   menuMap->Connect("Activated(Int_t)", "AliMUONTriggerGUI", this,
149                     "HandleMenu(Int_t)");
150
151   // Trigger menu
152
153   TGPopupMenu *menuTrigger = new TGPopupMenu(gClient->GetRoot());
154   //menuTrigger->AddLabel("");
155
156   menuTrigger->AddEntry("Trigger DSET",     kMTRIGGERDSET);
157
158   menuTrigger->Connect("Activated(Int_t)", "AliMUONTriggerGUI", this, "HandleMenu(Int_t)");
159
160   // Add menus to the menu bar
161
162   menuBar->AddPopup("File", menuFile, 
163                     new TGLayoutHints(kLHintsTop | kLHintsLeft, 5,5,2,2)
164                     );
165
166   menuBar->AddPopup("Maps", menuMap, 
167                     new TGLayoutHints(kLHintsTop | kLHintsLeft, 5,5,2,2)
168                     );
169
170   menuBar->AddPopup("Circuit", menuCircuit, 
171                     new TGLayoutHints(kLHintsTop | kLHintsLeft, 5,5,2,2)
172                     );
173
174   menuBar->AddPopup("Trigger", menuTrigger, 
175                     new TGLayoutHints(kLHintsTop | kLHintsLeft, 5,5,2,2)
176                     );
177
178   // Add menu bar to the main frame
179
180   fMain->AddFrame(menuBar,
181                   new TGLayoutHints(kLHintsTop | 
182                                     kLHintsLeft | 
183                                     kLHintsExpandX, 
184                                     0, 0, 1, 1)
185                   );
186  
187   // The image map
188
189   fImageMap = new TGImageMap(fMain,"$ALICE_ROOT/MUON/data/guimap.gif");
190   
191   fImageMap->Connect("RegionClicked(Int_t)", "AliMUONTriggerGUI", this, "OpenBoard(Int_t)");
192
193   fImageMap->SetToolTipText("Map of the local boards as seen from the I.P.");
194
195   // Add image map to the main frame
196
197   fMain->AddFrame(fImageMap);
198   fMain->SetWindowName("Map of the local boards as seen from the I.P.");
199
200   // Resize the main frame
201   
202   TGDimension size = fMain->GetDefaultSize();
203   fMain->Resize(size);
204
205   fMain->MapSubwindows();
206
207   fMain->MapWindow();
208
209   fBoardsInit = kFALSE;
210
211   //HandleMenu(kMFILERUN);  // temporary
212
213   //InitBoards();
214   //Init();
215   
216 }
217
218 //__________________________________________________________________________
219 void AliMUONTriggerGUI::HandleMenu(Int_t id)
220 {
221   /// handles entry numbers in the available menus (EMenuIdentifiers)
222
223   TGCompositeFrame *runInput1, *runInput2, *runInput3;
224   TGCompositeFrame *control1, *control2, *circuit1, *circuit2;
225   TGLabel *runL1, *runL2, *circuitL1;
226   TGTextEntry *runText1, *runText2, *circuitText1;
227   TGTextButton *runApply, *runCancel; 
228   TGTextButton *controlClose, *nextEvent, *previousEvent, *skipToEvent;
229   TGTextButton *circuitCancel, *circuitOpen;
230
231   //Int_t trigInfo[kNBoards*6] = {-1};
232   //Int_t nLocalTrigger = 0;
233
234   TString error = TString("");
235   if (id != kMFILEEXIT && id != kMFILERUN && fRunLoader == 0) {
236     error.Append("Run not initialized (Menu: File/Run)");
237     DoErrorGUI(error.Data());
238     return;
239   }
240
241   switch (id) {
242
243   case kMFILEEXIT:
244
245     printf("\nBye. \n");
246     CloseWindow();
247     break;
248     
249   case kMFILERUN:
250     
251     // input main frame
252
253     fRunInput = new TGTransientFrame(gClient->GetRoot(), fMain, 400, 200);
254     fRunInput->Connect("CloseWindow()", "AliMUONTriggerGUI", this, "CloseRunInput()");
255     fRunInput->DontCallClose(); // to avoid double deletions.
256
257     // use hierarchical cleaning
258     fRunInput->SetCleanup(kDeepCleanup);
259
260     fRunInput->SetWindowName("Input file and event number");
261
262     // input galice and event number frames
263
264     runInput1 = new TGCompositeFrame(fRunInput, 400, 200, kHorizontalFrame);
265     runInput2 = new TGCompositeFrame(fRunInput, 400, 200, kHorizontalFrame);
266
267     // .. with labels
268
269     runL1 = new TGLabel(runInput1, new TGString("Path to gAlice:"));
270     runL2 = new TGLabel(runInput2, new TGString("Event number:"));
271
272     // galice text entry
273
274     runText1 = new TGTextEntry(runInput1, fTxtBuffer1 = new TGTextBuffer(100));
275
276     //fPath->Append("dset");                 // temporary
277     //fTxtBuffer1->AddText(0,fPath->Data());  // temporary
278
279     runText1->SetToolTipText("Enter the path to galice.root");
280     runText1->Resize(300, runText1->GetDefaultHeight());
281
282     // event number text entry
283
284     runText2 = new TGTextEntry(runInput2, fTxtBuffer2 = new TGTextBuffer(5));
285
286     fEvString->Form("%d",0);               
287     fTxtBuffer2->AddText(0,fEvString->Data());
288
289     runText2->SetToolTipText("Enter the first event number to start with");
290     runText2->Resize(300, runText2->GetDefaultHeight());
291
292     // add to galice frame
293
294     runInput1->AddFrame(runL1,
295                          new TGLayoutHints(kLHintsLeft | 
296                                            kLHintsCenterY, 
297                                            3, 5, 0, 0)
298                         );
299
300     runInput1->AddFrame(runText1, 
301                         new TGLayoutHints(kLHintsRight | 
302                                           kLHintsCenterY, 
303                                           0, 2, 0, 0)
304                         );
305
306     // add to event number frame
307
308     runInput2->AddFrame(runL2,
309                          new TGLayoutHints(kLHintsLeft | 
310                                            kLHintsCenterY, 
311                                            3, 5, 0, 0)
312                         );
313
314     runInput2->AddFrame(runText2, 
315                         new TGLayoutHints(kLHintsRight | 
316                                           kLHintsCenterY, 
317                                           0, 2, 0, 0)
318                         );
319
320     // add input frames to main input frame
321
322     fRunInput->AddFrame(runInput1, 
323                         new TGLayoutHints(kLHintsTop | kLHintsExpandX, 2, 2, 3, 0));
324     
325     fRunInput->AddFrame(runInput2, 
326                         new TGLayoutHints(kLHintsTop | kLHintsExpandX, 2, 2, 3, 0));
327     
328     // frame with buttons
329
330     runInput3 = new TGCompositeFrame(fRunInput, 400, 200, kHorizontalFrame);
331
332     // buttons
333
334     runApply = new TGTextButton(runInput3, "Apply", 1);
335     runApply->Connect("Clicked()", "AliMUONTriggerGUI", this, "DoRunApply()");
336     runApply->SetToolTipText("Apply changes");
337
338     runCancel = new TGTextButton(runInput3, "Cancel", 2);
339     runCancel->Connect("Clicked()", "AliMUONTriggerGUI", this, "DoRunCancel()");    runCancel->SetToolTipText("Cancel changes");
340
341     // add buttons
342
343     runInput3->AddFrame(runApply, 
344                          new TGLayoutHints(kLHintsTop | 
345                                            kLHintsLeft, 
346                                            3, 3, 2, 2)
347                         );
348     
349     runInput3->AddFrame(runCancel, 
350                          new TGLayoutHints(kLHintsTop | 
351                                            kLHintsLeft, 
352                                            3, 3, 2, 2)
353                         );
354     
355     // add to the main input frame
356
357     fRunInput->AddFrame(runInput3, 
358                         new TGLayoutHints(kLHintsTop | 
359                                           kLHintsExpandX, 
360                                           2, 2, 3, 0)
361                         );
362     
363     fRunInput->MapSubwindows();
364     fRunInput->Resize();
365     fRunInput->MapWindow();
366
367     //DoRunApply();   // temporary
368
369     break;
370     
371   case kMFILECNTRL:
372
373     // control main frame
374
375     fControl = new TGTransientFrame(gClient->GetRoot(), fMain, 50, 50);
376     fControl->Connect("CloseWindow()", "AliMUONTriggerGUI", this, "CloseControl()");
377     fControl->DontCallClose(); // to avoid double deletions.
378   
379     // use hierarchical cleaning
380     fControl->SetCleanup(kDeepCleanup);
381   
382     fControl->SetWindowName("Run controls");
383
384     // frame to hold buttons
385
386     control1 = new TGCompositeFrame(fControl, 50, 50, kVerticalFrame);
387   
388     // buttons
389
390     controlClose = new TGTextButton(control1, "Close", 1);
391     controlClose->Connect("Clicked()", "AliMUONTriggerGUI", this, "DoControlClose()");
392     //controlClose->Resize(300, controlClose->GetDefaultHeight());  
393
394     nextEvent = new TGTextButton(control1, "Next event", 2);
395     nextEvent->Connect("Clicked()", "AliMUONTriggerGUI", this, "DoNextEvent()");
396
397     previousEvent = new TGTextButton(control1, "Previous event", 3);
398     previousEvent->Connect("Clicked()", "AliMUONTriggerGUI", this, "DoPreviousEvent()");
399   
400     // frame to hold event skip
401
402     control2 = new TGCompositeFrame(fControl, 50, 50, kHorizontalFrame);
403   
404     // skip to event text field
405
406     fSkipToEventTxt = new TGTextEntry(control2, fTxtBuffer2 = new TGTextBuffer(5));
407
408     fTxtBuffer2->AddText(0,fEvString->Data());
409
410     // skip to event button
411
412     skipToEvent = new TGTextButton(control2, "Skip to event", 1);
413     skipToEvent->Connect("Clicked()", "AliMUONTriggerGUI", this, "DoSkipToEvent()");
414
415     // add to event skip frame
416
417     control2->AddFrame(fSkipToEventTxt, 
418                        new TGLayoutHints(kLHintsTop | 
419                                          kLHintsRight, 
420                                          10, 10, 5, 5)
421                        );
422   
423     control2->AddFrame(skipToEvent, 
424                        new TGLayoutHints(kLHintsTop | 
425                                          kLHintsRight, 
426                                          10, 10, 5, 5)
427                        );
428   
429     // add buttons
430   
431     control1->AddFrame(controlClose, 
432                  new TGLayoutHints(kLHintsBottom | 
433                                    kLHintsExpandX, 
434                                    10, 10, 5, 5)
435                  );
436   
437     control1->AddFrame(nextEvent, 
438                  new TGLayoutHints(kLHintsBottom | 
439                                    kLHintsExpandX, 
440                                    10, 10, 5, 5)
441                  );
442   
443     control1->AddFrame(previousEvent, 
444                  new TGLayoutHints(kLHintsBottom | 
445                                    kLHintsExpandX, 
446                                    10, 10, 5, 5)
447                  );
448   
449     // add to the main frame
450
451     fControl->AddFrame(control1,
452                        new TGLayoutHints(kLHintsBottom |
453                                          kLHintsLeft | 
454                                          kLHintsCenterY, 
455                                          3, 5, 0, 0)
456                        );
457     
458     fControl->AddFrame(control2,
459                        new TGLayoutHints(kLHintsTop | 
460                                          kLHintsCenterX, 
461                                          3, 5, 0, 0)
462                        );
463     
464     fControl->MapSubwindows();
465     fControl->Resize();
466     fControl->MapWindow();
467    
468     break;
469
470   case kMMAPDIGITS:
471     
472     if (fDiMap == 0) {
473       fDiMap = new AliMUONTriggerGUIdimap(fLoader,fBoards,gClient->GetRoot(), fMain, 400, 200);
474     } else if (!fDiMap->IsOn()) {
475       fDiMap = new AliMUONTriggerGUIdimap(fLoader,fBoards,gClient->GetRoot(), fMain, 400, 200);
476     }
477
478     break;
479     
480   case kMRESETDIGITS:
481     
482     Int_t number, over, pos;
483     for (Int_t ib = 0; ib < kNBoards; ib++) {
484       AliMUONTriggerGUIboard *board = GetBoard(ib);
485       board->ClearXDigits();
486       board->ClearYDigits();
487       // extended y-strips
488       number = board->GetNumber();
489       pos    = board->GetPosition();
490       over   = board->GetYOver();
491       for (Int_t io = 1; io <= over; io++) {
492         if (io == pos) continue;
493         board = GetBoard(number+io-pos);
494         board->ClearYDigits();
495       }
496     }
497
498     break;
499     
500   case kMCIRCUITOPEN:
501
502     fCircuit = new TGTransientFrame(gClient->GetRoot(), fMain, 50, 50);
503     fCircuit->Connect("CloseWindow()", "AliMUONTriggerGUI", this, "CloseCircuit()");
504     fCircuit->DontCallClose(); // to avoid double deletions.
505   
506     // use hierarchical cleaning
507     fCircuit->SetCleanup(kDeepCleanup);
508   
509     fCircuit->SetWindowName("Board circuit");
510
511     // sub-frames
512
513     circuit1 = new TGCompositeFrame(fCircuit, 400, 200, kHorizontalFrame);
514     circuit2 = new TGCompositeFrame(fCircuit, 400, 200, kHorizontalFrame);
515
516     // labels
517
518     circuitL1 = new TGLabel(circuit1, new TGString("Circuit number:"));
519     
520     // text entry
521     
522     circuitText1 = new TGTextEntry(circuit1, fTxtCircuit = new TGTextBuffer(10));
523     // buttons
524
525     circuitCancel = new TGTextButton(circuit2, "Cancel", 1);
526     circuitCancel->Connect("Clicked()", "AliMUONTriggerGUI", this, "DoCircuitCancel()");
527     //circuitCancel->Resize(100, circuitCancel->GetDefaultHeight());  
528
529     circuitOpen = new TGTextButton(circuit2, "Open", 2);
530     circuitOpen->Connect("Clicked()", "AliMUONTriggerGUI", this, "DoCircuitOpen()");
531     //circuitOpen->Resize(100, circuitOpen->GetDefaultHeight());  
532
533     // adding
534
535     circuit1->AddFrame(circuitL1,
536                        new TGLayoutHints(kLHintsLeft | 
537                                          kLHintsCenterY, 
538                                          5, 5, 2, 2)
539                        );
540     
541     circuit1->AddFrame(circuitText1, 
542                        new TGLayoutHints(kLHintsRight | 
543                                          kLHintsCenterY, 
544                                          0, 2, 2, 2)
545                        );
546     
547     circuit2->AddFrame(circuitCancel, 
548                  new TGLayoutHints(kLHintsBottom | 
549                                    kLHintsCenterY, 
550                                    5, 5, 2, 2)
551                  );
552   
553     circuit2->AddFrame(circuitOpen, 
554                  new TGLayoutHints(kLHintsBottom | 
555                                    kLHintsCenterY, 
556                                    5, 5, 2, 2)
557                  );
558   
559     fCircuit->AddFrame(circuit1, 
560                        new TGLayoutHints(kLHintsTop | kLHintsExpandX, 2, 2, 3, 0));
561     
562     fCircuit->AddFrame(circuit2, 
563                        new TGLayoutHints(kLHintsTop | kLHintsExpandX, 2, 2, 3, 0));
564     
565     fCircuit->MapSubwindows();
566     fCircuit->Resize();
567     fCircuit->MapWindow();
568
569     break;
570
571   case kMTRIGGERDSET:
572     /*
573     cout << "Trigger with boards digits....." << endl;
574     fTriggerProcessor->FeedBoardsGUI(Boards());
575
576     nLocalTrigger = fTriggerProcessor->TriggerGUI(trigInfo,kTRUE);
577
578     cout << "Trigger done with " << nLocalTrigger << " local decisions !" << endl;
579
580     for (Int_t ilo = 0; ilo < nLocalTrigger; ilo++) {
581
582       cout << "Local decision " << ilo << endl;
583       cout << "Circuit = "  << trigInfo[6*ilo+0] << endl;
584       cout << "LoStripX = " << trigInfo[6*ilo+1] << endl;
585       cout << "LoStripY = " << trigInfo[6*ilo+2] << endl;
586       cout << "LoDev = "    << trigInfo[6*ilo+3] << endl;
587       cout << "LoLpt = "    << trigInfo[6*ilo+4] << endl;
588       cout << "LoHpt = "    << trigInfo[6*ilo+5] << endl;
589       cout                                       << endl;
590     }
591     */
592     break;
593
594   default:
595     printf("Menu item %d selected\n", id);
596     break;
597     
598   }
599
600 }
601
602 //__________________________________________________________________________
603 void AliMUONTriggerGUI::CloseRunInput() const
604 {
605   /// close the run input frame
606
607   delete fRunInput;
608
609 }
610
611 //__________________________________________________________________________
612 void AliMUONTriggerGUI::CloseError() const
613 {
614   /// close the error frame
615
616   delete fError;
617
618 }
619
620 //__________________________________________________________________________
621 void AliMUONTriggerGUI::CloseControl() const
622 {
623   /// close the event control frame
624
625   delete fControl;
626
627   //gSystem->Exec("cd $PWD/mtrun; aliroot -b -q mtsim.C");
628
629 }
630
631 //__________________________________________________________________________
632 void AliMUONTriggerGUI::CloseCircuit() const
633 {
634   /// close the circuit frame
635
636   delete fCircuit;
637
638 }
639
640 //__________________________________________________________________________
641 void AliMUONTriggerGUI::CloseWindow() 
642 {
643   /// close the main frame and exit aplication
644
645   delete fRunLoader;
646   printf("\n"); 
647   gApplication->Terminate(); 
648
649 }
650
651 //__________________________________________________________________________
652 void AliMUONTriggerGUI::DoRunApply()
653 {
654   /// apply changes in the run control frame
655
656   printf("Input 1 = %s \n",fTxtBuffer1->GetString());
657
658   TString es = TString(fTxtBuffer2->GetString());
659   fEvent = es.Atoi();
660
661   printf("Input 2 = %s event = %d \n",fTxtBuffer2->GetString(),fEvent);
662
663   TString error = TString("");;
664
665   fPath->Form("%s",fTxtBuffer1->GetString());
666   fFileName->Form("%s",fTxtBuffer1->GetString());
667   printf("File location: %s \n",fFileName->Data());
668
669   if (gSystem->AccessPathName(fFileName->Data()) || !fFileName->EndsWith(".root")) {
670
671     error.Append("No galice file: ");
672     error.Append(fFileName->Data());
673     DoErrorGUI(error.Data());
674
675   } else {
676
677     TFile *ftest = new TFile(fFileName->Data(),"READ");
678     AliRun *galice = (AliRun*)ftest->Get("gAlice");
679     if (galice == 0) {
680
681       ftest->Close();
682       delete ftest;
683       
684       error.Append("No gAlice in file: ");
685       error.Append(fFileName->Data());
686       DoErrorGUI(error.Data());
687
688       return;
689
690     } else {
691       ftest->Close();
692       delete ftest;
693     }
694
695     if (fRunLoader) delete fRunLoader;
696
697     fRunLoader = AliRunLoader::Open(fFileName->Data(),"MUONFolder","READ");
698
699     if (fRunLoader == 0x0) {
700
701       DoErrorGUI("No run loader !");
702
703     } else {
704       
705       fRunLoader->LoadgAlice();
706       gAlice = fRunLoader->GetAliRun();
707       fEventsPerRun = gAlice->GetEventsPerRun();
708       
709       fLoader = fRunLoader->GetLoader("MUONLoader");
710       fLoader->LoadDigits("READ");
711       fRunLoader->GetEvent(fEvent);
712            
713       fRunInput->SendCloseMessage();
714       
715       if (!fBoardsInit) {
716         InitBoards();
717       }
718       
719       if (fDiMap) {
720         if (fDiMap->IsOn()) {
721           fDiMap->SetLoader(fLoader);
722         }
723       }
724       
725       //fTriggerProcessor = new AliMUONTriggerElectronics(fCalibrationData);
726     }
727
728   }
729
730 }
731
732 //__________________________________________________________________________
733 void AliMUONTriggerGUI::DoRunCancel()
734 {
735   /// cancel the changes in the run control frame
736
737   printf("Input 1 = %s \n",fTxtBuffer1->GetString());
738
739   TString es = TString(fTxtBuffer2->GetString());
740   fEvent = es.Atoi();
741
742   printf("Input 2 = %s event = %d \n",fTxtBuffer2->GetString(),fEvent);
743
744   fRunInput->SendCloseMessage();
745
746 }
747
748 //__________________________________________________________________________
749 void AliMUONTriggerGUI::DoErrorGUI(const Char_t *wt)
750 {
751   /// show an error message in a new frame
752
753   fError = new TGTransientFrame(gClient->GetRoot(), fMain, 50, 50);
754   fError->Connect("CloseWindow()", "AliMUONTriggerGUI", this, "CloseError()");
755   fError->DontCallClose(); // to avoid double deletions.
756   
757   // use hierarchical cleaning
758   fError->SetCleanup(kDeepCleanup);
759   
760   fError->SetWindowName("Error !");
761
762   TGCompositeFrame *fW = new TGCompositeFrame(fError, 50, 50, kVerticalFrame);
763   
764   TGTextButton *fErrorOK = new TGTextButton(fW, "&Ok", 1);
765   fErrorOK->Connect("Clicked()", "AliMUONTriggerGUI", this, "DoErrorOK()");
766   
767   fW->AddFrame(fErrorOK, 
768                new TGLayoutHints(kLHintsBottom | 
769                                  kLHintsCenterX, 
770                                  5, 5, 5, 5)
771                );
772   
773   TGLabel *fWL = new TGLabel(fW, new TGString(wt));
774   
775   fW->AddFrame(fWL,
776                new TGLayoutHints(kLHintsTop | 
777                                  kLHintsCenterX, 
778                                  5, 5, 5, 5)
779                );
780   
781   fError->AddFrame(fW,
782                    new TGLayoutHints(kLHintsLeft | 
783                                      kLHintsCenterY, 
784                                      5, 5, 5, 5)
785                    );
786   
787   fError->MapSubwindows();
788   fError->Resize();
789   fError->MapWindow();
790    
791 }
792
793 //__________________________________________________________________________
794 void AliMUONTriggerGUI::DoNextEvent()
795 {
796   /// load next event from the file
797
798   TString error = TString("");
799
800   if (fEvent < (fEventsPerRun-1)) {
801     fEvent++;
802     fRunLoader->GetEvent(fEvent);
803
804     fEvString->Form("%d",fEvent);               
805     fTxtBuffer2->RemoveText(0,5);
806     fTxtBuffer2->AddText(0,fEvString->Data());
807     fSkipToEventTxt->SetFocus();
808
809   } else {
810     error.Form("Only %d event(s) in the run !",fEventsPerRun);
811     DoErrorGUI(error.Data());
812   }
813
814 }
815
816 //__________________________________________________________________________
817 void AliMUONTriggerGUI::DoPreviousEvent()
818 {
819   /// load previous event from the input file
820
821   TString error = TString("");
822
823   if (fEvent > 0) {
824     fEvent--;
825     fRunLoader->GetEvent(fEvent);
826
827     fEvString->Form("%d",fEvent);               
828     fTxtBuffer2->RemoveText(0,5);
829     fTxtBuffer2->AddText(0,fEvString->Data());
830     fSkipToEventTxt->SetFocus();
831
832   } else {
833     error.Form("Already at event 0 !");
834     DoErrorGUI(error.Data());
835   }
836
837 }
838
839 //__________________________________________________________________________
840 void AliMUONTriggerGUI::DoSkipToEvent()
841 {
842   /// skip to event -input- from the input file
843
844   TString error = TString("");
845
846   TString es = TString(fTxtBuffer2->GetString());
847   fEvent = es.Atoi();
848
849   if (fEvent < 0 || fEvent > (fEventsPerRun-1)) {
850     error.Form("Event number out of range !");
851     DoErrorGUI(error.Data());
852   } else {
853     fRunLoader->GetEvent(fEvent);
854     /*
855     fRunLoader->LoadKinematics();
856
857     AliStack* stack = gAlice->Stack();
858     Int_t nParticles = stack->GetNtrack();
859     Int_t nPrimaries = stack->GetNprimary();
860     
861     TParticle *part;
862     Int_t nMuons = 0;
863     Int_t pdgCode;
864     Double_t px, py, pz, theta, phi;
865     for (Int_t i = 0; i < nPrimaries; i++) {
866       part = stack->Particle(i);
867       if (!part) continue;
868       if (TMath::Abs(part->GetPdgCode()) == 13) {
869         nMuons++;
870         pdgCode = part->GetPdgCode();
871         px = part->Px();
872         py = part->Py();
873         pz = part->Pz();
874         theta = part->Theta();
875         phi = part->Phi();
876         printf("Kine %d px %f py %f pz %f th %f ph %f \n",pdgCode,px,py,pz,theta,phi); 
877       }
878     }
879     */    
880   }
881
882 }
883
884 //__________________________________________________________________________
885 void AliMUONTriggerGUI::DoErrorOK()
886 {
887   /// close the error frame
888
889   fError->SendCloseMessage();
890
891 }
892
893 //__________________________________________________________________________
894 void AliMUONTriggerGUI::DoControlClose()
895 {
896   /// close the event control frame
897
898   fControl->SendCloseMessage();
899
900 }
901
902 //__________________________________________________________________________
903 void AliMUONTriggerGUI::DoCircuitCancel()
904 {
905   /// close the circuit frame
906
907   fCircuit->SendCloseMessage();
908
909 }
910
911 //__________________________________________________________________________
912 void AliMUONTriggerGUI::DoCircuitOpen()
913 {
914   /// open a circuit
915
916   TString cs = TString(fTxtCircuit->GetString());
917   Int_t icirc = cs.Atoi();
918
919   AliMUONTriggerGUIboard *board;
920
921   for (Int_t ib = 0; ib < kNBoards; ib++) {
922
923     board = GetBoard(ib);
924
925     if (board->GetIdCircuit() == icirc) {
926
927       OpenBoard(ib);
928
929       if (fDiMap) {
930         if (fDiMap->IsOn()) {
931           fDiMap->SelectBoard(ib);
932         }
933       }
934       
935       fCircuit->SendCloseMessage();
936       return;
937     }
938
939   }
940
941 }
942
943 //__________________________________________________________________________
944 AliMUONTriggerGUIboard* AliMUONTriggerGUI::GetBoard(Int_t id) const
945 {
946   /// return board with "id"
947
948   if (fBoards == 0) return 0;
949   void * b = fBoards->UncheckedAt(id);
950   if (b == 0) return 0;
951
952   return (AliMUONTriggerGUIboard*)b;
953
954 }
955
956 //__________________________________________________________________________
957 void AliMUONTriggerGUI::OpenBoard(Int_t id)
958 {
959   /// open board with "id" in a new frame
960    
961   AliMUONTriggerGUIboard *board = GetBoard(id);
962   UShort_t status = board->GetStatus();
963   board->SetOpen(kTRUE);
964
965   AliMUONTriggerGUIbdmap *bf;
966   Char_t text[200];
967
968   bf = new AliMUONTriggerGUIbdmap(gClient->GetRoot(), fMain, 400, 200);
969
970   if (status & kGood) {
971     sprintf(text,"%s (Circuit %4d) status : working",
972             board->GetBoardName(),board->GetIdCircuit());
973   }
974
975   if (status & kWithProblems) {
976     sprintf(text,"%s (Circuit %4d)  status : has problems...",
977             board->GetBoardName(),board->GetIdCircuit());
978   }
979
980   if (status & kNotWorking) {
981     sprintf(text,"%s (Circuit %4d)  status : not working",
982             board->GetBoardName(),board->GetIdCircuit());
983   }
984
985   if (status & kUnknown) {
986     sprintf(text,"%s (Circuit %4d)  status : unknown",
987             board->GetBoardName(),board->GetIdCircuit());
988   }
989
990   bf->SetName(text);
991   bf->SetBoard(Boards(),id);
992   bf->SetLoader(fLoader);
993   bf->Init();
994
995   bf->Show();
996
997   if (fDiMap) {
998     if (fDiMap->IsOn()) {
999       fDiMap->SelectBoard(id);
1000     }
1001   }
1002
1003 }
1004
1005 //__________________________________________________________________________
1006 void AliMUONTriggerGUI::Init()
1007 {
1008   /// initialize the main GUI frame
1009   
1010   if (!fBoardsInit) {
1011     InitBoards();
1012   }
1013
1014   /*
1015   AliMUONTriggerGUIboard *board;
1016   for (Int_t ib = 0; ib < kNBoards; ib++) {
1017     board = GetBoard(ib);
1018     board->Dump();
1019   }
1020   */
1021 }
1022
1023 //__________________________________________________________________________
1024 void AliMUONTriggerGUI::InitBoards()
1025 {
1026   /// create board objects and define the sensitive regions in the image map
1027   
1028   fBoardsInit = kTRUE;
1029
1030   AliMUONTriggerCrateStore* crateManager = new AliMUONTriggerCrateStore();
1031   crateManager->ReadFromFile();
1032
1033   Int_t nPixelX = 700;
1034   Int_t nPixelY = 676;
1035
1036   Int_t nPixelBorderX = 40;  // to guess...
1037   Int_t nPixelBorderY = 40;  // to guess...
1038
1039   Float_t boardsX = 2*257.00;  // cm
1040   Float_t boardsY = 2*306.61;  // cm
1041
1042   FILE *fmap;
1043
1044   Int_t side, col, line, nbx, detElemId = 0;
1045   UShort_t status = 1;
1046   Float_t xCenter, yCenter, zCenter, xWidth, yWidth;
1047   Float_t xc, yc;
1048   Char_t name[8], text[200];
1049   Int_t x, y;
1050   UInt_t w, h;
1051   Int_t xp[5];
1052   Int_t yp[5];
1053   TString mapspath = gSystem->Getenv("ALICE_ROOT");
1054   mapspath.Append("/MUON/data");
1055
1056   TGRegion *reg;
1057   AliMUONTriggerGUIboard *board;
1058
1059   // regions for the image map
1060
1061   sprintf(text,"%s/guimapp11.txt",mapspath.Data());
1062   fmap = fopen(text,"r");
1063
1064   for (Int_t ib = 0; ib < kNBoards; ib++) {
1065
1066     fscanf(fmap,"%d   %d   %d   %d   %f   %f   %f   %f   %f   %s   \n",&side,&col,&line,&nbx,&xCenter,&yCenter,&xWidth,&yWidth,&zCenter,&name[0]);
1067
1068     //printf("%d   %d   %d   %d   %f   %f   %f   %f   %f   %s   \n",side,col,line,nbx,xCenter,yCenter,xWidth,yWidth,zCenter,name);
1069
1070     board = new AliMUONTriggerGUIboard(ib,name);
1071
1072     status = 1;
1073     board->SetStatus(status);
1074
1075     // calculate detElemId%100
1076     // side=0 left
1077     // side=1 right
1078     // ALICE SC
1079     if (side == 0)              detElemId = 14 - line;
1080     if (side == 1 && line <  5) detElemId = 13 + line;
1081     if (side == 1 && line >= 5) detElemId = line - 5;
1082     
1083     board->SetDetElemId(detElemId);
1084
1085     Boards()->Add(board);
1086
1087     xc = xCenter;
1088     yc = yCenter;
1089
1090     x = (Int_t)(nPixelX/2 + xc * (nPixelX - 2*nPixelBorderX)/boardsX);
1091     y = (Int_t)(nPixelY/2 - yc * (nPixelY - 2*nPixelBorderY)/boardsY);
1092
1093     if (x < 0) x = 0;
1094     if (y < 0) y = 0;
1095
1096     w = (UInt_t)(xWidth*(nPixelX-2*nPixelBorderX)/boardsX);
1097     h = (UInt_t)(yWidth*(nPixelY-2*nPixelBorderY)/boardsY);
1098     
1099     xp[0] = x-w/2;
1100     xp[1] = x+w/2;
1101     xp[2] = x+w/2;
1102     xp[3] = x-w/2;
1103     xp[4] = x-w/2;
1104     
1105     yp[0] = y-h/2;
1106     yp[1] = y-h/2;
1107     yp[2] = y+h/2;
1108     yp[3] = y+h/2;
1109     yp[4] = y-h/2;
1110
1111     reg = new TGRegion(5,xp,yp);
1112     fImageMap->AddRegion(*reg, ib);
1113
1114     if (status & kGood) {
1115       sprintf(text,"%s working",name);
1116     }
1117     if (status & kWithProblems) {
1118       sprintf(text,"%s has problems...",name);
1119     }
1120     if (status & kNotWorking) {
1121       sprintf(text,"%s not working",name);
1122     }
1123     if (status & kUnknown) {
1124       sprintf(text,"%s status unknown",name);
1125     }
1126     
1127     //fImageMap->SetToolTipText(ib, text);
1128     
1129   }
1130
1131   fclose(fmap);
1132   
1133   // MT position and dimension in board
1134   
1135   for (Int_t imt = 0; imt < kNMT; imt++) {
1136
1137     sprintf(text,"%s/guimapp%2d.txt",mapspath.Data(),11+imt);
1138
1139     fmap = fopen(text,"r");
1140
1141     for (Int_t ib = 0; ib < kNBoards; ib++) {
1142
1143       fscanf(fmap,"%d   %d   %d   %d   %f   %f   %f   %f   %f   %s   \n",&side,&col,&line,&nbx,&xCenter,&yCenter,&xWidth,&yWidth,&zCenter,&name[0]);
1144
1145       board = GetBoard(ib);
1146       board->SetDimensions(imt,xCenter,yCenter,zCenter,xWidth,yWidth);
1147
1148     }
1149
1150     fclose(fmap);
1151
1152   }
1153
1154   // MT x-strips indices and circuit number
1155   Int_t ix, iy1, iy2, sIx, sIy1, cathode, icirc;
1156   sprintf(text,"%s/guimapix11.txt",mapspath.Data());
1157
1158   fmap = fopen(text,"r");
1159   
1160   for (Int_t ib = 0; ib < kNBoards; ib++) {
1161     
1162     fscanf(fmap,"%d   %d   %d   %d   %d   %d   %d   %s   \n",&side,&col,&line,&nbx,&ix,&iy1,&iy2,&name[0]);
1163     
1164     board = GetBoard(ib);
1165     board->SetXSindex(ix,iy1,iy2);
1166
1167     // set the circuit number
1168     detElemId = board->GetDetElemId();
1169     detElemId += 100 * 11;
1170     cathode = 0;
1171     sIx  = board->GetXSix();
1172     sIy1 = board->GetXSiy1();
1173     const AliMpVSegmentation* seg = 
1174       AliMpSegmentation::Instance()->GetMpSegmentation(detElemId,AliMp::GetCathodType(cathode));
1175     
1176     AliMpPad pad = seg->PadByIndices(AliMpIntPair(sIx,sIy1),kTRUE);
1177     AliMpIntPair location = pad.GetLocation(0);
1178
1179     Int_t nboard = location.GetFirst();
1180     AliMUONLocalTriggerBoard* b = crateManager->LocalBoard(nboard);
1181     icirc = b->GetNumber();
1182     board->SetBoardName((Char_t*)b->GetName());
1183     board->SetIdCircuit(icirc);
1184
1185     TString crateName = b->GetCrate();
1186     
1187     sprintf(text,"%s (crate %s circuit %3d, number %3d)",board->GetBoardName(),crateName.Data(),icirc,ib);
1188     fImageMap->SetToolTipText(ib, text);
1189         
1190   }
1191   
1192   fclose(fmap);
1193
1194   // MT y-strips indices
1195   Int_t ix1, ix2, iy;
1196
1197   sprintf(text,"%s/guimapiy11.txt",mapspath.Data());
1198   
1199   fmap = fopen(text,"r");
1200   
1201   for (Int_t ib = 0; ib < kNBoards; ib++) {
1202     
1203     fscanf(fmap,"%d   %d   %d   %d   %d   %d   %d   %s   \n",&side,&col,&line,&nbx,&ix1,&ix2,&iy,&name[0]);
1204     
1205     board = GetBoard(ib);
1206     board->SetYSindex(ix1,ix2,iy);
1207     
1208   }
1209   
1210   fclose(fmap);
1211
1212   // Extended y-strips over neighbouring boards
1213
1214   sprintf(text,"%s/guimapp11.txt",mapspath.Data());
1215   
1216   fmap = fopen(text,"r");
1217   
1218   for (Int_t ib = 0; ib < kNBoards; ib++) {
1219     
1220     fscanf(fmap,"%d   %d   %d   %d   %f   %f   %f   %f   %f   %s   \n",&side,&col,&line,&nbx,&xCenter,&yCenter,&xWidth,&yWidth,&zCenter,&name[0]);
1221     
1222     board = GetBoard(ib);
1223
1224     board->SetPosition(nbx);
1225     
1226     if ((col == 2 || col == 3) && (line >= 4 && line <= 6)) {
1227       board->SetYOver(4);
1228     } else
1229       
1230     if (col == 1 && (line == 4 || line == 6)) {
1231       board->SetYOver(3);
1232     } else
1233         
1234     if (col == 7 || line == 1 || line == 9) {
1235       board->SetYOver(1);
1236     } else
1237           
1238     {
1239       board->SetYOver(2);
1240     }
1241     
1242   }
1243   
1244   fclose(fmap);
1245
1246   // Set coordinates of strips boxes
1247
1248   for (Int_t ib = 0; ib < kNBoards; ib++) {
1249     
1250     board = GetBoard(ib);
1251     SetStripBoxes(board);
1252
1253   }
1254
1255 }
1256
1257 //__________________________________________________________________________
1258 void AliMUONTriggerGUI::SetStripBoxes(AliMUONTriggerGUIboard *board) 
1259 {
1260   /// set coordinates of strip boxes
1261
1262   gAlice = fRunLoader->GetAliRun();
1263   AliMUON *pMUON = (AliMUON*)gAlice->GetModule("MUON");
1264   const AliMUONGeometryTransformer* kGeomTransformer = pMUON->GetGeometryTransformer();
1265
1266   const AliMpVSegmentation* seg;
1267   AliMpPad pad;
1268   AliMpDEIterator it;
1269
1270   Int_t chamber, detElemId, maxX, maxY, ic;
1271   Float_t xpmin, xpmax, ypmin, ypmax;
1272   Float_t xg1, xg2, yg1, yg2, zg1;
1273   Float_t xlocal1, xlocal2, ylocal1, ylocal2;
1274   Float_t xCenter, yCenter, xWidth, yWidth;
1275
1276   for (Int_t i = 0; i < kNMT; i++) {
1277
1278     chamber = 11+i;
1279
1280     xCenter = board->GetXCenter(i);
1281     yCenter = board->GetYCenter(i);
1282     xWidth  = board->GetXWidth(i);
1283     yWidth  = board->GetYWidth(i);
1284
1285     for ( it.First(chamber-1); ! it.IsDone(); it.Next() ) {
1286     
1287       detElemId = it.CurrentDEId();
1288     
1289       if (detElemId%100 != board->GetDetElemId()) continue;
1290
1291       /*---------- y-pads ic = 2 ----------*/
1292       ic = 2;
1293       
1294       seg = AliMpSegmentation::Instance()->GetMpSegmentation(detElemId, AliMp::GetCathodType(ic-1));
1295
1296       maxX = seg->MaxPadIndexX();
1297       maxY = seg->MaxPadIndexY();
1298       
1299       for (Int_t ix = 0; ix <= maxX; ix++) {
1300         for (Int_t iy = 0; iy <= maxY; iy++) {
1301           
1302           pad = seg->PadByIndices(AliMpIntPair(ix,iy),kFALSE);
1303           
1304           if (!pad.IsValid()) continue;
1305           
1306           // get the pad position and dimensions
1307           xlocal1 = pad.Position().X();
1308           ylocal1 = pad.Position().Y();
1309           xlocal2 = pad.Dimensions().X();
1310           ylocal2 = pad.Dimensions().Y();
1311           
1312           kGeomTransformer->Local2Global(detElemId, xlocal1, ylocal1, 0, xg1, yg1, zg1);
1313           // (no transformation for pad dimensions)
1314           xg2 = xlocal2;
1315           yg2 = ylocal2;
1316           
1317           // transform in the monitor coordinate system
1318           //xpmin = -(xg1+xg2);
1319           //xpmax = -(xg1-xg2);
1320           //ypmin = -(yg2-yg1);
1321           //ypmax = +(yg2+yg1);
1322           // ALICE SC
1323           xpmin = +(xg1-xg2);
1324           xpmax = +(xg1+xg2);
1325           ypmin = -(yg2-yg1);
1326           ypmax = +(yg2+yg1);
1327           
1328           xpmin -= xCenter;
1329           xpmax -= xCenter;
1330           ypmin -= yCenter;
1331           ypmax -= yCenter;
1332           
1333           Int_t iX1, iX2, iY, ixDig;
1334           iX1 = board->GetYSix1();
1335           iX2 = board->GetYSix2();
1336           iY  = board->GetYSiy();
1337           if (ix >= iX1 && ix <= iX2 && iy == iY) {
1338             
1339             ypmin = -0.5*yWidth;
1340             ypmax = +0.5*yWidth;
1341
1342             ixDig = ix - iX1;
1343
1344             board->SetYDigBox(i,ixDig,(Double_t)xpmin,(Double_t)ypmin,
1345                                       (Double_t)xpmax,(Double_t)ypmax);
1346
1347           }
1348
1349         }  // end maxY
1350       }  // end maxX
1351
1352       /*---------- x-pads ic = 1 ----------*/
1353       ic = 1;
1354       
1355       seg = AliMpSegmentation::Instance()->GetMpSegmentation(detElemId, AliMp::GetCathodType(ic-1));
1356
1357       maxX = seg->MaxPadIndexX();
1358       maxY = seg->MaxPadIndexY();
1359             
1360       for (Int_t ix = 0; ix <= maxX; ix++) {
1361         for (Int_t iy = 0; iy <= maxY; iy++) {
1362           
1363           pad = seg->PadByIndices(AliMpIntPair(ix,iy),kFALSE);
1364           
1365           if (!pad.IsValid()) continue;
1366           
1367           // get the pad position and dimensions
1368           xlocal1 = pad.Position().X();
1369           ylocal1 = pad.Position().Y();
1370           xlocal2 = pad.Dimensions().X();
1371           ylocal2 = pad.Dimensions().Y();
1372           
1373           kGeomTransformer->Local2Global(detElemId, xlocal1, ylocal1, 0, xg1, yg1, zg1);
1374           // (no transformation for pad dimensions)
1375           xg2 = xlocal2;
1376           yg2 = ylocal2;
1377           
1378           // transform in the monitor coordinate system
1379           //xpmin = -(xg1+xg2);
1380           //xpmax = -(xg1-xg2);
1381           //ypmin = -(yg2-yg1);
1382           //ypmax = +(yg2+yg1);
1383           // ALICE SC
1384           xpmin = +(xg1+xg2);
1385           xpmax = +(xg1-xg2);
1386           ypmin = -(yg2-yg1);
1387           ypmax = +(yg2+yg1);
1388           
1389           xpmin -= xCenter;
1390           xpmax -= xCenter;
1391           ypmin -= yCenter;
1392           ypmax -= yCenter;
1393
1394           Int_t iX, iY1, iY2, iyDig;
1395           iX  = board->GetXSix();
1396           iY1 = board->GetXSiy1();
1397           iY2 = board->GetXSiy2();
1398           if (ix == iX && iy >= iY1 && iy <= iY2) {
1399
1400             iyDig = iy - iY1;
1401
1402             board->SetXDigBox(i,iyDig,(Double_t)xpmin,(Double_t)ypmin,
1403                                       (Double_t)xpmax,(Double_t)ypmax);
1404
1405           }
1406
1407         }  // end maxY
1408       }  // end maxX
1409
1410     }  // end detElemId
1411
1412   }  // end kNMT loop
1413         
1414 }
1415