/* ============================================================ Edd - Evidence Data Display Qt-based graphical user interface for the Evidence contron system EddLineDisplay changes its background colour in case it display a DIM status service Note: Currently no limit to number of points in plot, can result in out of memory condition. ============================================================ */ #include "Edd.h" QString DRSBoard = "FADctrl"; std::string PixelMapText; extern class EddDim *Handler; //////////////////////// // Event oscilloscope // //////////////////////// // Constructor EventScope::EventScope(class TP_DAQ *Page, QWidget *P): EddBasePlot(P), PixelMap(PixelMapText, false) { // Initalise LastPath = "."; Name = DRSBoard+"/EventData"; DAQPage = Page; Active = false; // Open temporary files Tmpfile = tmpfile(); if(Tmpfile==NULL || !File.open()) { QMessageBox::warning(this, "Edd Message", "Could not open temporary file.", QMessageBox::Ok); return; } // Open file with RawDataCTX RD = new RawDataCTX(true); ErrCode = CTX_NOTOPEN; // Context menu PhysPipeAction = new QAction("Physical pipeline", this); PhysPipeAction->setCheckable(true); connect(PhysPipeAction, SIGNAL(triggered()), SLOT(PlotTraces())); Menu->insertAction(StripAction, PhysPipeAction); PersistanceAction = new QAction("Persistance", this); PersistanceAction->setCheckable(true); Menu->insertAction(StripAction, PersistanceAction); Menu->removeAction(StripAction); // Initial trace AddTrace(0,0,0); // Update interval Timer->setInterval(100); } // Destructor (items with parent widget are automatically deleted) EventScope::~EventScope() { SetActive(false); for (int i=0; isetSymbol(QwtSymbol(QwtSymbol::Diamond, QBrush(N.Curve->pen().color()), N.Curve->pen(), QSize(10,10))); N.Trigger->attach(this); if (List.isEmpty()) { QPen Pen = N.Curve->pen(); Pen.setWidth(2); N.Curve->setPen(Pen); } List.append(N); PlotTraces(); } // Update last trace (to reflect current setting of spin boxes in DAQ page) void EventScope::UpdateFirst(int Board, int Chip, int Channel) { if (List.isEmpty()) return; // Clear in case persistance was activated ClearCurve(0); List.first().Curve->setTitle(QString::number(Board)+","+QString::number(Chip)+","+ QString::number(Channel) + " (" + ToPixel(0, Board, Chip, Channel) + ")"); List.first().Board = Board; List.first().Chip = Chip; List.first().Channel = Channel; PlotTraces(); } // Update event buffer void EventScope::Update(const QString &, int Time, const QByteArray &Data, const QString &Format, const QString &, int) { // Check if service available if (!SetStatus(this, Name, Time, Format)) return; if (Data.size() < (int) sizeof(RunHeader)) return; // Ignore further data while processing this one Handler->Ignore(Name, true); // Clear temporary file and write event data to this file File.resize(0); if (File.write(Data) == -1) { QMessageBox::warning(this, "Edd Message","Could not write data to temporary file.",QMessageBox::Ok); return; } File.flush(); // Open temporary raw data file OpenRawFile(File.fileName()); // Process all pending events, then allow data again QApplication::processEvents(); Handler->Ignore(Name, false); } // New event number selected in raw data browser void EventScope::OpenRawFile(QString Filename) { // Request filename to open if none given if (Filename.isEmpty()) { Filename = QFileDialog::getOpenFileName(this, "Open raw file", LastPath, "Raw data files (*.raw);; All files (*)"); if (Filename == NULL) return; } DAQPage->FilenameBox->setText(Filename); LastPath = QFileInfo(Filename).absolutePath(); // Prepare temporary file for run header if (ftruncate(fileno(Tmpfile), 0) == -1) printf("Error with ftruncate() in EventScope::OpenRawFile() (%s)\n", strerror(errno)); rewind(Tmpfile); // Write run header to temporary file switch (ErrCode = RD->OpenDataFile(Filename.toAscii().data(), Tmpfile)) { case CTX_FOPEN: QMessageBox::warning(this, "Edd Message","Could not open file.",QMessageBox::Ok); return; case CTX_RHEADER: QMessageBox::warning(this, "Edd Message","Could not read run header.",QMessageBox::Ok); return; case CTX_BSTRUCT: QMessageBox::warning(this, "Edd Message","Could not read board structures.",QMessageBox::Ok); return; default: break; } RunHeader *R = RD->RHeader; // Magic number warnings not for online display if (Filename != File.fileName()) { if (R->MagicNum == MAGICNUM_OPEN) { QMessageBox::warning(this, "Edd Message","Magic number in run header indicates that the file has not been closed properly.",QMessageBox::Ok); } if (R->MagicNum == MAGICNUM_ERROR) { QMessageBox::warning(this, "Edd Message","Magic number in run header indicates that an error occurred while writing the file.",QMessageBox::Ok); } } // Print run header to display rewind(Tmpfile); QTextStream in(Tmpfile); QString text = in.readAll(); DAQPage->RunHeaderDisplay->setPlainText(text); // Read and display first event (must be before setting spin boxes) NewEventNum(DAQPage->Event->value()); // Update spin box ranges on DAQ page DAQPage->Event->setRange(0, R->Events-1); DAQPage->Event->setEnabled(true); DAQPage->Channel->setRange(0, R->NChannels-1); DAQPage->Chip->setRange(0, R->NChips-1); DAQPage->Board->setRange(0, R->NBoards-1); } // New event number selected in raw data browser void EventScope::NewEventNum(int Event) { // Prepare temporary file for event header if (ftruncate(fileno(Tmpfile), 0) == -1) printf("Error with ftruncate() in EventScope::NewEventNum() (%s)\n", strerror(errno)); rewind(Tmpfile); // Read event if (RD->ReadEvent(Event, Tmpfile) != CTX_OK) { QMessageBox::warning(this, "Edd Message","Could not read event.",QMessageBox::Ok); DAQPage->EventHeaderDisplay->clear(); return; } // Plot traces for event PlotTraces(); } // Update curves void EventScope::PlotTraces() { double x,y; unsigned int Cell, Trig; static int Last = 0; // Only process if valid data in RawDataCTX class if (ErrCode != CTX_OK) return; // Print event header and trigger cell information from event data rewind(Tmpfile); QTextStream in(Tmpfile); QString text = in.readAll(); text.append("\nTrigger cells: "); for (unsigned int i=0; iRHeader->NBoards*RD->RHeader->NChips; i++) { QString a; text.append(a.sprintf("%d ", *((int *)RD->Data + i))); } DAQPage->EventHeaderDisplay->setPlainText(text); // Set x axis title if (PhysPipeAction->isChecked()) setAxisTitle(QwtPlot::xBottom, "Time from start of pipeline (ns)"); else setAxisTitle(QwtPlot::xBottom, "Time from trigger minus one revolution (ns)"); // Loop through event data to update event scope RunHeader *R = RD->RHeader; for (int i=0; iisChecked()) List[i].Curve->setStyle(QwtPlotCurve::Dots); else { ClearCurve(i); List[i].Curve->setStyle(QwtPlotCurve::Lines); } // Check if current event contains requested trace if (List[i].Board>=R->NBoards || List[i].Chip>=R->NChips || List[i].Channel>=R->NChannels) continue; // Set trigger marker visibility List[i].Trigger->setVisible(PhysPipeAction->isChecked()); // Determine trigger cell Trig = *((int *) RD->Data + List[i].Board*R->NChips + List[i].Chip); // Calulate point of curve for (unsigned int j=0; jSamples; j++) { if (PhysPipeAction->isChecked()) Cell = (j - Trig) % 1024; else Cell = j; x = j / RD->BStruct[List[i].Board].NomFreq; y = *((short *) (RD->Data + R->NBoards*R->NChips*sizeof(int)) + List[i].Board*R->NChips*R->NChannels*R->Samples + List[i].Chip*R->NChannels*R->Samples + List[i].Channel*R->Samples + Cell) * RD->BStruct[List[i].Board].ScaleFactor; AddPoint(i, x, y); // Set trigger point indicator if (Trig == j) List[i].Trigger->setValue(x, y); } } // Limit update rate in persistance mode if (!PersistanceAction->isChecked() || time(NULL) > Last) { UpdatePlot(); Last = time(NULL); } // Loop through event data for pixel display QVector Pixel(R->NBoards*R->NChips*R->NChannels); int Count = 0; for (unsigned int Board=0; BoardNBoards; Board++) { for (unsigned int Chip=0; ChipNChips; Chip++) { for (unsigned int Channel=0; ChannelNChannels; Channel++) { Pixel[Count] = DBL_MIN; for (unsigned int i=0; iSamples; i++) { y = *((short *) (RD->Data + R->NBoards*R->NChips*sizeof(int)) + Board*R->NChips*R->NChannels*R->Samples + Chip*R->NChannels*R->Samples + Channel*R->Samples + i) * RD->BStruct[Board].ScaleFactor; if (y > Pixel[Count]) Pixel[Count] = y; } Count++; }}} emit(PixelData(Pixel)); } // Set display active (if inactive, disconnect from server) void EventScope::SetActive(bool State) { if (State && !Active) Handler->Subscribe(DRSBoard+"/EventData", this, -1); if (!State && Active) Handler->Unsubscribe(DRSBoard+"/EventData", this); Active = State; } // Translate FPA ID to Pixel ID (use '-' instead of PM_ERROR_CODE) QString EventScope::ToPixel(unsigned int Crate, unsigned int Board, unsigned int Patch, unsigned int Pixel) { if (FPA_to_Pixel(Crate, Board, Patch, Pixel) == PM_ERROR_CODE) return "-"; else return QString::number(FPA_to_Pixel(Crate, Board, Patch, Pixel)); } //------------------------------------------------------------------ //**************************** Tab pages *************************** //------------------------------------------------------------------ // // Environment page (For M0, server was called ARDUINO) // TP_Environment::TP_Environment() { QGridLayout *Layout = new QGridLayout(this); setAttribute(Qt::WA_DeleteOnClose); // Status display EddLineDisplay *Line = new EddLineDisplay("FSCctrl/Message"); Line->setMaximumWidth(300); Layout->addWidget(Line, 0, 0, 1, 4); Line = new EddLineDisplay("FSCctrl/Time"); Layout->addWidget(Line, 0, 5, 1, 2); // Temperatures EddPlot *Plot = new EddPlot(); for (int i=0; i<64; i++) { Line = new EddLineDisplay("FSCctrl/Temperature", i); Line->setMaximumWidth(50); Layout->addWidget(Line, i/8+1, i%8); Plot->AddService("FSCctrl/Temperature", i); } Layout->addWidget(Plot, 0, 10, 22, 15); // Voltages and currents for (int i=0; i<40; i++) { Line = new EddLineDisplay("FSCctrl/Voltage", i); Line->setMaximumWidth(50); Layout->addWidget(Line, i/8+10, i%8); Line = new EddLineDisplay("FSCctrl/Current", i); Line->setMaximumWidth(50); Layout->addWidget(Line, i/8+16, i%8); } // Night sky monitor Line = new EddLineDisplay("SQM/Message"); Line->setMaximumWidth(300); Layout->addWidget(Line, 22, 0, 1, 4); Line = new EddLineDisplay("SQM/NSB"); Layout->addWidget(Line, 22, 5, 1, 2); } // // Bias page // TP_Bias::TP_Bias() { QGridLayout *Layout = new QGridLayout(this); setAttribute(Qt::WA_DeleteOnClose); EddLineDisplay *Line; Line = new EddLineDisplay("Bias/Message"); Line->setMaximumWidth(200); Layout->addWidget(Line, 0, 0, 1, 8); EddCommand *Command = new EddCommand("Bias/Command"); Layout->addWidget(Command, 0, 9, 1, 7); //EddText *Text = new EddText("Bias/ConsoleOut", true); //Text->setFixedWidth(400); //Layout->addWidget(Text, 1, 0, 3, 8); EddPlot *Plot = new EddPlot(); //Plot->setMinimumWidth(400); for (int i=0; i<320; i++) { Line = new EddLineDisplay("Bias/VOLT/ID00", i); Line->setMaximumWidth(25); Layout->addWidget(Line, 6+i/16, i%16); //Plot->AddService("Bias/VOLT/ID00", i+64); //Line = new EddLineDisplay("Bias/VOLT/ID00", i+96); //Layout->addWidget(Line, i%9+1, 2+i/9, 1, 1); //Plot->AddService("Bias/VOLT/ID00",i+96); } //Layout->addWidget(Plot, 0, 4, 12, 3); // Current page EddWindow *Button = new EddWindow("Currents", "Edd - Edd - Bias currents"); Layout->addWidget(Button, 0, 16); Plot = new EddPlot(); for (int i=0; i<36; i++) { Line = new EddLineDisplay("Bias/MICROAMP/ID00", i+64); Line->setMaximumWidth(60); Button->Layout()->addWidget(Line, i%9, 0+i/9, 1, 1); Plot->AddService("Bias/MICROAMP/ID00", i+64); } Button->Layout()->addWidget(Plot, 0, 4, 30, 12); } // // Feedback page // TP_Feedback::TP_Feedback() { setAttribute(Qt::WA_DeleteOnClose); QGridLayout *Layout = new QGridLayout(this); EddLineDisplay *Line; EddPlot *Plot = new EddPlot(); for (int i=0; i<36; i++) { Line = new EddLineDisplay("Feedback/Average", i); Line->setMaximumWidth(60); Layout->addWidget(Line, i%9+2, 0+i/9, 1, 1); Plot->AddService("Feedback/Average", i); } Layout->addWidget(Plot, 0, 4, 12, 10); Line = new EddLineDisplay("Feedback/Message"); Line->setMaximumWidth(200); Layout->addWidget(Line, 0, 0, 1, 2); Line = new EddLineDisplay("Feedback/State"); Line->setMaximumWidth(150); Layout->addWidget(Line, 1, 0, 1, 2); Line = new EddLineDisplay("Feedback/Count"); Line->setMaximumWidth(60); Layout->addWidget(Line, 1, 2); // Details page EddWindow *Button = new EddWindow("Details", "Edd - Feedback Details"); Layout->addWidget(Button, 12, 0, 1, 1); Plot = new EddPlot(); for (int i=0; i<36; i++) { Line = new EddLineDisplay("Feedback/Sigma", i); Line->setMaximumWidth(60); Button->Layout()->addWidget(Line, i%9, 0+i/9, 1, 1); Plot->AddService("Feedback/Sigma", i); Line = new EddLineDisplay("Feedback/Target", i); Line->setMaximumWidth(60); Button->Layout()->addWidget(Line, i%9+10, 0+i/9, 1, 1); Line = new EddLineDisplay("Feedback/Response", i); Line->setMaximumWidth(60); Button->Layout()->addWidget(Line, i%9+20, 0+i/9, 1, 1); } Button->Layout()->addWidget(Plot, 0, 4, 30, 12); } // // FADctrl page // TP_FADctrl::TP_FADctrl() { QString Board; QScrollArea *scrollArea = new QScrollArea; scrollArea->setBackgroundRole(QPalette::Dark); scrollArea->setWidget(this); setMinimumSize(QSize(0,0)); QGridLayout *Layout = new QGridLayout(this); setAttribute(Qt::WA_DeleteOnClose); EddLineDisplay *Line; Line = new EddLineDisplay("FADctrl/Message"); Line->setMaximumWidth(200); Layout->addWidget(Line, 0, 0, 1, 3); EddCommand *Command = new EddCommand("FADctrl/Command"); Layout->addWidget(Command, 1, 0, 1, 3); EddText *Text = new EddText("FADctrl/ConsoleOut", true); Text->setFixedWidth(400); Layout->addWidget(Text, 2, 0, 4, 4); EddPlot *Plot = new EddPlot(); Layout->addWidget(Plot, 2, 4, 4, 4); // Details page EddWindow *Button[2]; Button[0] = new EddWindow("Boards 0-19", "Edd - FADctrl - Board 0 to 19"); Button[1] = new EddWindow("Boards 20-39", "Edd - FADctrl - Board 20 to 39"); Layout->addWidget(Button[0], 7, 0, 1, 1); Layout->addWidget(Button[1], 7, 1, 1, 1); for (int i=0; i<40; i++) { Board = Board.sprintf("FADctrl/Board%.2d/", i); Line = new EddLineDisplay(Board+"Server"); Line->setMinimumWidth(90); Button[i/20]->Layout()->addWidget(Line, i+7, 0, 1, 1); Line = new EddLineDisplay(Board+"BoardID"); Line->setMaximumWidth(30); Button[i/20]->Layout()->addWidget(Line, i+7, 1, 1, 1); Line = new EddLineDisplay(Board+"Frequency"); Line->setMaximumWidth(40); Button[i/20]->Layout()->addWidget(Line, i+7, 2, 1, 1); for (int j=0; j<4; j++) { Line = new EddLineDisplay(Board+"Lock", j); Line->setMaximumWidth(20); Button[i/20]->Layout()->addWidget(Line, i+7, 3+j, 1, 1); } for (int j=0; j<4; j++) { Plot->AddService(Board+"Temperature", j); Line = new EddLineDisplay(Board+"Temperature", j); Line->setMinimumWidth(40); Button[i/20]->Layout()->addWidget(Line, i+7, 7+j, 1, 1); } Line = new EddLineDisplay(Board+"TriggerNum"); Line->setMinimumWidth(40); Button[i/20]->Layout()->addWidget(Line, i+7, 11, 1, 1); Line = new EddLineDisplay(Board+"RateHz"); Line->setMinimumWidth(50); Button[i/20]->Layout()->addWidget(Line, i+7, 19, 1, 1); Line = new EddLineDisplay(Board+"Status"); Line->setMaximumWidth(150); Button[i/20]->Layout()->addWidget(Line, i+7, 20, 1, 1); } } // // Event scope page // TP_DAQ::TP_DAQ(bool IsBrowser) { EddLineDisplay *Line; setAttribute(Qt::WA_DeleteOnClose); QGridLayout *Layout = new QGridLayout(this); // Event scope Scope = new EventScope(this); Scope->setMinimumWidth(700); Scope->SetActive(!IsBrowser); // FilenameBox must exist also for online browser (but not added to layout) FilenameBox = new QLineEdit(); if (!IsBrowser) { // Message service Line = new EddLineDisplay(DRSBoard+"/Message"); Line->setMinimumWidth(500); Layout->addWidget(Line, 0, 1, 1, 6); // Run-related information Line = new EddLineDisplay(DRSBoard+"/EventNumber"); Line->setMaximumWidth(100); Layout->addWidget(Line, 0, 8, 1, 1); Line = new EddLineDisplay(DRSBoard+"/FileSizeMB"); Line->setMaximumWidth(100); Layout->addWidget(Line, 0, 9, 1, 1); } else { // Filename box Layout->addWidget(FilenameBox, 0, 1, 1, 6); connect(FilenameBox, SIGNAL(returnPressed()), SLOT(OpenDataFile())); FilenameBox->setToolTip("Raw data file name"); // Browse botton QToolButton *LoadButton = new QToolButton(); LoadButton->setToolButtonStyle (Qt::ToolButtonTextOnly); LoadButton->setText("..."); Layout->addWidget(LoadButton, 0, 7, 1, 1); connect(LoadButton, SIGNAL(clicked()), Scope, SLOT(OpenRawFile())); LoadButton->setToolTip("Open file dialog to select raw data file"); } // Text boxes for run and event header RunHeaderDisplay = new QPlainTextEdit(); EventHeaderDisplay = new QPlainTextEdit(); RunHeaderDisplay->setReadOnly(true); EventHeaderDisplay->setReadOnly(true); // Tab widget QTabWidget *TabWidget = new QTabWidget(); TabWidget->addTab(Scope, "&Signals"); TabWidget->addTab(RunHeaderDisplay, "&Run Header"); TabWidget->addTab(EventHeaderDisplay, "&Event Header"); Layout->addWidget(TabWidget, 1, 1, 7, 12); // Channel number Channel = new QSpinBox; connect(Channel, SIGNAL(valueChanged(int)), SLOT(UpdateScope(int))); Channel->setToolTip("Channel number"); // Chip number Chip = new QSpinBox; connect(Chip, SIGNAL(valueChanged(int)), SLOT(UpdateScope(int))); Chip->setToolTip("Chip number"); // Board number Board = new QSpinBox; connect(Board, SIGNAL(valueChanged(int)), SLOT(UpdateScope(int))); Board->setToolTip("Board number"); // Pixel ID PixelID = new QSpinBox; PixelID->setMaximumWidth(60); PixelID->setRange(-1, 9999); PixelID->setSpecialValueText("n/a"); connect(PixelID, SIGNAL(valueChanged(int)), SLOT(TranslatePixelID(int))); PixelID->setToolTip("Pixel identification"); // Add trace permanently QPushButton *Button = new QPushButton("Keep trace"); Button->setToolTip("Keep trace in display"); Button->setMaximumWidth(80); connect(Button, SIGNAL(clicked()), SLOT(KeepCurrent())); // Layout of pixel addressing widgets QFormLayout *FormLayout = new QFormLayout(); FormLayout->setRowWrapPolicy(QFormLayout::WrapAllRows); FormLayout->addRow("Board", Board); FormLayout->addRow("Chip", Chip); FormLayout->addRow("Channel", Channel); FormLayout->addRow("Pixel ID", PixelID); FormLayout->addRow("", Button); Layout->addLayout(FormLayout, 0, 0, 4, 1); // Spin box for event number Event = new QSpinBox; Event->setToolTip("Event number"); Event->setEnabled (false); // Stop/start button StartStopButton = new QPushButton("Stop"); StartStopButton->setToolTip("Start/stop display"); StartStopButton->setMaximumWidth(80); StartStopButton->setCheckable(true); QPalette Palette = StartStopButton->palette(); Palette.setColor(QPalette::ButtonText, Qt::blue); Palette.setColor(QPalette::Button, Qt::green); StartStopButton->setPalette(Palette); StartStopButton->setFont(QFont("Times", 10, QFont::Bold)); if (IsBrowser) { FormLayout = new QFormLayout(); FormLayout->setRowWrapPolicy(QFormLayout::WrapAllRows); FormLayout->addRow("Event Num", Event); Layout->addLayout(FormLayout, 6, 0); connect(Event, SIGNAL(valueChanged(int)), Scope, SLOT(NewEventNum(int))); } else { Layout->addWidget(StartStopButton, 6, 0); connect(StartStopButton, SIGNAL(toggled(bool)), SLOT(StartStop(bool))); } // Event display page EddWindow *New = new EddWindow("Pixel display", "Edd - Event display"); New->setFont(QFont("Times", 10, QFont::Bold)); New->setMaximumWidth(80); New->setToolTip("Show event display window"); Layout->addWidget(New, 7, 0); Pixel = new QPushButton *[MAXPIXEL]; int Count = 0; double x,y; for (int ix=-22; ix<=22; ix++) for (int iy=-19; iy<=20; iy++) { if (Count == MAXPIXEL) break; x = ix*0.866; y = iy - (ix%2==0 ? 0.5:0); if ((pow(x,2)+pow(y,2) >= 395) && !(abs(ix)==22 && iy==7)) continue; //Pixel[Count] = new QPushButton(Display); Pixel[Count] = new QPushButton(New->Window()); Pixel[Count]->setAutoFillBackground(true); Pixel[Count]->setGeometry((int) (x*12.5 + 250), (int) (y*12.5 + 250), 10, 10); Pixel[Count]->show(); Count++; } // Connect slots for updating displays if (!IsBrowser) StartStop(false); connect(Scope, SIGNAL(PixelData(QVector)), SLOT(SetPixelData(QVector))); // Call to get initial pixel ID correct UpdateScope(0); } TP_DAQ::~TP_DAQ() { delete[] Pixel; } // Translate pixel ID to board, chip, channel void TP_DAQ::TranslatePixelID(int ID) { // setValue() below will call UpdateScope() through signal, therefore need to store numbers here unsigned int BoardNo = Scope->Pixel_to_FPAboard(ID); unsigned int PatchNo = Scope->Pixel_to_FPApatch(ID); unsigned int PixelNo = Scope->Pixel_to_FPApixel(ID); if (BoardNo == Scope->PM_ERROR_CODE) PixelID->setValue(-1); else { Board->setValue(BoardNo); Chip->setValue(PatchNo); Channel->setValue(PixelNo); } } // Keep current trace void TP_DAQ::KeepCurrent() { Scope->AddTrace(Board->value(), Chip->value(), Channel->value()); } // Start/stop event acquisition void TP_DAQ::StartStop(bool State) { Scope->SetActive(!State); StartStopButton->setText(State ? "Start" : "Stop"); } // Open raw data file void TP_DAQ::OpenDataFile() { Scope->OpenRawFile(FilenameBox->text()); } // Update event scope void TP_DAQ::UpdateScope(int) { // Update pixel ID PixelID->setValue(Scope->FPA_to_Pixel(0, Board->value(), Chip->value(), Channel->value())); if (PixelID->value() == (int) Scope->PM_ERROR_CODE) PixelID->setValue(-1); // Update first trace Scope->UpdateFirst(Board->value(), Chip->value(), Channel->value()); } // Show/hide pixel display void TP_DAQ::ShowPixelDisplay() { Display->show(); Display->raise(); } void TP_DAQ::SetPixelData(QVector Data) { QwtLinearColorMap Map; for (int i=0; isetPalette(QPalette(Map.color(QwtDoubleInterval(300, 400), Data[i]))); } } // // Evidence page // TP_Evidence::TP_Evidence() { setAttribute(Qt::WA_DeleteOnClose); QGridLayout *Layout = new QGridLayout(this); EddLineDisplay *Line; EddText *Text; Line = new EddLineDisplay("Alarm/Message"); Line->setMaximumWidth(200); Layout->addWidget(Line, 0, 0, 1, 2); QPushButton *Button = new QPushButton(); Button->setText("ON/OFF"); Button->setCheckable(true); connect(Button, SIGNAL(toggled(bool)), SLOT(ToggleAlarm(bool))); Layout->addWidget(Button, 0, 3, 1, 1); Line = new EddLineDisplay("Alarm/MasterAlarm"); Layout->addWidget(Line, 0, 1, 1, 1); Text = new EddText("Alarm/Summary", true); Text->Accumulate = false; Text->setMaximumWidth(200); Text->setMaximumHeight(150); Layout->addWidget(Text, 1, 0, 1, 2); Line = new EddLineDisplay("DColl/Message"); Line->setMaximumWidth(200); Layout->addWidget(Line, 3, 0, 1, 2); Line = new EddLineDisplay("DColl/DataSizeMB"); Layout->addWidget(Line, 4, 0, 1, 1); Line = new EddLineDisplay("DColl/LogSizeMB"); Layout->addWidget(Line, 4, 1, 1, 1); Line = new EddLineDisplay("DColl/CurrentFile"); Line->setMaximumWidth(400); Layout->addWidget(Line, 5, 0, 1, 3); Line = new EddLineDisplay("Config/Message"); Line->setMaximumWidth(200); Layout->addWidget(Line, 6, 0, 1, 2); Line = new EddLineDisplay("Config/ModifyTime"); Line->setMaximumWidth(200); Line->ShowAsTime = true; Layout->addWidget(Line, 7, 0, 1, 1); Button = new QPushButton(); Button->setText("eLogBook"); connect(Button, SIGNAL(released()), SLOT(StartELog())); Layout->addWidget(Button, 6, 1, 1, 1); Button = new QPushButton(); Button->setText("DIM browser"); connect(Button, SIGNAL(released()), SLOT(StartDIMBrowser())); Layout->addWidget(Button, 7, 1, 1, 1); Line = new EddLineDisplay("Edd/Rate_kBSec"); Layout->addWidget(Line, 8, 0, 1, 1); } // Toggle state of Alarm server void TP_Evidence::ToggleAlarm(bool State) { if (State) DimClient::sendCommandNB((char *) "Alarm/Switch", (char *) "off"); else DimClient::sendCommandNB((char *) "Alarm/Switch", (char *) "on"); } // Start DIM Browser void TP_Evidence::StartDIMBrowser() { QProcess::startDetached("did", QStringList(), QString(getenv("DIMDIR"))+"/linux/"); } // Start eLogBook void TP_Evidence::StartELog() { QProcess::startDetached("firefox http://fact.ethz.ch/FACTelog/index.jsp"); } //-------------------------------------------------------------------- //*************************** Main GUI ******************************* //-------------------------------------------------------------------- // // All widgets have ultimately Central as parent. // GUI::GUI() { Handler = new EddDim; // Arrangement in tabs TabWidget = new QTabWidget(this); TabWidget->setTabsClosable(true); connect(TabWidget, SIGNAL(tabCloseRequested(int)), SLOT(DetachTab(int))); TabWidget->addTab(new TP_DAQ(false), "Event scope"); TabWidget->addTab(new TP_FADctrl, "FADctrl"); TabWidget->addTab(new TP_Bias, "Bias"); TabWidget->addTab(new TP_Feedback, "Feedback"); TabWidget->addTab(new TP_Environment, "FSC/SQM"); TabWidget->addTab(new TP_Evidence, "Evidence"); // Set features of main window setStatusBar(new QStatusBar(this)); setWindowTitle("Edd - Evidence Data Display - Node: " + QString(DimServer::getDnsNode()) + ":" + QString::number(DimServer::getDnsPort())); setCentralWidget(TabWidget); // Menu bar QMenu* Menu = menuBar()->addMenu("&Menu"); Menu->addAction("Open history plot", this, SLOT(MenuNewHistory())); Menu->addAction("Send command", this, SLOT(MenuCommand())); Menu->addAction("Raw data browser", this, SLOT(MenuRawDataBrowser())); Menu->addSeparator(); Menu->addAction("About", this, SLOT(MenuAbout())); Menu->addSeparator(); QAction* QuitAction = Menu->addAction("Quit", qApp, SLOT(quit())); QuitAction->setShortcut(Qt::CTRL + Qt::Key_Q); // Size and show main window QSize Size = TabWidget->sizeHint()*1.1; Size = Size.boundedTo(QApplication::desktop()->screenGeometry(this).size()*0.6); //setMaximumSize(QApplication::desktop()->screenGeometry(this).size()*0.9); TabWidget->resize(Size); TabWidget->setMaximumSize(QApplication::desktop()->screenGeometry(this).size()*0.9); show(); // Set timer to regularly check the master alarm QTimer *Timer = new QTimer(this); connect(Timer, SIGNAL(timeout()), this, SLOT(CheckAlarm())); if (QString(DimServer::getDnsNode()).contains("ethz.ch")) Timer->start(5000); } void GUI::MenuAbout() { QString Rev(SVN_REVISION); Rev.remove(0,1).chop(2); QMessageBox::about(this, "About Edd","Evidence Data Display\n\n" "Written by Oliver Grimm, IPP, ETH Zurich\n" "This version compiled "__DATE__" ("+Rev+")\n\n" "Graphical user interface implemented with Qt and Qwt.\n" "Evidence control system based on DIM (http://dim.web.cern.ch).\n\n" "Comments to oliver.grimm@phys.ethz.ch."); } // Open new history plot void GUI::MenuNewHistory() { QStringList List, Index; char *Name, *Format; int Type; bool OK; // Find all DIM services and sort getServices("*"); while ((Type = getNextService(Name, Format)) != 0) { if (Type==DimSERVICE) List.append(Name); } List.sort(); // Open dialog and open history window QString Result = QInputDialog::getItem(this, "Edd Request", "Enter or choose DIM service name\n\nAdd index or index range a-b separated by colon\nWithout index, all array members will be plotted (for numeric data)", List, 0, true, &OK); // Check if cancelled or empty data List = Result.trimmed().split(":", QString::SkipEmptyParts); if (!OK || List.isEmpty()) return; // Check if index was given if (List.size() == 1) { OpenHistory(List[0].toAscii().data(), -1); return; } // Single index or index range? Index = List[1].trimmed().split("-", QString::SkipEmptyParts); if (Index.isEmpty()) { QMessageBox::warning(NULL, "Edd Message", QString("Incorrect index range given") ,QMessageBox::Ok); return; } if (Index.size() == 1) OpenHistory(List[0].toAscii().data(), atoi(Index[0].toAscii().data())); else OpenHistory(List[0].toAscii().data(), atoi(Index[0].toAscii().data()), atoi(Index[1].toAscii().data())); } // Send command selected from list void GUI::MenuCommand() { QStringList List; char *Name, *Format; int Type; bool OK; // Find all DIM commands and sort getServices("*"); while ((Type = getNextService(Name, Format)) != 0) { if (Type==DimCOMMAND) List.append(Name); } List.sort(); // Open dialog and open window for entering command QString Result = QInputDialog::getItem(this, "Edd Request", "Enter or choose DIM command name", List, 0, true, &OK); if (OK && !Result.isEmpty()) { Result = Result.trimmed(); QMainWindow *M = new QMainWindow; M->setCentralWidget(new QWidget(M)); M->setStatusBar(new QStatusBar(M)); M->setAttribute(Qt::WA_DeleteOnClose); QWidget *W = new EddCommand(Result.toAscii().data()); QFormLayout *FormLayout = new QFormLayout(M->centralWidget()); FormLayout->setRowWrapPolicy(QFormLayout::WrapAllRows); FormLayout->addRow("Enter argument for " + Result, W); M->show(); } } // Raw data browser void GUI::MenuRawDataBrowser() { DetachTab(0, true); } // Open tab as separate window void GUI::DetachTab(int Tab, bool IsDAQBrowser) { QWidget *W = NULL; QMainWindow *M = new QMainWindow; M->setCentralWidget(new QWidget(M)); M->setStatusBar(new QStatusBar(M)); switch(Tab) { case 0: W = new TP_DAQ(IsDAQBrowser); break; case 1: W = new TP_FADctrl; break; case 2: W = new TP_Bias; break; case 3: W = new TP_Feedback; break; case 4: W = new TP_Environment; break; case 5: W = new TP_Evidence; break; default: break; } if (W == NULL) { delete M->centralWidget(); delete M; return; } W->setParent(M); W->resize(size()); M->resize(size()); if (!IsDAQBrowser) M->setWindowTitle("Edd - " + TabWidget->tabText(Tab)); else M->setWindowTitle("Edd - Raw Data Browser"); M->show(); return; } // Check alarm level and if Alarm server is alive void GUI::CheckAlarm() { static int WarnedLevel = 0; static bool AlarmServerWarned = false; // === Check service Alarm/MasterAlarm === DimCurrentInfo MasterAlarm("Alarm/MasterAlarm", -1); if (MasterAlarm.getInt() > WarnedLevel) { QSound::play(QApplication::applicationDirPath() + "/Error.wav"); // Construct warning message box QMessageBox Box; Box.setWindowTitle("Edd Alarm"); Box.setText("Service 'Alarm/MasterAlarm' is at " + QString::number(MasterAlarm.getInt())); Box.setInformativeText("Warn again for the same alarm level?"); Box.setIcon(QMessageBox::Warning); Box.setStandardButtons(QMessageBox::Yes | QMessageBox::No); Box.setDefaultButton(QMessageBox::No); // Check if user wants to reset warned level if (Box.exec() == QMessageBox::Yes) WarnedLevel = 0; else WarnedLevel = MasterAlarm.getInt(); } // If MasterAlam decreased, lower also warned level if (MasterAlarm.getInt() < WarnedLevel && MasterAlarm.getInt() >=0 ) WarnedLevel = MasterAlarm.getInt(); // === Check Alarm server === DimCurrentInfo ServerList("DIS_DNS/SERVER_LIST", NO_LINK); std::string Result = EvidenceServer::ToString((char *) "C", ServerList.getData(), ServerList.getSize()); // Warn if SERVER_LIST does not contain alarm server if (Result.find("Alarm@") == std::string::npos && !AlarmServerWarned) { QMessageBox Box; Box.setWindowTitle("Edd Alarm"); Box.setText("Alarm server is unavailable or in error"); Box.setIcon(QMessageBox::Critical); Box.setStandardButtons(QMessageBox::Ok); Box.setDefaultButton(QMessageBox::Ok); Box.exec(); AlarmServerWarned = true; } if (Result.find("Alarm@") != std::string::npos) AlarmServerWarned = false; } // Quit application when clicking close button on window void GUI::closeEvent(QCloseEvent *) { qApp->quit(); } // //**************************** Main program *************************** // int main(int argc, char *argv[]) { if (argc>1 && strcmp(argv[1],"drsdaq")==0) DRSBoard = "drsdaq"; // If name server node not set, use localhost if (getenv("DIM_DNS_NODE") == NULL && setenv("DIM_DNS_NODE", "localhost", 0) == -1) { printf("setenv() failed in EvidenceServer::EvidenceServer (%s)\n", strerror(errno)); } // EvidenceServer::ToString() works for structures only without padding dic_disable_padding(); dis_disable_padding(); // Make RPC to get pixelmap DimRpcInfo RPC((char *) "ConfigRequest", (char *) ""); RPC.setData((char *) "Misc PixelMap"); PixelMapText = std::string(RPC.getString(), RPC.getSize()); QApplication app(argc, argv); GUI MainWindow; return app.exec(); }