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

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