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

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