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

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