source: fact/tools/Edd/Edd.cc@ 13259

Last change on this file since 13259 was 12940, checked in by ogrimm, 13 years ago
History handling in Edd faster, improved drag and drop functionality, version of Edd for La Palma
File size: 33.3 KB
Line 
1/* ============================================================
2
3Edd - Evidence Data Display
4
5Qt-based graphical user interface for the Evidence contron system
6
7EddLineDisplay changes its background colour in case it display
8a DIM status service
9
10Note: Currently no limit to number of points in plot, can result in out of memory condition.
11
12============================================================ */
13
14#include "Edd.h"
15
16QString DRSBoard = "FADctrl";
17std::string PixelMapText;
18
19extern class EddDim *Handler;
20
21////////////////////////
22// Event oscilloscope //
23////////////////////////
24
25// Constructor
26EventScope::EventScope(class TP_DAQ *Page, QWidget *P): EddBasePlot(P), PixelMap(PixelMapText, false) {
27
28 // Initalise
29 LastPath = ".";
30 Name = DRSBoard+"/EventData";
31 DAQPage = Page;
32 Active = false;
33
34 // Open temporary files
35 Tmpfile = tmpfile();
36 if(Tmpfile==NULL || !File.open()) {
37 QMessageBox::warning(this, "Edd Message", "Could not open temporary file.", QMessageBox::Ok);
38 return;
39 }
40
41 // Open file with RawDataCTX
42 RD = new RawDataCTX(true);
43 ErrCode = CTX_NOTOPEN;
44
45 // Context menu
46 PhysPipeAction = new QAction("Physical pipeline", this);
47 PhysPipeAction->setCheckable(true);
48 connect(PhysPipeAction, SIGNAL(triggered()), SLOT(PlotTraces()));
49 Menu->insertAction(StripAction, PhysPipeAction);
50
51 PersistanceAction = new QAction("Persistance", this);
52 PersistanceAction->setCheckable(true);
53 Menu->insertAction(StripAction, PersistanceAction);
54 Menu->removeAction(StripAction);
55
56 // Initial trace
57 AddTrace(0,0,0);
58
59 // Update interval
60 Timer->setInterval(100);
61}
62
63// Destructor (items with parent widget are automatically deleted)
64EventScope::~EventScope() {
65
66 SetActive(false);
67
68 for (int i=0; i<List.size(); i++) {
69 DeleteCurve(List[i].Curve);
70 delete List[i].Trigger;
71 }
72
73 delete RD;
74 if (Tmpfile != NULL) fclose(Tmpfile);
75}
76
77// Add trace
78void EventScope::AddTrace(int Board, int Chip, int Channel) {
79
80 struct ItemDetails N;
81
82 N.Curve = NewCurve(QString::number(Board)+","+QString::number(Chip)+","+ QString::number(Channel)+ " (" + ToPixel(0, Board, Chip, Channel) + ")");
83 N.Board = Board;
84 N.Chip = Chip;
85 N.Channel = Channel;
86 N.Trigger = new QwtPlotMarker();
87 N.Trigger->setSymbol(QwtSymbol(QwtSymbol::Diamond, QBrush(N.Curve->pen().color()), N.Curve->pen(), QSize(10,10)));
88 N.Trigger->attach(this);
89
90 if (List.isEmpty()) {
91 QPen Pen = N.Curve->pen();
92 Pen.setWidth(2);
93 N.Curve->setPen(Pen);
94 }
95 List.append(N);
96
97 PlotTraces();
98}
99
100// Update last trace (to reflect current setting of spin boxes in DAQ page)
101void EventScope::UpdateFirst(int Board, int Chip, int Channel) {
102
103 if (List.isEmpty()) return;
104
105 // Clear in case persistance was activated
106 ClearCurve(0);
107
108 List.first().Curve->setTitle(QString::number(Board)+","+QString::number(Chip)+","+ QString::number(Channel) + " (" + ToPixel(0, Board, Chip, Channel) + ")");
109 List.first().Board = Board;
110 List.first().Chip = Chip;
111 List.first().Channel = Channel;
112
113 PlotTraces();
114}
115
116// Update event buffer
117void EventScope::Update(const QString &, int Time, const QByteArray &Data, const QString &Format, const QString &, int) {
118
119 // Check if service available
120 if (!SetStatus(this, Name, Time, Format)) return;
121 if (Data.size() < (int) sizeof(RunHeader)) return;
122
123 // Ignore further data while processing this one
124 Handler->Ignore(Name, true);
125
126 // Clear temporary file and write event data to this file
127 File.resize(0);
128 if (File.write(Data) == -1) {
129 QMessageBox::warning(this, "Edd Message","Could not write data to temporary file.",QMessageBox::Ok);
130 return;
131 }
132 File.flush();
133
134 // Open temporary raw data file
135 OpenRawFile(File.fileName());
136
137 // Process all pending events, then allow data again
138 QApplication::processEvents();
139 Handler->Ignore(Name, false);
140}
141
142
143// New event number selected in raw data browser
144void EventScope::OpenRawFile(QString Filename) {
145
146 // Request filename to open if none given
147 if (Filename.isEmpty()) {
148 Filename = QFileDialog::getOpenFileName(this, "Open raw file", LastPath, "Raw data files (*.raw);; All files (*)");
149 if (Filename == NULL) return;
150 }
151
152 DAQPage->FilenameBox->setText(Filename);
153 LastPath = QFileInfo(Filename).absolutePath();
154
155 // Prepare temporary file for run header
156 if (ftruncate(fileno(Tmpfile), 0) == -1) printf("Error with ftruncate() in EventScope::OpenRawFile() (%s)\n", strerror(errno));
157 rewind(Tmpfile);
158
159 // Write run header to temporary file
160 switch (ErrCode = RD->OpenDataFile(Filename.toAscii().data(), Tmpfile)) {
161 case CTX_FOPEN: QMessageBox::warning(this, "Edd Message","Could not open file.",QMessageBox::Ok);
162 return;
163 case CTX_RHEADER: QMessageBox::warning(this, "Edd Message","Could not read run header.",QMessageBox::Ok);
164 return;
165 case CTX_BSTRUCT: QMessageBox::warning(this, "Edd Message","Could not read board structures.",QMessageBox::Ok);
166 return;
167 default: break;
168 }
169 RunHeader *R = RD->RHeader;
170
171 // Magic number warnings not for online display
172 if (Filename != File.fileName()) {
173 if (R->MagicNum == MAGICNUM_OPEN) {
174 QMessageBox::warning(this, "Edd Message","Magic number in run header indicates that the file has not been closed properly.",QMessageBox::Ok);
175 }
176 if (R->MagicNum == MAGICNUM_ERROR) {
177 QMessageBox::warning(this, "Edd Message","Magic number in run header indicates that an error occurred while writing the file.",QMessageBox::Ok);
178 }
179 }
180
181 // Print run header to display
182 rewind(Tmpfile);
183 QTextStream in(Tmpfile);
184 QString text = in.readAll();
185 DAQPage->RunHeaderDisplay->setPlainText(text);
186
187 // Read and display first event (must be before setting spin boxes)
188 NewEventNum(DAQPage->Event->value());
189
190 // Update spin box ranges on DAQ page
191 DAQPage->Event->setRange(0, R->Events-1);
192 DAQPage->Event->setEnabled(true);
193 DAQPage->Channel->setRange(0, R->NChannels-1);
194 DAQPage->Chip->setRange(0, R->NChips-1);
195 DAQPage->Board->setRange(0, R->NBoards-1);
196}
197
198// New event number selected in raw data browser
199void EventScope::NewEventNum(int Event) {
200
201 // Prepare temporary file for event header
202 if (ftruncate(fileno(Tmpfile), 0) == -1) printf("Error with ftruncate() in EventScope::NewEventNum() (%s)\n", strerror(errno));
203 rewind(Tmpfile);
204
205 // Read event
206 if (RD->ReadEvent(Event, Tmpfile) != CTX_OK) {
207 QMessageBox::warning(this, "Edd Message","Could not read event.",QMessageBox::Ok);
208 DAQPage->EventHeaderDisplay->clear();
209 return;
210 }
211
212 // Plot traces for event
213 PlotTraces();
214}
215
216// Update curves
217void EventScope::PlotTraces() {
218
219 double x,y;
220 unsigned int Cell, Trig;
221 static int Last = 0;
222
223 // Only process if valid data in RawDataCTX class
224 if (ErrCode != CTX_OK) return;
225
226 // Print event header and trigger cell information from event data
227 rewind(Tmpfile);
228 QTextStream in(Tmpfile);
229 QString text = in.readAll();
230
231 text.append("\nTrigger cells: ");
232 for (unsigned int i=0; i<RD->RHeader->NBoards*RD->RHeader->NChips; i++) {
233 QString a;
234 text.append(a.sprintf("%d ", *((int *)RD->Data + i)));
235 }
236 DAQPage->EventHeaderDisplay->setPlainText(text);
237
238 // Set x axis title
239 if (PhysPipeAction->isChecked()) setAxisTitle(QwtPlot::xBottom, "Time from start of pipeline (ns)");
240 else setAxisTitle(QwtPlot::xBottom, "Time from trigger minus one revolution (ns)");
241
242 // Loop through event data to update event scope
243 RunHeader *R = RD->RHeader;
244 for (int i=0; i<List.size(); i++) {
245
246 if (PersistanceAction->isChecked()) List[i].Curve->setStyle(QwtPlotCurve::Dots);
247 else {
248 ClearCurve(i);
249 List[i].Curve->setStyle(QwtPlotCurve::Lines);
250 }
251
252 // Check if current event contains requested trace
253 if (List[i].Board>=R->NBoards || List[i].Chip>=R->NChips || List[i].Channel>=R->NChannels) continue;
254
255 // Set trigger marker visibility
256 List[i].Trigger->setVisible(PhysPipeAction->isChecked());
257
258 // Determine trigger cell
259 Trig = *((int *) RD->Data + List[i].Board*R->NChips + List[i].Chip);
260
261 // Calulate point of curve
262 for (unsigned int j=0; j<R->Samples; j++) {
263
264 if (PhysPipeAction->isChecked()) Cell = (j - Trig) % 1024;
265 else Cell = j;
266
267 x = j / RD->BStruct[List[i].Board].NomFreq;
268 y = *((short *) (RD->Data + R->NBoards*R->NChips*sizeof(int)) +
269 List[i].Board*R->NChips*R->NChannels*R->Samples + List[i].Chip*R->NChannels*R->Samples +
270 List[i].Channel*R->Samples + Cell) * RD->BStruct[List[i].Board].ScaleFactor;
271
272 AddPoint(i, x, y);
273
274 // Set trigger point indicator
275 if (Trig == j) List[i].Trigger->setValue(x, y);
276 }
277 }
278
279 // Limit update rate in persistance mode
280 if (!PersistanceAction->isChecked() || time(NULL) > Last) {
281 UpdatePlot();
282 Last = time(NULL);
283 }
284
285 // Loop through event data for pixel display
286 QVector<double> Pixel(R->NBoards*R->NChips*R->NChannels);
287 int Count = 0;
288
289 for (unsigned int Board=0; Board<R->NBoards; Board++) {
290 for (unsigned int Chip=0; Chip<R->NChips; Chip++) {
291 for (unsigned int Channel=0; Channel<R->NChannels; Channel++) {
292 Pixel[Count] = DBL_MIN;
293
294 for (unsigned int i=0; i<R->Samples; i++) {
295 y = *((short *) (RD->Data + R->NBoards*R->NChips*sizeof(int)) +
296 Board*R->NChips*R->NChannels*R->Samples + Chip*R->NChannels*R->Samples +
297 Channel*R->Samples + i) * RD->BStruct[Board].ScaleFactor;
298
299 if (y > Pixel[Count]) Pixel[Count] = y;
300 }
301 Count++;
302 }}}
303
304 emit(PixelData(Pixel));
305}
306
307// Set display active (if inactive, disconnect from server)
308void EventScope::SetActive(bool State) {
309
310 if (State && !Active) Handler->Subscribe(DRSBoard+"/EventData", this, -1);
311 if (!State && Active) Handler->Unsubscribe(DRSBoard+"/EventData", this);
312
313 Active = State;
314}
315
316// Translate FPA ID to Pixel ID (use '-' instead of PM_ERROR_CODE)
317QString EventScope::ToPixel(unsigned int Crate, unsigned int Board, unsigned int Patch, unsigned int Pixel) {
318
319 if (FPA_to_Pixel(Crate, Board, Patch, Pixel) == PM_ERROR_CODE) return "-";
320 else return QString::number(FPA_to_Pixel(Crate, Board, Patch, Pixel));
321}
322
323//------------------------------------------------------------------
324//**************************** Tab pages ***************************
325//------------------------------------------------------------------
326
327//
328// Environment page (For M0, server was called ARDUINO)
329//
330TP_Environment::TP_Environment() {
331
332 QGridLayout *Layout = new QGridLayout(this);
333 setAttribute(Qt::WA_DeleteOnClose);
334
335 // Status display
336 EddLineDisplay *Line = new EddLineDisplay("FSCctrl/Message");
337 Line->setMaximumWidth(300);
338 Layout->addWidget(Line, 0, 0, 1, 4);
339
340 Line = new EddLineDisplay("FSCctrl/Time");
341 Layout->addWidget(Line, 0, 5, 1, 2);
342
343 // Temperatures
344 EddPlot *Plot = new EddPlot();
345 for (int i=0; i<64; i++) {
346 Line = new EddLineDisplay("FSCctrl/Temperature", i);
347 Line->setMaximumWidth(50);
348 Layout->addWidget(Line, i/8+1, i%8);
349 Plot->AddService("FSCctrl/Temperature", i);
350 }
351 Layout->addWidget(Plot, 0, 10, 22, 15);
352
353 // Voltages and currents
354 for (int i=0; i<40; i++) {
355 Line = new EddLineDisplay("FSCctrl/Voltage", i);
356 Line->setMaximumWidth(50);
357 Layout->addWidget(Line, i/8+10, i%8);
358
359 Line = new EddLineDisplay("FSCctrl/Current", i);
360 Line->setMaximumWidth(50);
361 Layout->addWidget(Line, i/8+16, i%8);
362 }
363
364 // Night sky monitor
365 Line = new EddLineDisplay("SQM/Message");
366 Line->setMaximumWidth(300);
367 Layout->addWidget(Line, 22, 0, 1, 4);
368
369 Line = new EddLineDisplay("SQM/NSB");
370 Layout->addWidget(Line, 22, 5, 1, 2);
371}
372
373//
374// Bias page
375//
376TP_Bias::TP_Bias() {
377
378 QGridLayout *Layout = new QGridLayout(this);
379 setAttribute(Qt::WA_DeleteOnClose);
380 EddLineDisplay *Line;
381
382 Line = new EddLineDisplay("Bias/Message");
383 Line->setMaximumWidth(200);
384 Layout->addWidget(Line, 0, 0, 1, 8);
385
386 EddCommand *Command = new EddCommand("Bias/Command");
387 Layout->addWidget(Command, 0, 9, 1, 7);
388
389 //EddText *Text = new EddText("Bias/ConsoleOut", true);
390 //Text->setFixedWidth(400);
391 //Layout->addWidget(Text, 1, 0, 3, 8);
392
393 EddPlot *Plot = new EddPlot();
394 //Plot->setMinimumWidth(400);
395 for (int i=0; i<320; i++) {
396 Line = new EddLineDisplay("Bias/VOLT/ID00", i);
397 Line->setMaximumWidth(25);
398 Layout->addWidget(Line, 6+i/16, i%16);
399 //Plot->AddService("Bias/VOLT/ID00", i+64);
400
401 //Line = new EddLineDisplay("Bias/VOLT/ID00", i+96);
402 //Layout->addWidget(Line, i%9+1, 2+i/9, 1, 1);
403 //Plot->AddService("Bias/VOLT/ID00",i+96);
404 }
405
406 //Layout->addWidget(Plot, 0, 4, 12, 3);
407
408 // Current page
409 EddWindow *Button = new EddWindow("Currents", "Edd - Edd - Bias currents");
410 Layout->addWidget(Button, 0, 16);
411
412 Plot = new EddPlot();
413
414 for (int i=0; i<36; i++) {
415 Line = new EddLineDisplay("Bias/MICROAMP/ID00", i+64);
416 Line->setMaximumWidth(60);
417 Button->Layout()->addWidget(Line, i%9, 0+i/9, 1, 1);
418 Plot->AddService("Bias/MICROAMP/ID00", i+64);
419 }
420 Button->Layout()->addWidget(Plot, 0, 4, 30, 12);
421}
422
423//
424// Feedback page
425//
426TP_Feedback::TP_Feedback() {
427
428 setAttribute(Qt::WA_DeleteOnClose);
429 QGridLayout *Layout = new QGridLayout(this);
430 EddLineDisplay *Line;
431
432 EddPlot *Plot = new EddPlot();
433 for (int i=0; i<36; i++) {
434 Line = new EddLineDisplay("Feedback/Average", i);
435 Line->setMaximumWidth(60);
436 Layout->addWidget(Line, i%9+2, 0+i/9, 1, 1);
437 Plot->AddService("Feedback/Average", i);
438 }
439 Layout->addWidget(Plot, 0, 4, 12, 10);
440
441 Line = new EddLineDisplay("Feedback/Message");
442 Line->setMaximumWidth(200);
443 Layout->addWidget(Line, 0, 0, 1, 2);
444
445 Line = new EddLineDisplay("Feedback/State");
446 Line->setMaximumWidth(150);
447 Layout->addWidget(Line, 1, 0, 1, 2);
448 Line = new EddLineDisplay("Feedback/Count");
449 Line->setMaximumWidth(60);
450 Layout->addWidget(Line, 1, 2);
451
452 // Details page
453 EddWindow *Button = new EddWindow("Details", "Edd - Feedback Details");
454 Layout->addWidget(Button, 12, 0, 1, 1);
455
456 Plot = new EddPlot();
457
458 for (int i=0; i<36; i++) {
459 Line = new EddLineDisplay("Feedback/Sigma", i);
460 Line->setMaximumWidth(60);
461 Button->Layout()->addWidget(Line, i%9, 0+i/9, 1, 1);
462 Plot->AddService("Feedback/Sigma", i);
463
464 Line = new EddLineDisplay("Feedback/Target", i);
465 Line->setMaximumWidth(60);
466 Button->Layout()->addWidget(Line, i%9+10, 0+i/9, 1, 1);
467
468 Line = new EddLineDisplay("Feedback/Response", i);
469 Line->setMaximumWidth(60);
470 Button->Layout()->addWidget(Line, i%9+20, 0+i/9, 1, 1);
471 }
472 Button->Layout()->addWidget(Plot, 0, 4, 30, 12);
473}
474
475
476//
477// FADctrl page
478//
479TP_FADctrl::TP_FADctrl() {
480
481 QString Board;
482
483 QScrollArea *scrollArea = new QScrollArea;
484 scrollArea->setBackgroundRole(QPalette::Dark);
485 scrollArea->setWidget(this);
486 setMinimumSize(QSize(0,0));
487
488 QGridLayout *Layout = new QGridLayout(this);
489 setAttribute(Qt::WA_DeleteOnClose);
490 EddLineDisplay *Line;
491
492 Line = new EddLineDisplay("FADctrl/Message");
493 Line->setMaximumWidth(200);
494 Layout->addWidget(Line, 0, 0, 1, 3);
495
496 EddCommand *Command = new EddCommand("FADctrl/Command");
497 Layout->addWidget(Command, 1, 0, 1, 3);
498
499 EddText *Text = new EddText("FADctrl/ConsoleOut", true);
500 Text->setFixedWidth(400);
501 Layout->addWidget(Text, 2, 0, 4, 4);
502
503 EddPlot *Plot = new EddPlot();
504 Layout->addWidget(Plot, 2, 4, 4, 4);
505
506 // Details page
507 EddWindow *Button[2];
508 Button[0] = new EddWindow("Boards 0-19", "Edd - FADctrl - Board 0 to 19");
509 Button[1] = new EddWindow("Boards 20-39", "Edd - FADctrl - Board 20 to 39");
510 Layout->addWidget(Button[0], 7, 0, 1, 1);
511 Layout->addWidget(Button[1], 7, 1, 1, 1);
512
513 for (int i=0; i<40; i++) {
514 Board = Board.sprintf("FADctrl/Board%.2d/", i);
515 Line = new EddLineDisplay(Board+"Server");
516 Line->setMinimumWidth(90);
517 Button[i/20]->Layout()->addWidget(Line, i+7, 0, 1, 1);
518
519 Line = new EddLineDisplay(Board+"BoardID");
520 Line->setMaximumWidth(30);
521 Button[i/20]->Layout()->addWidget(Line, i+7, 1, 1, 1);
522
523 Line = new EddLineDisplay(Board+"Frequency");
524 Line->setMaximumWidth(40);
525 Button[i/20]->Layout()->addWidget(Line, i+7, 2, 1, 1);
526
527 for (int j=0; j<4; j++) {
528 Line = new EddLineDisplay(Board+"Lock", j);
529 Line->setMaximumWidth(20);
530 Button[i/20]->Layout()->addWidget(Line, i+7, 3+j, 1, 1);
531 }
532
533 for (int j=0; j<4; j++) {
534 Plot->AddService(Board+"Temperature", j);
535
536 Line = new EddLineDisplay(Board+"Temperature", j);
537 Line->setMinimumWidth(40);
538 Button[i/20]->Layout()->addWidget(Line, i+7, 7+j, 1, 1);
539 }
540
541 Line = new EddLineDisplay(Board+"TriggerNum");
542 Line->setMinimumWidth(40);
543 Button[i/20]->Layout()->addWidget(Line, i+7, 11, 1, 1);
544
545 Line = new EddLineDisplay(Board+"RateHz");
546 Line->setMinimumWidth(50);
547 Button[i/20]->Layout()->addWidget(Line, i+7, 19, 1, 1);
548
549 Line = new EddLineDisplay(Board+"Status");
550 Line->setMaximumWidth(150);
551 Button[i/20]->Layout()->addWidget(Line, i+7, 20, 1, 1);
552 }
553}
554
555//
556// Event scope page
557//
558TP_DAQ::TP_DAQ(bool IsBrowser) {
559
560 EddLineDisplay *Line;
561
562 setAttribute(Qt::WA_DeleteOnClose);
563 QGridLayout *Layout = new QGridLayout(this);
564
565 // Event scope
566 Scope = new EventScope(this);
567 Scope->setMinimumWidth(700);
568
569 Scope->SetActive(!IsBrowser);
570
571 // FilenameBox must exist also for online browser (but not added to layout)
572 FilenameBox = new QLineEdit();
573
574 if (!IsBrowser) {
575 // Message service
576 Line = new EddLineDisplay(DRSBoard+"/Message");
577 Line->setMinimumWidth(500);
578 Layout->addWidget(Line, 0, 1, 1, 6);
579
580 // Run-related information
581 Line = new EddLineDisplay(DRSBoard+"/EventNumber");
582 Line->setMaximumWidth(100);
583 Layout->addWidget(Line, 0, 8, 1, 1);
584 Line = new EddLineDisplay(DRSBoard+"/FileSizeMB");
585 Line->setMaximumWidth(100);
586 Layout->addWidget(Line, 0, 9, 1, 1);
587 }
588 else {
589 // Filename box
590 Layout->addWidget(FilenameBox, 0, 1, 1, 6);
591 connect(FilenameBox, SIGNAL(returnPressed()), SLOT(OpenDataFile()));
592 FilenameBox->setToolTip("Raw data file name");
593
594 // Browse botton
595 QToolButton *LoadButton = new QToolButton();
596 LoadButton->setToolButtonStyle (Qt::ToolButtonTextOnly);
597 LoadButton->setText("...");
598 Layout->addWidget(LoadButton, 0, 7, 1, 1);
599 connect(LoadButton, SIGNAL(clicked()), Scope, SLOT(OpenRawFile()));
600 LoadButton->setToolTip("Open file dialog to select raw data file");
601 }
602
603 // Text boxes for run and event header
604 RunHeaderDisplay = new QPlainTextEdit();
605 EventHeaderDisplay = new QPlainTextEdit();
606 RunHeaderDisplay->setReadOnly(true);
607 EventHeaderDisplay->setReadOnly(true);
608
609 // Tab widget
610 QTabWidget *TabWidget = new QTabWidget();
611 TabWidget->addTab(Scope, "&Signals");
612 TabWidget->addTab(RunHeaderDisplay, "&Run Header");
613 TabWidget->addTab(EventHeaderDisplay, "&Event Header");
614 Layout->addWidget(TabWidget, 1, 1, 7, 12);
615
616 // Channel number
617 Channel = new QSpinBox;
618 connect(Channel, SIGNAL(valueChanged(int)), SLOT(UpdateScope(int)));
619 Channel->setToolTip("Channel number");
620
621 // Chip number
622 Chip = new QSpinBox;
623 connect(Chip, SIGNAL(valueChanged(int)), SLOT(UpdateScope(int)));
624 Chip->setToolTip("Chip number");
625
626 // Board number
627 Board = new QSpinBox;
628 connect(Board, SIGNAL(valueChanged(int)), SLOT(UpdateScope(int)));
629 Board->setToolTip("Board number");
630
631 // Pixel ID
632 PixelID = new QSpinBox;
633 PixelID->setMaximumWidth(60);
634 PixelID->setRange(-1, 9999);
635 PixelID->setSpecialValueText("n/a");
636 connect(PixelID, SIGNAL(valueChanged(int)), SLOT(TranslatePixelID(int)));
637 PixelID->setToolTip("Pixel identification");
638
639 // Add trace permanently
640 QPushButton *Button = new QPushButton("Keep trace");
641 Button->setToolTip("Keep trace in display");
642 Button->setMaximumWidth(80);
643 connect(Button, SIGNAL(clicked()), SLOT(KeepCurrent()));
644
645 // Layout of pixel addressing widgets
646 QFormLayout *FormLayout = new QFormLayout();
647 FormLayout->setRowWrapPolicy(QFormLayout::WrapAllRows);
648 FormLayout->addRow("Board", Board);
649 FormLayout->addRow("Chip", Chip);
650 FormLayout->addRow("Channel", Channel);
651 FormLayout->addRow("Pixel ID", PixelID);
652 FormLayout->addRow("", Button);
653 Layout->addLayout(FormLayout, 0, 0, 4, 1);
654
655 // Spin box for event number
656 Event = new QSpinBox;
657 Event->setToolTip("Event number");
658 Event->setEnabled (false);
659
660 // Stop/start button
661 StartStopButton = new QPushButton("Stop");
662 StartStopButton->setToolTip("Start/stop display");
663 StartStopButton->setMaximumWidth(80);
664 StartStopButton->setCheckable(true);
665 QPalette Palette = StartStopButton->palette();
666 Palette.setColor(QPalette::ButtonText, Qt::blue);
667 Palette.setColor(QPalette::Button, Qt::green);
668 StartStopButton->setPalette(Palette);
669 StartStopButton->setFont(QFont("Times", 10, QFont::Bold));
670
671 if (IsBrowser) {
672 FormLayout = new QFormLayout();
673 FormLayout->setRowWrapPolicy(QFormLayout::WrapAllRows);
674 FormLayout->addRow("Event Num", Event);
675 Layout->addLayout(FormLayout, 6, 0);
676 connect(Event, SIGNAL(valueChanged(int)), Scope, SLOT(NewEventNum(int)));
677 }
678 else {
679 Layout->addWidget(StartStopButton, 6, 0);
680 connect(StartStopButton, SIGNAL(toggled(bool)), SLOT(StartStop(bool)));
681 }
682
683 // Event display page
684 EddWindow *New = new EddWindow("Pixel display", "Edd - Event display");
685 New->setFont(QFont("Times", 10, QFont::Bold));
686 New->setMaximumWidth(80);
687 New->setToolTip("Show event display window");
688 Layout->addWidget(New, 7, 0);
689
690 Pixel = new QPushButton *[MAXPIXEL];
691 int Count = 0;
692 double x,y;
693
694 for (int ix=-22; ix<=22; ix++) for (int iy=-19; iy<=20; iy++) {
695 if (Count == MAXPIXEL) break;
696
697 x = ix*0.866;
698 y = iy - (ix%2==0 ? 0.5:0);
699 if ((pow(x,2)+pow(y,2) >= 395) && !(abs(ix)==22 && iy==7)) continue;
700
701 //Pixel[Count] = new QPushButton(Display);
702 Pixel[Count] = new QPushButton(New->Window());
703 Pixel[Count]->setAutoFillBackground(true);
704 Pixel[Count]->setGeometry((int) (x*12.5 + 250), (int) (y*12.5 + 250), 10, 10);
705 Pixel[Count]->show();
706 Count++;
707 }
708
709 // Connect slots for updating displays
710 if (!IsBrowser) StartStop(false);
711 connect(Scope, SIGNAL(PixelData(QVector<double>)), SLOT(SetPixelData(QVector<double>)));
712
713 // Call to get initial pixel ID correct
714 UpdateScope(0);
715}
716
717TP_DAQ::~TP_DAQ() {
718
719 delete[] Pixel;
720}
721
722// Translate pixel ID to board, chip, channel
723void TP_DAQ::TranslatePixelID(int ID) {
724
725 // setValue() below will call UpdateScope() through signal, therefore need to store numbers here
726 unsigned int BoardNo = Scope->Pixel_to_FPAboard(ID);
727 unsigned int PatchNo = Scope->Pixel_to_FPApatch(ID);
728 unsigned int PixelNo = Scope->Pixel_to_FPApixel(ID);
729
730 if (BoardNo == Scope->PM_ERROR_CODE) PixelID->setValue(-1);
731 else {
732 Board->setValue(BoardNo);
733 Chip->setValue(PatchNo);
734 Channel->setValue(PixelNo);
735 }
736}
737
738// Keep current trace
739void TP_DAQ::KeepCurrent() {
740
741 Scope->AddTrace(Board->value(), Chip->value(), Channel->value());
742}
743
744// Start/stop event acquisition
745void TP_DAQ::StartStop(bool State) {
746
747 Scope->SetActive(!State);
748 StartStopButton->setText(State ? "Start" : "Stop");
749}
750
751// Open raw data file
752void TP_DAQ::OpenDataFile() {
753
754 Scope->OpenRawFile(FilenameBox->text());
755}
756
757// Update event scope
758void TP_DAQ::UpdateScope(int) {
759
760 // Update pixel ID
761 PixelID->setValue(Scope->FPA_to_Pixel(0, Board->value(), Chip->value(), Channel->value()));
762 if (PixelID->value() == (int) Scope->PM_ERROR_CODE) PixelID->setValue(-1);
763
764 // Update first trace
765 Scope->UpdateFirst(Board->value(), Chip->value(), Channel->value());
766}
767
768// Show/hide pixel display
769void TP_DAQ::ShowPixelDisplay() {
770
771 Display->show();
772 Display->raise();
773}
774
775void TP_DAQ::SetPixelData(QVector<double> Data) {
776
777 QwtLinearColorMap Map;
778
779 for (int i=0; i<Data.size(); i++) {
780 Pixel[i]->setPalette(QPalette(Map.color(QwtDoubleInterval(300, 400), Data[i])));
781 }
782}
783
784
785//
786// Evidence page
787//
788TP_Evidence::TP_Evidence() {
789
790 setAttribute(Qt::WA_DeleteOnClose);
791 QGridLayout *Layout = new QGridLayout(this);
792 EddLineDisplay *Line;
793 EddText *Text;
794
795 Line = new EddLineDisplay("Alarm/Message");
796 Line->setMaximumWidth(200);
797 Layout->addWidget(Line, 0, 0, 1, 2);
798
799 QPushButton *Button = new QPushButton();
800 Button->setText("ON/OFF");
801 Button->setCheckable(true);
802 connect(Button, SIGNAL(toggled(bool)), SLOT(ToggleAlarm(bool)));
803 Layout->addWidget(Button, 0, 3, 1, 1);
804
805 Line = new EddLineDisplay("Alarm/MasterAlarm");
806 Layout->addWidget(Line, 0, 1, 1, 1);
807
808 Text = new EddText("Alarm/Summary", true);
809 Text->Accumulate = false;
810 Text->setMaximumWidth(200);
811 Text->setMaximumHeight(150);
812 Layout->addWidget(Text, 1, 0, 1, 2);
813
814 Line = new EddLineDisplay("DColl/Message");
815 Line->setMaximumWidth(200);
816 Layout->addWidget(Line, 3, 0, 1, 2);
817
818 Line = new EddLineDisplay("DColl/DataSizeMB");
819 Layout->addWidget(Line, 4, 0, 1, 1);
820
821 Line = new EddLineDisplay("DColl/LogSizeMB");
822 Layout->addWidget(Line, 4, 1, 1, 1);
823
824 Line = new EddLineDisplay("DColl/CurrentFile");
825 Line->setMaximumWidth(400);
826 Layout->addWidget(Line, 5, 0, 1, 3);
827
828 Line = new EddLineDisplay("Config/Message");
829 Line->setMaximumWidth(200);
830 Layout->addWidget(Line, 6, 0, 1, 2);
831
832 Line = new EddLineDisplay("Config/ModifyTime");
833 Line->setMaximumWidth(200);
834 Line->ShowAsTime = true;
835 Layout->addWidget(Line, 7, 0, 1, 1);
836
837 Button = new QPushButton();
838 Button->setText("eLogBook");
839 connect(Button, SIGNAL(released()), SLOT(StartELog()));
840 Layout->addWidget(Button, 6, 1, 1, 1);
841
842 Button = new QPushButton();
843 Button->setText("DIM browser");
844 connect(Button, SIGNAL(released()), SLOT(StartDIMBrowser()));
845 Layout->addWidget(Button, 7, 1, 1, 1);
846
847 Line = new EddLineDisplay("Edd/Rate_kBSec");
848 Layout->addWidget(Line, 8, 0, 1, 1);
849}
850
851// Toggle state of Alarm server
852void TP_Evidence::ToggleAlarm(bool State) {
853
854 if (State) DimClient::sendCommandNB((char *) "Alarm/Switch", (char *) "off");
855 else DimClient::sendCommandNB((char *) "Alarm/Switch", (char *) "on");
856}
857
858// Start DIM Browser
859void TP_Evidence::StartDIMBrowser() {
860
861 QProcess::startDetached("did", QStringList(), QString(getenv("DIMDIR"))+"/linux/");
862}
863
864// Start eLogBook
865void TP_Evidence::StartELog() {
866
867 QProcess::startDetached("firefox http://fact.ethz.ch/FACTelog/index.jsp");
868}
869
870
871//--------------------------------------------------------------------
872//*************************** Main GUI *******************************
873//--------------------------------------------------------------------
874//
875// All widgets have ultimately Central as parent.
876//
877GUI::GUI() {
878
879 Handler = new EddDim;
880
881 // Arrangement in tabs
882 TabWidget = new QTabWidget(this);
883 TabWidget->setTabsClosable(true);
884 connect(TabWidget, SIGNAL(tabCloseRequested(int)), SLOT(DetachTab(int)));
885 TabWidget->addTab(new TP_DAQ(false), "Event scope");
886 TabWidget->addTab(new TP_FADctrl, "FADctrl");
887 TabWidget->addTab(new TP_Bias, "Bias");
888 TabWidget->addTab(new TP_Feedback, "Feedback");
889 TabWidget->addTab(new TP_Environment, "FSC/SQM");
890 TabWidget->addTab(new TP_Evidence, "Evidence");
891
892 // Set features of main window
893 setStatusBar(new QStatusBar(this));
894 setWindowTitle("Edd - Evidence Data Display - Node: " + QString(DimServer::getDnsNode()) + ":" + QString::number(DimServer::getDnsPort()));
895 setCentralWidget(TabWidget);
896
897 // Menu bar
898 QMenu* Menu = menuBar()->addMenu("&Menu");
899 Menu->addAction("Open history plot", this, SLOT(MenuNewHistory()));
900 Menu->addAction("Send command", this, SLOT(MenuCommand()));
901 Menu->addAction("Raw data browser", this, SLOT(MenuRawDataBrowser()));
902 Menu->addSeparator();
903 Menu->addAction("About", this, SLOT(MenuAbout()));
904 Menu->addSeparator();
905 QAction* QuitAction = Menu->addAction("Quit", qApp, SLOT(quit()));
906 QuitAction->setShortcut(Qt::CTRL + Qt::Key_Q);
907
908 // Size and show main window
909 QSize Size = TabWidget->sizeHint()*1.1;
910 Size = Size.boundedTo(QApplication::desktop()->screenGeometry(this).size()*0.6);
911 //setMaximumSize(QApplication::desktop()->screenGeometry(this).size()*0.9);
912 TabWidget->resize(Size);
913 TabWidget->setMaximumSize(QApplication::desktop()->screenGeometry(this).size()*0.9);
914 show();
915
916 // Set timer to regularly check the master alarm
917 QTimer *Timer = new QTimer(this);
918 connect(Timer, SIGNAL(timeout()), this, SLOT(CheckAlarm()));
919 if (QString(DimServer::getDnsNode()).contains("ethz.ch")) Timer->start(5000);
920}
921
922
923void GUI::MenuAbout() {
924 QString Rev(SVN_REVISION);
925 Rev.remove(0,1).chop(2);
926
927 QMessageBox::about(this, "About Edd","Evidence Data Display\n\n"
928 "Written by Oliver Grimm, IPP, ETH Zurich\n"
929 "This version compiled "__DATE__" ("+Rev+")\n\n"
930 "Graphical user interface implemented with Qt and Qwt.\n"
931 "Evidence control system based on DIM (http://dim.web.cern.ch).\n\n"
932 "Comments to oliver.grimm@phys.ethz.ch.");
933}
934
935// Open new history plot
936void GUI::MenuNewHistory() {
937
938 QStringList List, Index;
939 char *Name, *Format;
940 int Type;
941 bool OK;
942
943 // Find all DIM services and sort
944 getServices("*");
945 while ((Type = getNextService(Name, Format)) != 0) {
946 if (Type==DimSERVICE) List.append(Name);
947 }
948 List.sort();
949
950 // Open dialog and open history window
951 QString Result = QInputDialog::getItem(this, "Edd Request",
952 "Enter or choose DIM service name (add index or index range a-b separated by colon)", List, 0, true, &OK);
953
954 // Check if cancelled or empty data
955 List = Result.trimmed().split(":", QString::SkipEmptyParts);
956 if (!OK || List.isEmpty()) return;
957
958 // Check if index was given
959 if (List.size() == 1) {
960 OpenHistory(List[0].toAscii().data(), -1);
961 return;
962 }
963
964 // Single index or index range?
965 Index = List[1].trimmed().split("-", QString::SkipEmptyParts);
966 if (Index.isEmpty()) {
967 QMessageBox::warning(NULL, "Edd Message", QString("Incorrect index range given") ,QMessageBox::Ok);
968 return;
969 }
970
971 if (Index.size() == 1) OpenHistory(List[0].toAscii().data(), atoi(Index[0].toAscii().data()));
972 else OpenHistory(List[0].toAscii().data(), atoi(Index[0].toAscii().data()), atoi(Index[1].toAscii().data()));
973}
974
975// Send command selected from list
976void GUI::MenuCommand() {
977
978 QStringList List;
979 char *Name, *Format;
980 int Type;
981 bool OK;
982
983 // Find all DIM commands and sort
984 getServices("*");
985 while ((Type = getNextService(Name, Format)) != 0) {
986 if (Type==DimCOMMAND) List.append(Name);
987 }
988 List.sort();
989
990 // Open dialog and open window for entering command
991 QString Result = QInputDialog::getItem(this, "Edd Request",
992 "Enter or choose DIM command name", List, 0, true, &OK);
993 if (OK && !Result.isEmpty()) {
994 Result = Result.trimmed();
995
996 QMainWindow *M = new QMainWindow;
997 M->setCentralWidget(new QWidget(M));
998 M->setStatusBar(new QStatusBar(M));
999 M->setAttribute(Qt::WA_DeleteOnClose);
1000 QWidget *W = new EddCommand(Result.toAscii().data());
1001 QFormLayout *FormLayout = new QFormLayout(M->centralWidget());
1002 FormLayout->setRowWrapPolicy(QFormLayout::WrapAllRows);
1003 FormLayout->addRow("Enter argument for " + Result, W);
1004 M->show();
1005 }
1006}
1007
1008// Raw data browser
1009void GUI::MenuRawDataBrowser() {
1010
1011 DetachTab(0, true);
1012}
1013
1014// Open tab as separate window
1015void GUI::DetachTab(int Tab, bool IsDAQBrowser) {
1016
1017 QWidget *W = NULL;
1018 QMainWindow *M = new QMainWindow;
1019
1020 M->setCentralWidget(new QWidget(M));
1021 M->setStatusBar(new QStatusBar(M));
1022
1023 switch(Tab) {
1024 case 0: W = new TP_DAQ(IsDAQBrowser); break;
1025 case 1: W = new TP_FADctrl; break;
1026 case 2: W = new TP_Bias; break;
1027 case 3: W = new TP_Feedback; break;
1028 case 4: W = new TP_Environment; break;
1029 case 5: W = new TP_Evidence; break;
1030 default: break;
1031 }
1032
1033 if (W == NULL) {
1034 delete M->centralWidget();
1035 delete M;
1036 return;
1037 }
1038
1039 W->setParent(M);
1040 W->resize(size());
1041 M->resize(size());
1042 if (!IsDAQBrowser) M->setWindowTitle("Edd - " + TabWidget->tabText(Tab));
1043 else M->setWindowTitle("Edd - Raw Data Browser");
1044 M->show();
1045
1046 return;
1047}
1048
1049// Check alarm level and if Alarm server is alive
1050void GUI::CheckAlarm() {
1051
1052 static int WarnedLevel = 0;
1053 static bool AlarmServerWarned = false;
1054
1055 // === Check service Alarm/MasterAlarm ===
1056 DimCurrentInfo MasterAlarm("Alarm/MasterAlarm", -1);
1057
1058 if (MasterAlarm.getInt() > WarnedLevel) {
1059 QSound::play(QApplication::applicationDirPath() + "/Error.wav");
1060
1061 // Construct warning message box
1062 QMessageBox Box;
1063 Box.setWindowTitle("Edd Alarm");
1064 Box.setText("Service 'Alarm/MasterAlarm' is at " + QString::number(MasterAlarm.getInt()));
1065 Box.setInformativeText("Warn again for the same alarm level?");
1066 Box.setIcon(QMessageBox::Warning);
1067 Box.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
1068 Box.setDefaultButton(QMessageBox::No);
1069
1070 // Check if user wants to reset warned level
1071 if (Box.exec() == QMessageBox::Yes) WarnedLevel = 0;
1072 else WarnedLevel = MasterAlarm.getInt();
1073 }
1074
1075 // If MasterAlam decreased, lower also warned level
1076 if (MasterAlarm.getInt() < WarnedLevel && MasterAlarm.getInt() >=0 ) WarnedLevel = MasterAlarm.getInt();
1077
1078 // === Check Alarm server ===
1079 DimCurrentInfo ServerList("DIS_DNS/SERVER_LIST", NO_LINK);
1080 std::string Result = EvidenceServer::ToString((char *) "C", ServerList.getData(), ServerList.getSize());
1081
1082 // Warn if SERVER_LIST does not contain alarm server
1083 if (Result.find("Alarm@") == std::string::npos && !AlarmServerWarned) {
1084 QMessageBox Box;
1085 Box.setWindowTitle("Edd Alarm");
1086 Box.setText("Alarm server is unavailable or in error");
1087 Box.setIcon(QMessageBox::Critical);
1088 Box.setStandardButtons(QMessageBox::Ok);
1089 Box.setDefaultButton(QMessageBox::Ok);
1090 Box.exec();
1091
1092 AlarmServerWarned = true;
1093 }
1094
1095 if (Result.find("Alarm@") != std::string::npos) AlarmServerWarned = false;
1096}
1097
1098// Quit application when clicking close button on window
1099void GUI::closeEvent(QCloseEvent *) {
1100
1101 qApp->quit();
1102}
1103
1104
1105//
1106//**************************** Main program ***************************
1107//
1108int main(int argc, char *argv[]) {
1109
1110 if (argc>1 && strcmp(argv[1],"drsdaq")==0) DRSBoard = "drsdaq";
1111
1112 // EvidenceServer::ToString() works for structures only without padding
1113 dic_disable_padding();
1114 dis_disable_padding();
1115
1116 // Make RPC to get pixelmap
1117 DimRpcInfo RPC((char *) "ConfigRequest", (char *) "");
1118 RPC.setData((char *) "Misc PixelMap");
1119 PixelMapText = std::string(RPC.getString(), RPC.getSize());
1120
1121 QApplication app(argc, argv);
1122 GUI MainWindow;
1123
1124 return app.exec();
1125}
Note: See TracBrowser for help on using the repository browser.