Changeset 10280 for fact/Evidence
- Timestamp:
- 04/01/11 09:39:25 (14 years ago)
- Location:
- fact/Evidence
- Files:
-
- 1 deleted
- 1 edited
- 3 copied
Legend:
- Unmodified
- Added
- Removed
-
fact/Evidence/Doc/Evidence.tex
r262 r10280 11 11 \usepackage{url} 12 12 \usepackage{listings} 13 \usepackage[light]{draftcopy}13 %\usepackage[light]{draftcopy} 14 14 \usepackage{longtable} 15 15 … … 39 39 \maketitle 40 40 41 This report describes the design and basic functionality of the \E control system. This essentially is a C++ class and a set of programs running on Linux for controlling small scale experiments. It is based on CERN's DIM library for interprocess communication over TCP/IP connections. \lstinline{$Rev$}41 This report describes the design and basic functionality of the \E control system. This essentially is a C++ class and a set of programs running on Linux for controlling small scale experiments. It is based on CERN's DIM library for interprocess communication over TCP/IP connections. 42 42 43 43 \tableofcontents … … 48 48 \label{Overview} 49 49 50 The \E control system has been developed for application in small-scale experiments, for which established and comprehensive control systems like EPICS, DOOCS or PVS-II are too large and impose too much overburden.\footnote{Information on these systems can be found at \url{http://www.aps.anl.gov/epics/}, \url{http://tesla.desy.de/doocs}, \url{http://www.etm.at/}.} The development of \E has been started within the FACT project (\emph{First G-APD Cherenkov Telescope} \cite{Bra09,And1 0}).50 The \E control system has been developed for application in small-scale experiments, for which established and comprehensive control systems like EPICS, DOOCS or PVS-II are too large and impose too much overburden.\footnote{Information on these systems can be found at \url{http://www.aps.anl.gov/epics/}, \url{http://tesla.desy.de/doocs}, \url{http://www.etm.at/}.} The development of \E has been started within the FACT project (\emph{First G-APD Cherenkov Telescope} \cite{Bra09,And11}). 51 51 52 52 An experiment control system often comprises several individual programs that require configuration information, produce data that should be stored and easily visualized, and at least partly need to exchange information between each other. The intention of \E is to a) integrate this with a minimum of extra coding on part of the applications, b) to achieve this using as far as reasonable established tools, c) to be centralized\footnote{This is a design choice that partly defines the range of applicable experiments. For large systems decentralization can be a better approach.}, and d) to be lean. … … 130 130 \item Provides a method for configuration requests. If the configuration data is not available, the application terminates with a message of FATAL severity unless default data is given. 131 131 \item Provides a method for safely translating DIM service data into text. 132 \item Implements the virtual DIM methods \lstinline{exitHandler()}. It can be called through a standard DIM command \lstinline{SvrName/EXIT}, taking a single integer as argument. Upon first invocation, the handler just sets the flag \lstinline{ExitRequest} which should be handled by the application. Upon second invocation, it will call \lstinline{exit()}. The user application can override this handler.132 \item Implements the virtual DIM methods \lstinline{exitHandler()}. It can be called through a standard DIM command \lstinline{SvrName/EXIT}, taking a single integer as argument. The handler will issue a message with severity \lstinline{INFO}, including the received integer, and then calls \lstinline{exit(EXIT_SUCCESS)}. The user application can override this handler. 133 133 \item Provides a DIM command \lstinline{SvrName/ResetMessage}. It will set the message service to INFO severity with the information which client issued the command. This can be used to remove a WARN, ERROR or FATAL serverity once the problem has been fixed. The \lstinline{Alarm} server uses this command if it is instructed to reset an alarm level. The command takes no arguments. 134 134 \item Implements the virtual DIM methods \lstinline{errorHandler()}. The error handler will issue a message with ERROR severity that contains the DIM error code. The user application can override this handler. … … 153 153 ~EvidenceServer(); 154 154 155 enum MessageType {INFO=0, WARN=1 , ERROR=2, FATAL=3};156 157 void Message( MessageType, const char *, ...);155 enum MessageType {INFO=0, WARN=10, ERROR=20, FATAL=30}; 156 157 void Message(int, const char *, ...); 158 158 void SendToLog(const char *, ...); 159 159 string GetConfig(string, string = string()); … … 164 164 static bool ServiceOK(DimInfo *); 165 165 static bool ServiceOK(DimRpcInfo *); 166 166 static bool ServiceOK(DimCurrentInfo *); 167 167 static vector<string> Tokenize(const string &, const string & = " "); 168 168 … … 181 181 \underline{\lstinline{GetConfig()}} issues, on first invocation, a DIM remote procedure call to the configuration server to retrieve the required data and returns it as a string. The second argument gives the data to be returned in case the server is unavailable or cannot provide the requested data. If in this case the second string is empty, the program terminates with a FATAL message. Using the service \lstinline{Config/ModifyTime}, the server keeps track of changes to the configuration file in the background. Upon subsequent requests for the same configuration data, it only issues a remote procedure call again if the file changed in the meantime. If not, the same data already retrieved is returned. This way, this function can be repeatedly called, even at high rate, without generating unnecessary load to the configuration server (as the configuration file does not change frequently). 182 182 183 The virtual method \underline{\lstinline{ConfigChanged()}} is executed in a separate thread when the configuration file changes. It can be reimplemented by the application. Calls to \lstinline{GetConfig()} from this method will be blocking and thus result in updated configuration data.183 The virtual method \underline{\lstinline{ConfigChanged()}} is executed in a separate thread when the configuration file changes. It can be reimplemented by the application. Calls to \lstinline{GetConfig()} from this method will be blocking and thus result in immediately updated configuration data. See Sect.\,\ref{ConfigHandling} for more details. 184 184 185 185 The methods \underline{\lstinline{Lock()}} and \underline{\lstinline{Unlock()}} work on an internal mutex.\footnote{Its type is \lstinline{PTHREAD_MUTEX_ERRORCHECK}. In case an already locked mutex is re-locked, the corresponding system call will therefore return a error and thus avoid dead-locking. Error messages from \lstinline{Lock()} and \lstinline{Unlock()} are written to the console and to the log file. They are not published using \lstinline{Message()} since this method itself uses locking and calling it would result in an infinite recursion.} They are used by \lstinline{GetConfig()} but are also available for the user application to serialize access from multiple threads. Calling functions in the locked state should be avoided as it might result in re-locking. … … 265 265 \subsection{\lstinline{Alarm} --- Handling of error conditions} 266 266 267 The alarm server maintains a list of \emph{alarm levels} for a given set of servers. The alarm levels are defined as \lstinline{OK} (0), \lstinline{WARN} (1), \lstinline{ERROR} (2), \lstinline{FATAL} (3), and \lstinline{UNAVAILABLE} (4). The first four result from the corresponding severities of the message services, to which the alarm server subscribes. The alarm level does not decrease if, for example, a server issues a message with severity \lstinline{WARN} after one with \lstinline{ERROR}. It is only reset by command or by restarting the alarm server.267 The alarm server maintains a list of \emph{alarm levels} for a given set of servers. The standard alarm levels are defined as \lstinline{OK} (0), \lstinline{WARN} (10), \lstinline{ERROR} (20), \lstinline{FATAL} (30), and \lstinline{UNAVAILABLE} (40). The first four result from the corresponding severities of the message services, to which the alarm server subscribes. The alarm level does not decrease if, for example, a server issues a message with severity \lstinline{WARN} after one with \lstinline{ERROR}. Only if a server changes from \lstinline{UNAVAILABLE} to \lstinline{OK} (by restarting) or through the command \lstinline|ResetAlarm| can the alarm level be lowered. 268 268 269 269 A master alarm is generated from the highest server alarm level. The alarm server also periodically checks if all required servers are up (searching for them with the DIM browser). It can send an email in case a server is down or in error. One email will be send with each increase of alarm level for each server. … … 277 277 \lstinline|period| & Interval in seconds to check for server availability.\\[1ex] 278 278 \multicolumn{2}{l}{\textbf{Commands}} \\ 279 \lstinline|ResetAlarm xyz| & Reset alarm level of server \lstinline|xyz|.\\[1ex] 279 \lstinline|ResetAlarm xyz| & Reset alarm level of server \lstinline|xyz|.\\ 280 \lstinline|Switch on/off| & Switch alarm server on or off.\\[1ex] 280 281 \multicolumn{2}{l}{\textbf{Services}} \\ 281 282 \lstinline|Alarm/Summary| & Text listing all observed servers and their alarm level.\\ … … 360 361 As a first step in achieving this, the application should not store the obtained configuration data internally, but always re-request it using the method \lstinline{GetConfig()} described in Sect.\,\ref{EvidenceServer-Methods}. This method will only issue a remote procedure call to the \lstinline{Config} server if the configuration file has been modified since the last invocation. So calling this method even at high rate will not load the configuration server at all if the configuraton file is unchanged, but will yield up-to-date information if it did change. 361 362 362 The remote procedure call is blocking when called from the main thread or from the method \lstinline{ConfigChanged()} (which runs in a separate thread). It is non-blocking, using an \lstinline{rpcInfoHandler()}, when called from any other thread, especially also from the DIM handler thread. Blocking execution means that the remote procedure call will wait until the data has arrived from the server before returning to the application, whereas non-blocking execution will return immediately and invoke a handler later when the data arrived. This procedure is necessary since a blocking remote procedure call from \lstinline{infoHandler()}will result in a dead-lock.363 The remote procedure call is non-blocking (using an \lstinline{rpcInfoHandler()}) when invoked from the DIM handler thread and blocking otherwise. Blocking execution means that the remote procedure call will wait until the data has arrived from the server before returning to the application, whereas non-blocking execution will return immediately and invoke an internal handler later when the data arrived. This procedure is necessary since a blocking remote procedure call from the DIM handler thread (e.g. from an \lstinline{infoHandler()}) will result in a dead-lock. 363 364 364 365 In the non-blocking case, the call to \lstinline{GetConfig()} returns still the previous, non-updated data even if the configuration file changed. The result of the non-blocking remote procedure call can only be processed by DIM once the current and all queued handler invocations have finished. When this is done, updated data will be returned by subsequent calls to \lstinline{GetConfig()}. … … 368 369 An alternative, albeit for the programmer more demanding, procedure for semi-automatic updates on configuration information is to reimplement the virtual method \lstinline{ConfigChanged()} in the user class. This method is invoked as a separate thread by the \lstinline{EvidenceServer} class whenever the service \lstinline{Config/ModifyTime} changes (and also at program start-up). As it is not running within the DIM handler thread, \lstinline{GetConfig()} will use blocking connections to get immediately up-to-date data when called from \lstinline{ConfigChanged()}. 369 370 370 Running in a separate thread requires suitable protection by the programmer when accessing common data structures. To ease that, the \lstinline{EvidenceServer} class contains the pair of methods \lstinline{Lock()} and \lstinline{Unlock()} that work on an class internal mutex. The mutex type is \lstinline{PTHREAD_MUTEX_ERRORCHECK} and therefore includes error checking: no dead-lock will occur if double locking, butthe program will terminate with a \lstinline{FATAL} message.371 Running in a separate thread requires suitable protection by the programmer when accessing common data structures. To ease that, the \lstinline{EvidenceServer} class contains the methods \lstinline{Lock()} and \lstinline{Unlock()} that work on an class internal mutex. The mutex type is \lstinline{PTHREAD_MUTEX_ERRORCHECK} and therefore includes error checking: no dead-lock will occur if double locking, but instead the program will terminate with a \lstinline{FATAL} message. 371 372 372 373 … … 450 451 A graphical user interface (GUI), implemented using the Qt and Qwt frameworks\footnote{Information on these frameworks is available at \url{http://qt.nokia.com/} and \url{http://qwt.sourceforge.net/}.}, is available. It derives from standard widget classes extended versions that can display the contents of DIM services and history buffers. A widget to send generic text commands is also available. Qwt is used to display graphs which is not supported by Qt. 451 452 452 The GUI is called \emph{Evidence Data Display} (\lstinline{E DD}). It has a single point interface to the DIM system and distributes received service updates to its widgets using the Qt signal/slot mechanism. This is necessary since the DIM \lstinline{infoHandler()} receiving the updates runs in a separate thread, but manipulations of GUI elements within Qt may only be done by the main thread. This mechanism also guarantees that one GUI instance subscribes not more than once to a particular service, even if the same data is shown by multiple widgets.453 The GUI is called \emph{Evidence Data Display} (\lstinline{Edd}). It has a single point interface to the DIM system and distributes received service updates to its widgets using the Qt signal/slot mechanism. This is necessary since the DIM \lstinline{infoHandler()} receiving the updates runs in a separate thread, but manipulations of GUI elements within Qt may only be done by the main thread. This mechanism also guarantees that one GUI instance subscribes not more than once to a particular service, even if the same data is shown by multiple widgets. 453 454 454 455 The GUI implementation is designed to be easily portable and does not use operating-system specifics. It sticks to standard C++ code, to Qt capabilities and to DIM. Qt is explicitely designed for cross-platform applications. … … 466 467 \label{DIMDetails} 467 468 468 \subsection{Format of \lstinline{DI M_DNS/SERVER_LIST} and \lstinline{xyz/SERVICE_LIST}}469 \subsection{Format of \lstinline{DIS_DNS/SERVER_LIST} and \lstinline{xyz/SERVICE_LIST}} 469 470 \label{ServiceFormats} 470 471 … … 497 498 If server and client use the same structure definition, a cast like \lstinline{struct A *Pnt = getData()} will guarantee correct access to elements. For example, \lstinline{Pnt->b[1]} will contain the second integer, even if client and server pad structures differently. Padding can also be disabled for a server if desired. 498 499 499 In general, no blocking functions should be called from within a DIM handler. Specifically, making a blocking remote procedure call in an \lstinline{infoHandler()} will dead lock.\footnote{Non-blocking reception using an \lstinline{rpcInfoHandler()} is possible}. 500 In general, no blocking functions should be called from within a DIM handler. Specifically, making a blocking remote procedure call in an \lstinline{infoHandler()} will dead lock.\footnote{Non-blocking reception using an \lstinline{rpcInfoHandler()} is possible}. This also applies to the \lstinline{exitHandler()}. Calling \lstinline{exit()} from this handler will run destructors of static objects within the DIM thread. This might result in undesirable behaviour if DIM services are deleted in those destructors. 500 501 501 502 If DIM is compiled without threads, it uses internally the signals SIGIO and SIGALRM for communication. … … 538 539 \begin{thebibliography}{xxxx00} 539 540 540 \bibitem[Gas01]{Gas01} C. Gaspar, M. D\"{o}nszelmann and Ph. Charpentier, \emph{DIM, a portable, light weight package for information publishing, data transfer and inter-process communication}, Computer Physics Communications 140 1+2 102-9, 2001 541 \bibitem[Bra09]{Bra09} I. Braun et al., Nucl. Inst. and Methods A 610, 400 (2009) 542 \bibitem[And10]{And10} H. Anderhub et al., Nucl. Inst. and Methods, to be published (2010) 543 541 \bibitem[Gas01]{Gas01} C. Gaspar, M. D\"{o}nszelmann and Ph. Charpentier, \emph{DIM, a portable, light weight package for information publishing, data transfer and inter-process communication}, Computer Physics Communications \textbf{140}, 102 (2001) 542 \bibitem[Bra09]{Bra09} I. Braun et al., Nucl. Inst. and Methods \textbf{A 610}, 400 (2009) 543 \bibitem[And11]{And11} H. Anderhub et al., Nucl. Inst. and Methods, \textbf{A 628}, 107 (2011) 544 544 \end{thebibliography} 545 545 -
fact/Evidence/Doc/INSTALL_Edd
r10262 r10280 36 36 and DIMDIR to the DIM installation. 37 37 38 The subversion repository should preferably be checked out fully, as39 the class describing the M0 raw data format is currently needed.40 41 38 Issue 'qmake', then 'make' will build the executable Edd. 42 39 -
fact/Evidence/GUI.cc
r10262 r10280 13 13 ============================================================ */ 14 14 15 #include "Edd.h" 16 17 Qt::GlobalColor LineColors[] = {Qt::black, Qt::blue, Qt::red, Qt::green, Qt::white, 18 Qt::darkRed, Qt::darkGreen, Qt::darkBlue, Qt::cyan, 19 Qt::darkCyan, Qt::magenta, Qt::darkMagenta, 20 Qt::gray, Qt::darkGray, Qt::lightGray}; 21 15 #include "GUI.h" 22 16 23 17 class EddDim *Handler; 24 QString DRSBoard = "drsdaq"; 25 std::string PixelMapText; 26 18 19 // 27 20 // History chooser function (opens plot for numeric data, TextHist for all other) 21 // 28 22 QWidget *OpenHistory(char *Service, int Index) { 29 23 … … 54 48 } 55 49 50 // 56 51 // Set status tip (returns true if service was available) 52 // 57 53 bool SetStatus(QWidget *W, QString Name, int Time, QString Format, int Index) { 58 54 … … 589 585 QwtPlotCurve *EddBasePlot::NewCurve(QwtText Title) { 590 586 587 static Qt::GlobalColor LineColors[] = {Qt::black, Qt::blue, Qt::red, Qt::green, Qt::white, 588 Qt::darkRed, Qt::darkGreen, Qt::darkBlue, Qt::cyan, Qt::darkCyan, Qt::magenta, Qt::darkMagenta, 589 Qt::gray, Qt::darkGray, Qt::lightGray}; 591 590 struct PlotItem N; 592 591 … … 1006 1005 } 1007 1006 1008 // 1009 // 1010 // ====== FACT specific part ====== 1011 // 1012 // 1013 1014 //////////////////////// 1015 // Event oscilloscope // 1016 //////////////////////// 1007 1008 ///////////////////// 1009 // Open new window // 1010 ///////////////////// 1017 1011 1018 1012 // Constructor 1019 EventScope::EventScope(QWidget *P): EddBasePlot(P), PixelMap(PixelMapText, false) { 1020 1021 Name = DRSBoard+"/EventData"; 1022 1023 Tmpfile = tmpfile(); 1024 if(Tmpfile == NULL) { 1025 QMessageBox::warning(this, "Edd Message", "Could not open temporary file.", QMessageBox::Ok); 1026 } 1027 1028 // Open file with RawDataCTX 1029 RD = new RawDataCTX(true); 1030 ErrCode = CTX_NOTOPEN; 1031 1032 // Context menu 1033 PhysPipeAction = new QAction("Physical pipeline", this); 1034 PhysPipeAction->setCheckable(true); 1035 connect(PhysPipeAction, SIGNAL(triggered()), SLOT(PlotTraces())); 1036 Menu->insertAction(StripAction, PhysPipeAction); 1037 1038 PersistanceAction = new QAction("Persistance", this); 1039 PersistanceAction->setCheckable(true); 1040 Menu->insertAction(StripAction, PersistanceAction); 1041 Menu->removeAction(StripAction); 1042 1043 // Initial trace 1044 AddTrace(0,0,0); 1045 1046 // Connect to DIM handler 1047 if (connect(Handler, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), SLOT(Update(QString, int, QByteArray, QString, QString))) == false) { 1048 printf("Failed connection for %s\n", Name.toAscii().data()); 1049 } 1050 SetActive(true); 1051 } 1052 1053 // Destructor (items with parent widget are automatically deleted) 1054 EventScope::~EventScope() { 1055 1056 SetActive(false); 1057 while (!List.isEmpty()) DeleteCurve(List.last().Signal); 1058 delete RD; 1059 if (Tmpfile != NULL) fclose(Tmpfile); 1060 } 1061 1062 // Add trace 1063 void EventScope::AddTrace(int Board, int Chip, int Channel) { 1064 1065 struct ItemDetails N; 1066 1067 N.Signal = NewCurve(QString::number(Board)+","+QString::number(Chip)+","+ QString::number(Channel)+ " (" + ToPixel(0, Board, Chip, Channel) + ")"); 1068 N.Board = Board; 1069 N.Chip = Chip; 1070 N.Channel = Channel; 1071 N.Trigger = new QwtPlotMarker(); 1072 N.Trigger->setSymbol(QwtSymbol(QwtSymbol::Diamond, QBrush(N.Signal->pen().color()), N.Signal->pen(), QSize(10,10))); 1073 N.Trigger->attach(this); 1074 1075 if (List.isEmpty()) { 1076 QPen Pen = N.Signal->pen(); 1077 Pen.setWidth(2); 1078 N.Signal->setPen(Pen); 1079 } 1080 List.append(N); 1081 1082 PlotTraces(); 1083 } 1084 1085 // Update last trace (to reflect current setting of spin boxes in DAQ page) 1086 void EventScope::UpdateFirst(int Board, int Chip, int Channel) { 1087 1088 if (List.isEmpty()) return; 1089 1090 // Clear in case persistance was activated 1091 ClearCurve(0); 1092 1093 List.first().Signal->setTitle(QString::number(Board)+","+QString::number(Chip)+","+ QString::number(Channel) + " (" + ToPixel(0, Board, Chip, Channel) + ")"); 1094 List.first().Board = Board; 1095 List.first().Chip = Chip; 1096 List.first().Channel = Channel; 1097 1098 PlotTraces(); 1099 } 1100 1101 // Update event buffer 1102 void EventScope::Update(QString Name, int Time, QByteArray Data, QString Format, QString) { 1103 1104 if (Name != this->Name) return; 1105 1106 // Check if service available 1107 if (!SetStatus(this, Name, Time, Format)) return; 1108 if (Data.size() < (int) sizeof(RunHeader)) return; 1109 1110 // Disconnect while processing to avoid queing of events 1111 disconnect(Handler, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), this, SLOT(Update(QString, int, QByteArray, QString, QString))); 1112 1113 // Open tempory file and write event data to this file 1114 QTemporaryFile File; 1115 if (!File.open()) { 1116 QMessageBox::warning(this, "Edd Message","Could not open temporary file.",QMessageBox::Ok); 1117 return; 1118 } 1119 if (File.write(Data) == -1) { 1120 QMessageBox::warning(this, "Edd Message","Could not write data to temporary file.",QMessageBox::Ok); 1121 return; 1122 } 1123 1124 // Prepare temporary file for run header 1125 ftruncate(fileno(Tmpfile), 0); 1126 rewind(Tmpfile); 1127 1128 // Open file with RawDataCTX 1129 switch (ErrCode = RD->OpenDataFile(File.fileName().toAscii().data(), Tmpfile)) { 1130 case CTX_FOPEN: QMessageBox::warning(this, "Edd Message","Could not open file.",QMessageBox::Ok); 1131 return; 1132 case CTX_RHEADER: QMessageBox::warning(this, "Edd Message","Could not read run header.",QMessageBox::Ok); 1133 return; 1134 case CTX_BSTRUCT: QMessageBox::warning(this, "Edd Message","Could not read board structures.",QMessageBox::Ok); 1135 return; 1136 default: break; 1137 } 1138 1139 // Emit signal containing run header 1140 rewind(Tmpfile); 1141 QTextStream Stream(Tmpfile); 1142 emit(RunHeaderChanged(Stream.readAll())); 1143 1144 // Prepare temporary file for event header 1145 ftruncate(fileno(Tmpfile), 0); 1146 rewind(Tmpfile); 1147 1148 // Write event header text to file 1149 if (RD->ReadEvent(0, Tmpfile) != CTX_OK) { 1150 QMessageBox::warning(this, "Edd Warning","Could not read event.",QMessageBox::Ok); 1151 return; 1152 } 1153 1154 // Add trigger cells to file 1155 fprintf(Tmpfile, "\nTrigger cells:"); 1156 int *TrigCells = (int *) RD->Data; 1157 for (unsigned int i=0; i<RD->RHeader->NBoards; i++) { 1158 fprintf(Tmpfile, "\n Board %d ", i); 1159 for (unsigned int j=0; j<RD->RHeader->NChips; j++) fprintf(Tmpfile, "%d ", *(TrigCells++)); 1160 } 1161 1162 // Emit signal containing run header 1163 rewind(Tmpfile); 1164 emit(EventHeaderChanged(Stream.readAll())); 1165 1166 // Update display 1167 PlotTraces(); 1168 1169 // Reconnect after processing 1170 connect(Handler, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), SLOT(Update(QString, int, QByteArray, QString, QString))); 1171 } 1172 1173 // Update curves 1174 void EventScope::PlotTraces() { 1175 1176 double x,y; 1177 unsigned int Cell, Trig; 1178 static int Last = 0; 1179 1180 // Only process if valid data in RawDataCTX class 1181 if (ErrCode != CTX_OK) return; 1182 1183 // Set x axis title 1184 if (PhysPipeAction->isChecked()) setAxisTitle(QwtPlot::xBottom, "Time from start of pipeline (ns)"); 1185 else setAxisTitle(QwtPlot::xBottom, "Time from trigger minus one revolution (ns)"); 1186 1187 // Loop through event data to update event scope 1188 RunHeader *R = RD->RHeader; 1189 for (int i=0; i<List.size(); i++) { 1190 1191 if (PersistanceAction->isChecked()) List[i].Signal->setStyle(QwtPlotCurve::Dots); 1192 else { 1193 ClearCurve(i); 1194 List[i].Signal->setStyle(QwtPlotCurve::Lines); 1195 } 1196 1197 // Check if current event contains requested trace 1198 if (List[i].Board>=R->NBoards || List[i].Chip>=R->NChips || List[i].Channel>=R->NChannels) continue; 1199 1200 // Set trigger marker visibility 1201 List[i].Trigger->setVisible(PhysPipeAction->isChecked()); 1202 1203 // Determine trigger cell 1204 Trig = *((int *) RD->Data + List[i].Board*R->NChips + List[i].Chip); 1205 1206 // Calulate point of curve 1207 for (unsigned int j=0; j<R->Samples; j++) { 1208 1209 if (PhysPipeAction->isChecked()) Cell = (j - Trig) % 1024; 1210 else Cell = j; 1211 1212 x = j / RD->BStruct[List[i].Board].NomFreq; 1213 y = *((short *) (RD->Data + R->NBoards*R->NChips*sizeof(int)) + 1214 List[i].Board*R->NChips*R->NChannels*R->Samples + List[i].Chip*R->NChannels*R->Samples + 1215 List[i].Channel*R->Samples + Cell) * RD->BStruct[List[i].Board].ScaleFactor; 1216 1217 AddPoint(i, x, y); 1218 1219 // Set trigger point indicator 1220 if (Trig == j) List[i].Trigger->setValue(x, y); 1221 } 1222 } 1223 1224 // Limit update rate in persistance mode 1225 if (!PersistanceAction->isChecked() || time(NULL) > Last) { 1226 UpdatePlot(); 1227 Last = time(NULL); 1228 } 1229 1230 1231 // Loop through event data for pixel display 1232 QVector<double> Pixel(R->NBoards*R->NChips*R->NChannels); 1233 int Count = 0; 1234 1235 for (unsigned int Board=0; Board<R->NBoards; Board++) { 1236 for (unsigned int Chip=0; Chip<R->NChips; Chip++) { 1237 for (unsigned int Channel=0; Channel<R->NChannels; Channel++) { 1238 Pixel[Count] = DBL_MIN; 1239 1240 for (unsigned int i=0; i<R->Samples; i++) { 1241 y = *((short *) (RD->Data + R->NBoards*R->NChips*sizeof(int)) + 1242 Board*R->NChips*R->NChannels*R->Samples + Chip*R->NChannels*R->Samples + 1243 Channel*R->Samples + i) * RD->BStruct[Board].ScaleFactor; 1244 1245 if (y > Pixel[Count]) Pixel[Count] = y; 1246 } 1247 Count++; 1248 }}} 1249 1250 emit(PixelData(Pixel)); 1251 } 1252 1253 // Remove list entry 1254 void EventScope::DeleteCurve(QwtPlotCurve *Curve) { 1255 1256 for (int i=0; i<List.size(); i++) if (List[i].Signal == Curve) { 1257 delete List[i].Trigger; 1258 List.removeAt(i); 1259 } 1260 } 1261 1262 // Set display active (if inactive, disconnect from server) 1263 void EventScope::SetActive(bool State) { 1264 1265 static bool Active = false; 1266 1267 if (State && !Active) Handler->Subscribe(DRSBoard+"/EventData"); 1268 if (!State && Active) Handler->Unsubscribe(DRSBoard+"/EventData"); 1269 Active = State; 1270 } 1271 1272 // Translate FPA ID to Pixel ID (use '-' instead of PM_ERROR_CODE) 1273 QString EventScope::ToPixel(unsigned int Crate, unsigned int Board, unsigned int Patch, unsigned int Pixel) { 1274 1275 if (FPA_to_Pixel(Crate, Board, Patch, Pixel) == PM_ERROR_CODE) return "-"; 1276 else return QString::number(FPA_to_Pixel(Crate, Board, Patch, Pixel)); 1277 } 1278 1279 //------------------------------------------------------------------ 1280 //**************************** Tab pages *************************** 1281 //------------------------------------------------------------------ 1282 1283 // 1284 // Environment page 1285 // 1286 TP_Environment::TP_Environment() { 1287 1288 QGridLayout *Layout = new QGridLayout(this); 1289 setAttribute(Qt::WA_DeleteOnClose); 1290 1291 // Status display 1292 EddLineDisplay *Line = new EddLineDisplay("ARDUINO/Message"); 1293 Line->setMaximumWidth(200); 1294 Layout->addWidget(Line, 0, 0, 1, 2); 1295 1296 // Generate plot and data displays 1297 EddPlot *Plot = new EddPlot(); 1298 for (int i=0; i<10; i++) { 1299 Line = new EddLineDisplay("ARDUINO/Data", i); 1300 Layout->addWidget(Line, i%5+1, i/5, 1, 1); 1301 Plot->AddService("ARDUINO/Data", i); 1302 } 1303 Layout->addWidget(Plot, 0, 2, 9, 7); 1304 1305 // Night sky monitor 1306 Line = new EddLineDisplay("SQM/Message"); 1307 Line->setMaximumWidth(200); 1308 Layout->addWidget(Line, 6, 0, 1, 2); 1309 1310 Line = new EddLineDisplay("SQM/NSB"); 1311 Layout->addWidget(Line, 7, 0, 1, 1); 1312 } 1313 1314 // 1315 // Bias page 1316 // 1317 TP_Bias::TP_Bias() { 1318 1319 QGridLayout *Layout = new QGridLayout(this); 1320 setAttribute(Qt::WA_DeleteOnClose); 1321 EddLineDisplay *Line; 1322 1323 EddPlot *Plot = new EddPlot(); 1324 Plot->setMinimumWidth(400); 1325 for (int i=0; i<18; i++) { 1326 Line = new EddLineDisplay("Bias/VOLT/ID00", i+64); 1327 Layout->addWidget(Line, i%9+1, 0+i/9, 1, 1); 1328 Plot->AddService("Bias/VOLT/ID00", i+64); 1329 1330 Line = new EddLineDisplay("Bias/VOLT/ID00", i+96); 1331 Layout->addWidget(Line, i%9+1, 2+i/9, 1, 1); 1332 Plot->AddService("Bias/VOLT/ID00",i+96); 1333 } 1334 1335 Layout->addWidget(Plot, 0, 4, 12, 3); 1336 Line = new EddLineDisplay("Bias/Message"); 1337 Line->setMaximumWidth(200); 1338 Layout->addWidget(Line, 0, 0, 1, 3); 1339 1340 EddCommand *Command = new EddCommand("Bias/Command"); 1341 Layout->addWidget(Command, 10, 0, 1, 4); 1342 1343 EddText *Text = new EddText("Bias/ConsoleOut", true); 1344 Text->setFixedWidth(400); 1345 Layout->addWidget(Text, 11, 0, 4, 4); 1346 1347 QWidget *Button = new QPushButton("Currents"); 1348 Layout->addWidget(Button, 13, 4, 1, 1); 1349 connect(Button, SIGNAL(pressed()), SLOT(BiasCurrents())); 1350 } 1351 1352 void TP_Bias::BiasCurrents() { 1353 1354 QMainWindow *M = new QMainWindow; 1013 EddWindow::EddWindow(QString ButtonName, QString WindowName): QPushButton(ButtonName) { 1014 1015 M = new QMainWindow; 1355 1016 M->setCentralWidget(new QWidget); 1356 1017 M->setStatusBar(new QStatusBar(M)); 1357 M->setWindowTitle("Edd - Bias currents"); 1358 M->setAttribute(Qt::WA_DeleteOnClose); 1359 1360 QGridLayout *Layout = new QGridLayout(M->centralWidget()); 1361 EddLineDisplay *Line; 1362 EddPlot *Plot = new EddPlot(); 1363 1364 for (int i=0; i<36; i++) { 1365 Line = new EddLineDisplay("Bias/MICROAMP/ID00", i+64); 1366 Line->setMaximumWidth(60); 1367 Layout->addWidget(Line, i%9, 0+i/9, 1, 1); 1368 Plot->AddService("Bias/MICROAMP/ID00", i+64); 1369 } 1370 Layout->addWidget(Plot, 0, 4, 30, 12); 1371 1372 M->show(); 1373 } 1374 1375 // 1376 // Feedback page 1377 // 1378 TP_Feedback::TP_Feedback() { 1379 1380 setAttribute(Qt::WA_DeleteOnClose); 1381 QGridLayout *Layout = new QGridLayout(this); 1382 EddLineDisplay *Line; 1383 1384 EddPlot *Plot = new EddPlot(); 1385 for (int i=0; i<36; i++) { 1386 Line = new EddLineDisplay("Feedback/Average", i); 1387 Line->setMaximumWidth(60); 1388 Layout->addWidget(Line, i%9+2, 0+i/9, 1, 1); 1389 Plot->AddService("Feedback/Average", i); 1390 } 1391 Layout->addWidget(Plot, 0, 4, 12, 10); 1392 1393 Line = new EddLineDisplay("Feedback/Message"); 1394 Line->setMaximumWidth(200); 1395 Layout->addWidget(Line, 0, 0, 1, 2); 1396 1397 Line = new EddLineDisplay("Feedback/State"); 1398 Line->setMaximumWidth(150); 1399 Layout->addWidget(Line, 1, 0, 1, 2); 1400 Line = new EddLineDisplay("Feedback/Count"); 1401 Line->setMaximumWidth(60); 1402 Layout->addWidget(Line, 1, 2); 1403 1404 QWidget *Button = new QPushButton("Details"); 1405 Layout->addWidget(Button, 12, 0, 1, 1); 1406 connect(Button, SIGNAL(pressed()), SLOT(FeedbackDetails())); 1407 } 1408 1409 void TP_Feedback::FeedbackDetails() { 1410 1411 QMainWindow *M = new QMainWindow; 1412 M->setCentralWidget(new QWidget); 1413 M->setStatusBar(new QStatusBar(M)); 1414 M->setWindowTitle("Edd - Feedback Details"); 1415 M->setAttribute(Qt::WA_DeleteOnClose); 1416 1417 QGridLayout *Layout = new QGridLayout(M->centralWidget()); 1418 EddLineDisplay *Line; 1419 EddPlot *Plot = new EddPlot(); 1420 1421 for (int i=0; i<36; i++) { 1422 Line = new EddLineDisplay("Feedback/Sigma", i); 1423 Line->setMaximumWidth(60); 1424 Layout->addWidget(Line, i%9, 0+i/9, 1, 1); 1425 Plot->AddService("Feedback/Sigma", i); 1426 1427 Line = new EddLineDisplay("Feedback/Target", i); 1428 Line->setMaximumWidth(60); 1429 Layout->addWidget(Line, i%9+10, 0+i/9, 1, 1); 1430 1431 Line = new EddLineDisplay("Feedback/Response", i); 1432 Line->setMaximumWidth(60); 1433 Layout->addWidget(Line, i%9+20, 0+i/9, 1, 1); 1434 } 1435 Layout->addWidget(Plot, 0, 4, 30, 12); 1436 1437 M->show(); 1438 } 1439 1440 // 1441 // FADctrl page 1442 // 1443 TP_FADctrl::TP_FADctrl() { 1444 1445 QString Board; 1446 1447 QGridLayout *Layout = new QGridLayout(this); 1448 setAttribute(Qt::WA_DeleteOnClose); 1449 EddLineDisplay *Line; 1450 1451 Line = new EddLineDisplay("FADctrl/Message"); 1452 Line->setMaximumWidth(200); 1453 Layout->addWidget(Line, 0, 0, 1, 3); 1454 1455 EddCommand *Command = new EddCommand("FADctrl/Command"); 1456 Layout->addWidget(Command, 1, 0, 1, 4); 1457 1458 EddText *Text = new EddText("FADctrl/ConsoleOut", true); 1459 Text->setFixedWidth(400); 1460 Layout->addWidget(Text, 2, 0, 4, 4); 1461 1462 for (int i=0; i<10; i++) { 1463 Board = Board.sprintf("FADctrl/Board%.2d/", i); 1464 Line = new EddLineDisplay(Board+"Server"); 1465 Layout->addWidget(Line, i+7, 0, 1, 1); 1466 Line = new EddLineDisplay(Board+"BoardID"); 1467 Layout->addWidget(Line, i+7, 1, 1, 1); 1468 Line = new EddLineDisplay(Board+"Temperature", 0); 1469 Layout->addWidget(Line, i+7, 2, 1, 1); 1470 Line = new EddLineDisplay(Board+"Temperature", 1); 1471 Layout->addWidget(Line, i+7, 3, 1, 1); 1472 Line = new EddLineDisplay(Board+"Temperature", 2); 1473 Layout->addWidget(Line, i+7, 4, 1, 1); 1474 Line = new EddLineDisplay(Board+"Temperature", 3); 1475 Layout->addWidget(Line, i+7, 5, 1, 1); 1476 } 1477 } 1478 1479 // 1480 // Event scope page 1481 // 1482 TP_DAQ::TP_DAQ() { 1483 1484 EddLineDisplay *Line; 1485 1486 setAttribute(Qt::WA_DeleteOnClose); 1487 QGridLayout *Layout = new QGridLayout(this); 1488 1489 // Run-related information 1490 Line = new EddLineDisplay("drsdaq/RunNumber"); 1491 Line->setMaximumWidth(100); 1492 Layout->addWidget(Line, 0, 1, 1, 1); 1493 Line = new EddLineDisplay(DRSBoard+"/EventNumber"); 1494 Line->setMaximumWidth(100); 1495 Layout->addWidget(Line, 0, 2, 1, 1); 1496 Line = new EddLineDisplay("drsdaq/RunSizeMB"); 1497 Line->setMaximumWidth(100); 1498 Layout->addWidget(Line, 0, 3, 1, 1); 1499 Line = new EddLineDisplay("drsdaq/FileSizeMB"); 1500 Line->setMaximumWidth(100); 1501 Layout->addWidget(Line, 0, 4, 1, 1); 1502 Line = new EddLineDisplay("drsdaq/FileName"); 1503 Line->setMaximumWidth(200); 1504 Layout->addWidget(Line, 0, 5, 1, 1); 1505 1506 // Message service 1507 Line = new EddLineDisplay(DRSBoard+"/Message"); 1508 Line->setMaximumWidth(200); 1509 Layout->addWidget(Line, 1, 1, 1, 2); 1510 1511 // Event scope 1512 Scope = new EventScope; 1513 Scope->setMinimumWidth(700); 1514 1515 // Text boxes for run and event header 1516 RunHeaderDisplay = new QPlainTextEdit(); 1517 EventHeaderDisplay = new QPlainTextEdit(); 1518 RunHeaderDisplay->setReadOnly(true); 1519 EventHeaderDisplay->setReadOnly(true); 1520 1521 // Tab widget 1522 QTabWidget *TabWidget = new QTabWidget(); 1523 TabWidget->addTab(Scope, "&Signals"); 1524 TabWidget->addTab(RunHeaderDisplay, "&Run Header"); 1525 TabWidget->addTab(EventHeaderDisplay, "&Event Header"); 1526 Layout->addWidget(TabWidget, 2, 1, 6, 5); 1527 1528 // Channel number 1529 Channel = new QSpinBox; 1530 connect(Channel, SIGNAL(valueChanged(int)), SLOT(UpdateScope(int))); 1531 Channel->setToolTip("DRS channel number"); 1532 1533 // Chip number 1534 Chip = new QSpinBox; 1535 connect(Chip, SIGNAL(valueChanged(int)), SLOT(UpdateScope(int))); 1536 Chip->setToolTip("DRS chip number"); 1537 1538 // Board number 1539 Board = new QSpinBox; 1540 connect(Board, SIGNAL(valueChanged(int)), SLOT(UpdateScope(int))); 1541 Board->setToolTip("DRS board number"); 1542 1543 // Pixel ID 1544 PixelID = new QSpinBox; 1545 PixelID->setMaximumWidth(60); 1546 PixelID->setRange(-1, 9999); 1547 PixelID->setSpecialValueText("n/a"); 1548 connect(PixelID, SIGNAL(valueChanged(int)), SLOT(TranslatePixelID(int))); 1549 PixelID->setToolTip("Pixel identification"); 1550 1551 // Layout of pixel addressing widgets 1552 QFormLayout *FormLayout = new QFormLayout(); 1553 FormLayout->setRowWrapPolicy(QFormLayout::WrapAllRows); 1554 FormLayout->addRow("Board", Board); 1555 FormLayout->addRow("Chip", Chip); 1556 FormLayout->addRow("Channel", Channel); 1557 FormLayout->addRow("Pixel ID", PixelID); 1558 Layout->addLayout(FormLayout, 0, 0, 4, 1); 1559 1560 // Add trace permanently 1561 QPushButton *Button = new QPushButton("Keep trace"); 1562 Button->setToolTip("Keep trace in display"); 1563 Button->setMaximumWidth(80); 1564 Layout->addWidget(Button, 4, 0); 1565 connect(Button, SIGNAL(clicked()), SLOT(KeepCurrent())); 1566 1567 // Stop/start 1568 StartStopButton = new QPushButton("Stop"); 1569 StartStopButton->setToolTip("Start/stop display"); 1570 StartStopButton->setMaximumWidth(80); 1571 StartStopButton->setCheckable(true); 1572 QPalette Palette = StartStopButton->palette(); 1573 Palette.setColor(QPalette::ButtonText, Qt::blue); 1574 Palette.setColor(QPalette::Button, Qt::green); 1575 StartStopButton->setPalette(Palette); 1576 StartStopButton->setFont(QFont("Times", 10, QFont::Bold)); 1577 Layout->addWidget(StartStopButton, 6, 0); 1578 connect(StartStopButton, SIGNAL(toggled(bool)), SLOT(StartStop(bool))); 1579 1580 // Button to show event display 1581 QPushButton *PixDisplay = new QPushButton("Pixel display"); 1582 PixDisplay->setFont(QFont("Times", 10, QFont::Bold)); 1583 PixDisplay->setToolTip("Show event display window"); 1584 PixDisplay->setMaximumWidth(80); 1585 Layout->addWidget(PixDisplay, 7, 0); 1586 connect(PixDisplay, SIGNAL(clicked()), SLOT(ShowPixelDisplay())); 1587 1588 // Event display window 1589 Display = new QWidget(); 1590 Display->setWindowTitle("Edd - Event display"); 1591 1592 Pixel = new QPushButton *[MAXPIXEL]; 1593 int Count = 0; 1594 double x,y; 1595 1596 for (int ix=-22; ix<=22; ix++) for (int iy=-19; iy<=20; iy++) { 1597 if (Count == MAXPIXEL) break; 1598 1599 x = ix*0.866; 1600 y = iy - (ix%2==0 ? 0.5:0); 1601 if ((pow(x,2)+pow(y,2) >= 395) && !(abs(ix)==22 && iy==7)) continue; 1602 1603 Pixel[Count] = new QPushButton(Display); 1604 Pixel[Count]->setAutoFillBackground(true); 1605 Pixel[Count]->setGeometry((int) (x*12.5 + 250), (int) (y*12.5 + 250), 10, 10); 1606 Pixel[Count]->show(); 1607 Count++; 1608 } 1609 1610 connect(Scope, SIGNAL(RunHeaderChanged(QString)), RunHeaderDisplay, SLOT(setPlainText(QString))); 1611 connect(Scope, SIGNAL(EventHeaderChanged(QString)), EventHeaderDisplay, SLOT(setPlainText(QString))); 1612 1613 StartStop(false); 1614 connect(Scope, SIGNAL(PixelData(QVector<double>)), SLOT(SetPixelData(QVector<double>))); 1615 1616 // Call to get initial pixel ID correct 1617 UpdateScope(0); 1618 } 1619 1620 TP_DAQ::~TP_DAQ() { 1621 1622 delete[] Pixel; 1623 } 1624 1625 // Translate pixel ID to board, chip, channel 1626 void TP_DAQ::TranslatePixelID(int ID) { 1627 1628 // setValue() below will call UpdateScope() through signal, therefore need to store numbers here 1629 unsigned int BoardNo = Scope->Pixel_to_FPAboard(ID); 1630 unsigned int PatchNo = Scope->Pixel_to_FPApatch(ID); 1631 unsigned int PixelNo = Scope->Pixel_to_FPApixel(ID); 1632 1633 if (BoardNo == Scope->PM_ERROR_CODE) PixelID->setValue(-1); 1634 else { 1635 Board->setValue(BoardNo); 1636 Chip->setValue(PatchNo); 1637 Channel->setValue(PixelNo); 1638 } 1639 } 1640 1641 // Keep current trace 1642 void TP_DAQ::KeepCurrent() { 1643 1644 Scope->AddTrace(Board->value(), Chip->value(), Channel->value()); 1645 } 1646 1647 // Start/stop event acquisition 1648 void TP_DAQ::StartStop(bool State) { 1649 1650 Scope->SetActive(!State); 1651 StartStopButton->setText(State ? "Start" : "Stop"); 1652 if (!State) connect(Handler, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), Scope, SLOT(Update(QString, int, QByteArray, QString, QString))); 1653 else disconnect(Handler, SIGNAL(YEP(QString, int, QByteArray, QString, QString)), Scope, SLOT(Update(QString, int, QByteArray, QString, QString))); 1654 } 1655 1656 // Update event scope 1657 void TP_DAQ::UpdateScope(int) { 1658 1659 // Update pixel ID 1660 PixelID->setValue(Scope->FPA_to_Pixel(0, Board->value(), Chip->value(), Channel->value())); 1661 if (PixelID->value() == (int) Scope->PM_ERROR_CODE) PixelID->setValue(-1); 1662 1663 // Update first trace 1664 Scope->UpdateFirst(Board->value(), Chip->value(), Channel->value()); 1665 } 1666 1667 // Show/hide pixel display 1668 void TP_DAQ::ShowPixelDisplay() { 1669 1670 Display->show(); 1671 Display->raise(); 1672 } 1673 1674 void TP_DAQ::SetPixelData(QVector<double> Data) { 1675 1676 QwtLinearColorMap Map; 1677 1678 for (int i=0; i<Data.size(); i++) { 1679 Pixel[i]->setPalette(QPalette(Map.color(QwtDoubleInterval(300, 400), Data[i]))); 1680 } 1681 } 1682 1683 1684 // 1685 // Evidence page 1686 // 1687 TP_Evidence::TP_Evidence() { 1688 1689 setAttribute(Qt::WA_DeleteOnClose); 1690 QGridLayout *Layout = new QGridLayout(this); 1691 EddLineDisplay *Line; 1692 EddText *Text; 1693 1694 Line = new EddLineDisplay("Alarm/Message"); 1695 Line->setMaximumWidth(200); 1696 Layout->addWidget(Line, 0, 0, 1, 2); 1697 1698 QPushButton *Button = new QPushButton(); 1699 Button->setText("ON/OFF"); 1700 Button->setCheckable(true); 1701 connect(Button, SIGNAL(toggled(bool)), SLOT(ToggleAlarm(bool))); 1702 Layout->addWidget(Button, 0, 3, 1, 1); 1703 1704 Line = new EddLineDisplay("Alarm/MasterAlarm"); 1705 Layout->addWidget(Line, 0, 1, 1, 1); 1706 1707 Text = new EddText("Alarm/Summary", true); 1708 Text->Accumulate = false; 1709 Text->setMaximumWidth(200); 1710 Text->setMaximumHeight(150); 1711 Layout->addWidget(Text, 1, 0, 1, 2); 1712 1713 Line = new EddLineDisplay("DColl/Message"); 1714 Line->setMaximumWidth(200); 1715 Layout->addWidget(Line, 3, 0, 1, 2); 1716 1717 Line = new EddLineDisplay("DColl/DataSizeMB"); 1718 Layout->addWidget(Line, 4, 0, 1, 1); 1719 1720 Line = new EddLineDisplay("DColl/LogSizeMB"); 1721 Layout->addWidget(Line, 4, 1, 1, 1); 1722 1723 Line = new EddLineDisplay("DColl/CurrentFile"); 1724 Line->setMaximumWidth(400); 1725 Layout->addWidget(Line, 5, 0, 1, 3); 1726 1727 Line = new EddLineDisplay("Config/Message"); 1728 Line->setMaximumWidth(200); 1729 Layout->addWidget(Line, 6, 0, 1, 2); 1730 1731 Line = new EddLineDisplay("Config/ModifyTime"); 1732 Line->setMaximumWidth(200); 1733 Line->ShowAsTime = true; 1734 Layout->addWidget(Line, 7, 0, 1, 1); 1735 1736 Button = new QPushButton(); 1737 Button->setText("eLogBook"); 1738 connect(Button, SIGNAL(released()), SLOT(StartELog())); 1739 Layout->addWidget(Button, 6, 1, 1, 1); 1740 1741 Button = new QPushButton(); 1742 Button->setText("Start DIM browser"); 1743 connect(Button, SIGNAL(released()), SLOT(StartDIMBrowser())); 1744 Layout->addWidget(Button, 7, 1, 1, 1); 1745 1746 Line = new EddLineDisplay("Edd/Rate_kBSec"); 1747 Layout->addWidget(Line, 8, 0, 1, 1); 1748 } 1749 1750 // Toggle state of Alarm server 1751 void TP_Evidence::ToggleAlarm(bool State) { 1752 1753 if (State) DimClient::sendCommandNB((char *) "Alarm/Switch", (char *) "off"); 1754 else DimClient::sendCommandNB((char *) "Alarm/Switch", (char *) "on"); 1755 } 1756 1757 // Start DIM Browser 1758 void TP_Evidence::StartDIMBrowser() { 1759 1760 QProcess::startDetached("did", QStringList(), QString(getenv("DIMDIR"))+"/linux/"); 1761 } 1762 1763 // Start eLogBook 1764 void TP_Evidence::StartELog() { 1765 1766 QProcess::startDetached("firefox http://fact.ethz.ch/FACTelog/index.jsp"); 1767 } 1768 1769 1770 //-------------------------------------------------------------------- 1771 //*************************** Main GUI ******************************* 1772 //-------------------------------------------------------------------- 1773 // 1774 // All widgets have ultimately Central as parent. 1775 // 1776 GUI::GUI() { 1777 1778 Handler = new EddDim(); 1779 1780 // Set features of main window 1781 Central = new QWidget(this); 1782 setCentralWidget(Central); 1783 setStatusBar(new QStatusBar(this)); 1784 setWindowTitle("Edd - Evidence Data Display"); 1785 1786 // Arrangement in tabs 1787 TabWidget = new QTabWidget(Central); 1788 TabWidget->setTabsClosable(true); 1789 connect(TabWidget, SIGNAL(tabCloseRequested(int)), SLOT(DetachTab(int))); 1790 TabWidget->addTab(new TP_DAQ, "Event scope " + DRSBoard); 1791 TabWidget->addTab(new TP_FADctrl, "FADctrl"); 1792 TabWidget->addTab(new TP_Bias, "Bias"); 1793 TabWidget->addTab(new TP_Feedback, "Feedback"); 1794 TabWidget->addTab(new TP_Environment, "Environment"); 1795 TabWidget->addTab(new TP_Evidence, "Evidence"); 1796 1797 // Menu bar 1798 QMenu* Menu = menuBar()->addMenu("&Menu"); 1799 Menu->addAction("New history plot", this, SLOT(MenuNewHistory())); 1800 Menu->addSeparator(); 1801 Menu->addAction("About", this, SLOT(MenuAbout())); 1802 Menu->addSeparator(); 1803 QAction* QuitAction = Menu->addAction("Quit", qApp, SLOT(quit())); 1804 QuitAction->setShortcut(Qt::CTRL + Qt::Key_Q); 1805 1806 // Show main window 1807 resize(TabWidget->sizeHint()*1.1); 1808 show(); 1809 1810 // Set timer to regularly check the master alarm 1811 QTimer *Timer = new QTimer(this); 1812 connect(Timer, SIGNAL(timeout()), this, SLOT(CheckAlarm())); 1813 Timer->start(5000); 1814 } 1815 1816 GUI::~GUI() { 1817 delete Central; 1818 } 1819 1820 1821 void GUI::MenuAbout() { 1822 QString Rev(SVN_REVISION); 1823 Rev.remove(0,1).chop(2); 1824 1825 QMessageBox::about(this, "About Edd","Evidence Data Display\n\n" 1826 "Written by Oliver Grimm, IPP, ETH Zurich\n" 1827 "This version compiled "__DATE__" ("+Rev+")\n\n" 1828 "Graphical user interface implemented with Qt and Qwt.\n" 1829 "Evidence control system based on DIM (http://dim.web.cern.ch).\n\n" 1830 "Comments to oliver.grimm@phys.ethz.ch."); 1831 } 1832 1833 // Open request for new history plot 1834 void GUI::MenuNewHistory() { 1835 1836 QStringList List; 1837 char *Name, *Format; 1838 int Type; 1839 bool OK; 1840 1841 // Find all DIM services and sort 1842 getServices("*"); 1843 while ((Type = getNextService(Name, Format)) != 0) { 1844 if (Type==DimSERVICE) List.append(Name); 1845 } 1846 List.sort(); 1847 1848 // Open dialog and open history window 1849 QString Result = QInputDialog::getItem(this, "Edd Request", 1850 "Enter DIM service name", List, 0, true, &OK); 1851 if (OK && !Result.isEmpty()) { 1852 Result = Result.trimmed(); 1853 QWidget *Hist = OpenHistory(Result.toAscii().data(), 0); 1854 if (Hist != NULL) Hist->show(); 1855 } 1856 } 1857 1858 // Open tab as separate window 1859 void GUI::DetachTab(int Tab) { 1860 1861 QWidget *W = NULL; 1862 QMainWindow *M = new QMainWindow; 1863 1864 M->setCentralWidget(new QWidget(M)); 1865 M->setStatusBar(new QStatusBar(M)); 1866 1867 switch(Tab) { 1868 case 0: W = new TP_DAQ; break; 1869 case 1: W = new TP_Bias; break; 1870 case 2: W = new TP_Feedback; break; 1871 case 3: W = new TP_Environment; break; 1872 case 4: W = new TP_Evidence; break; 1873 default: break; 1874 } 1875 1876 if (W == NULL) { 1877 delete M->centralWidget(); 1878 delete M; 1879 return; 1880 } 1881 1882 W->setParent(M); 1883 M->resize(size()); 1884 M->setWindowTitle("Edd - " + TabWidget->tabText(Tab)); 1885 M->show(); 1886 } 1887 1888 // Check alarm level and if Alarm server is alive 1889 void GUI::CheckAlarm() { 1890 1891 static int WarnedLevel = 0; 1892 static bool AlarmServerWarned = false; 1893 1894 // === Check service Alarm/MasterAlarm === 1895 DimCurrentInfo MasterAlarm("Alarm/MasterAlarm", -1); 1896 1897 if (MasterAlarm.getInt() > WarnedLevel) { 1898 QSound::play(QApplication::applicationDirPath() + "/Error.wav"); 1899 1900 // Construct warning message box 1901 QMessageBox Box; 1902 Box.setWindowTitle("Edd Alarm"); 1903 Box.setText("Service 'Alarm/MasterAlarm' is at " + QString::number(MasterAlarm.getInt())); 1904 Box.setInformativeText("Warn again for the same alarm level?"); 1905 Box.setIcon(QMessageBox::Warning); 1906 Box.setStandardButtons(QMessageBox::Yes | QMessageBox::No); 1907 Box.setDefaultButton(QMessageBox::No); 1908 1909 // Check if user wants to reset warned level 1910 if (Box.exec() == QMessageBox::Yes) WarnedLevel = 0; 1911 else WarnedLevel = MasterAlarm.getInt(); 1912 } 1913 1914 // If MasterAlam decreased, lower also warned level 1915 if (MasterAlarm.getInt() < WarnedLevel && MasterAlarm.getInt() >=0 ) WarnedLevel = MasterAlarm.getInt(); 1916 1917 // === Check Alarm server === 1918 DimCurrentInfo ServerList("DIS_DNS/SERVER_LIST", NO_LINK); 1919 std::string Result = EvidenceServer::ToString((char *) "C", ServerList.getData(), ServerList.getSize()); 1920 1921 // Warn if SERVER_LIST does not contain alarm server 1922 if (Result.find("Alarm@") == std::string::npos && !AlarmServerWarned) { 1923 QMessageBox Box; 1924 Box.setWindowTitle("Edd Alarm"); 1925 Box.setText("Alarm server is unavailable or in error"); 1926 Box.setIcon(QMessageBox::Critical); 1927 Box.setStandardButtons(QMessageBox::Ok); 1928 Box.setDefaultButton(QMessageBox::Ok); 1929 Box.exec(); 1930 1931 AlarmServerWarned = true; 1932 } 1933 1934 if (Result.find("Alarm@") != std::string::npos) AlarmServerWarned = false; 1935 } 1936 1937 // Quit application when clicking close button on window 1938 void GUI::closeEvent(QCloseEvent *) { 1939 1940 qApp->quit(); 1941 } 1942 1943 1944 //**************************** Main program *************************** 1945 int main(int argc, char *argv[]) { 1946 1947 if (argc > 1) DRSBoard = "FADctrl"; 1948 1949 // Make RPC to get pixelmap 1950 DimRpcInfo RPC((char *) "ConfigRequest", (char *) ""); 1951 RPC.setData((char *) "Misc PixelMap"); 1952 PixelMapText = std::string(RPC.getString(), RPC.getSize()); 1953 1954 QApplication app(argc, argv); 1955 GUI MainWindow; 1956 1957 return app.exec(); 1958 } 1018 M->setWindowTitle(WindowName); 1019 1020 L = new QGridLayout(M->centralWidget()); 1021 1022 connect(this, SIGNAL(pressed()), SLOT(Toggle())); 1023 } 1024 1025 // Show or hide window 1026 void EddWindow::Toggle() { 1027 1028 if (M->isVisible()) M->hide(); 1029 else M->show(); 1030 } 1031 1032 // Return layout 1033 QGridLayout *EddWindow::Layout() { 1034 1035 return L; 1036 } 1037 1038 // Return window 1039 QMainWindow *EddWindow::Window() { 1040 1041 return M; 1042 } -
fact/Evidence/GUI.h
r10262 r10280 1 #ifndef EDD_H_SEEN2 #define EDD_H_SEEN1 #ifndef GUI_H_SEEN 2 #define GUI_H_SEEN 3 3 4 4 #include <QtGui> … … 26 26 #include "dic.hxx" 27 27 #include "Evidence.h" 28 #include "RawDataCTX.h"29 #include "PixelMap.h"30 31 #define SVN_REVISION "$Revision$"32 28 33 29 const QColor EddPlotBackgroundColor(Qt::yellow); … … 258 254 }; 259 255 260 // 261 // 262 // ====== FACT specific part ====== 263 // 264 // 265 266 // Event oscilloscope 267 class EventScope: public EddBasePlot, public PixelMap { 268 Q_OBJECT 269 270 private: 271 struct ItemDetails { 272 unsigned int Board, Chip, Channel; 273 QwtPlotCurve *Signal; 274 QwtPlotMarker *Trigger; 275 }; 276 QList<struct ItemDetails> List; 277 278 QString Name; 279 RawDataCTX *RD; 280 CTX_ErrCode ErrCode; 281 QAction *PhysPipeAction; 282 QAction *PersistanceAction; 283 FILE *Tmpfile; 284 285 public: 286 EventScope(QWidget * = NULL); 287 ~EventScope(); 288 289 void UpdateFirst(int, int, int); 290 void AddTrace(int, int, int); 291 void SetActive(bool); 292 QString ToPixel(unsigned int, unsigned int, unsigned int, unsigned int); 293 294 private slots: 295 void Update(QString, int, QByteArray, QString, QString); 296 void PlotTraces(); 297 void DeleteCurve(QwtPlotCurve *); 298 299 signals: 300 void RunHeaderChanged(QString); 301 void EventHeaderChanged(QString); 302 void PixelData(QVector<double>); 303 }; 304 305 306 // Tab page classes 307 class TP_Environment: public QWidget { 308 Q_OBJECT 309 310 public: 311 TP_Environment(); 312 }; 313 314 class TP_Bias: public QWidget { 315 Q_OBJECT 316 317 private slots: 318 void BiasCurrents(); 319 320 public: 321 TP_Bias(); 322 }; 323 324 class TP_FADctrl: public QWidget { 325 Q_OBJECT 326 327 public: 328 TP_FADctrl(); 329 }; 330 331 class TP_Feedback: public QWidget { 332 Q_OBJECT 333 334 private slots: 335 void FeedbackDetails(); 336 337 public: 338 TP_Feedback(); 339 }; 340 341 class TP_DAQ: public QWidget { 342 Q_OBJECT 343 static const int MAXPIXEL = 1440; 344 345 private: 346 EventScope *Scope; 347 QPlainTextEdit *RunHeaderDisplay, *EventHeaderDisplay; 348 349 QSpinBox *Channel, *Chip, *Board, *PixelID; 350 QFormLayout *FormLayout; 351 QWidget *Display; 352 QPushButton **Pixel; 353 QPushButton *StartStopButton; 354 355 private slots: 356 void TranslatePixelID(int); 357 void UpdateScope(int); 358 void KeepCurrent(); 359 void StartStop(bool); 360 void ShowPixelDisplay(); 361 void SetPixelData(QVector<double>); 362 363 public: 364 TP_DAQ(); 365 ~TP_DAQ(); 366 }; 367 368 class TP_Evidence: public QWidget { 369 Q_OBJECT 370 371 private slots: 372 void ToggleAlarm(bool); 373 void StartDIMBrowser(); 374 void StartELog(); 375 376 public: 377 TP_Evidence(); 378 }; 379 380 381 // Main window class 382 class GUI: public QMainWindow, public DimBrowser { 383 Q_OBJECT 384 385 private: 386 QWidget *Central; 387 QTabWidget *TabWidget; 388 389 void closeEvent(QCloseEvent *); 390 391 public: 392 GUI(); 393 ~GUI(); 394 395 private slots: 396 void MenuAbout(); 397 void MenuNewHistory(); 398 void DetachTab(int); 399 void CheckAlarm(); 256 // Open new window 257 class EddWindow: public QPushButton { 258 Q_OBJECT 259 260 QMainWindow *M; 261 QGridLayout *L; 262 263 public: 264 EddWindow(QString, QString); 265 QGridLayout *Layout(); 266 QMainWindow *Window(); 267 private slots: 268 void Toggle(); 400 269 }; 401 270
Note:
See TracChangeset
for help on using the changeset viewer.