/********************************************************************\ Main class of FADCtrl If outputting text with PrintMessage(), a '\r' is used on the console automatically if the given text ends with '\n'. Comment 19/10/2010: It is assumed that boolean access is an atomic operation. \********************************************************************/ #include "FAD.h" using namespace std; static const struct CL_Struct { const char *Name; void (FAD::*CommandPointer)(); bool NeedIdle; unsigned int MinNumParameter; const char *Parameters; const char *Help; } CommandList[] = {{"board", &FAD::cmd_board, true, 1, "<[+|-]range|none>" ,"Activate or deactivate board(s)"}, {"status", &FAD::cmd_status, false, 0, "[range]", "Show board status information"}, {"domino", &FAD::cmd_domino, true, 1, "", "Switch Domino wave"}, {"dwrite", &FAD::cmd_dwrite, true, 1, "", "Set DWRITE"}, {"phase", &FAD::cmd_phase, true, 1, "", "Adjust ADC phase (in 'steps')"}, {"srclk", &FAD::cmd_srclk, true, 1, "", "Set SRCLK"}, {"sclk", &FAD::cmd_sclk, true, 1, "", "Set SCLK"}, {"trigger", &FAD::cmd_trigger, false, 0, "[n|cont [rate]|stop|enable|disable]", "Issue software triggers"}, {"drsreset", &FAD::cmd_drsreset, true, 1, "", "Set DRS reset line"}, {"reset", &FAD::cmd_reset, true, 0, "", "Reset internal trigger counter"}, {"runnumber", &FAD::cmd_runnumber, true, 1, "", "Set runnumber"}, {"roi", &FAD::cmd_roi, true, 2, " ", "Set ROI to for channel range to value"}, {"dac", &FAD::cmd_dac, true, 2, " ", "Set DAC numbers in range to value"}, {"execute", &FAD::cmd_execute, true, 0, "", "confirm FAD configuration settings, e.g. DAC, ROI, run#"}, {"address", &FAD::cmd_address, true, 2, " ", "Set addresses in range to value"}, {"send", &FAD::cmd_send, true, 1, "", "Send arbitrary data to board"}, {"take", &FAD::cmd_take, true, 1, " ", "Start run with n events (n=0 -> unlimited)"}, {"acalib", &FAD::cmd_acalib, true, 0, "[n|inval|file]", "Perform or read amplitude calibration (n events)"}, //{"wmode", &FAD::cmd_wmode, 0, "", "Domino wave running or stopped during read out"}, //{"rmode", &FAD::cmd_rmode, 0, "", "Readout start at first bin or stop position (DRS4)"}, //{"dmode", &FAD::cmd_dmode, 0, "", "Domino wave single shot or continuous"}, {"stop", &FAD::cmd_stop, false, 0, "", "Stop current operation/run"}, {"update", &FAD::cmd_update, false, 1, "", "Minimum delay between updates to DIM event service"}, {"socketmode", &FAD::cmd_socketmode, true, 1, "", "Choose which Sockets are used for data transmission"}, {"dynrange", &FAD::cmd_dynrange, true, 0, "", "Determine dynamic range using calibration DAC"}, {"exit", &FAD::cmd_exit, false, 0, "", "Exit program"}, {"help", &FAD::cmd_help, false, 0, "", "Print help"}}; // ------------------------------------ // ***** Constructor/Destructor ***** // ------------------------------------ // // Constructor // FAD::FAD(std::vector List): EvidenceServer(SERVER_NAME) { // Initialization ConsoleText = NULL; MainThread = pthread_self(); Mode = idle; Datafile = -1; EventUpdateDelay = atof(GetConfig("EventUpdateDelay", "2").c_str()); // Create pipe for data exchange if (pipe(Pipe) == -1) Message(FATAL, "pipe() failed in FAD::FAD() (%s)", strerror(errno)); // DIM console service used in PrintMessage() ConsoleOut = new DimService(SERVER_NAME"/ConsoleOut", (char *) ""); // Initialise configuration information (later non-blocking access in commandHandler()) GetConfig("CalibTempDiffWarn", "0"); GetConfig("CalibFreqDiffWarn", "0"); // Construct boards if (List.empty()) BoardList = Tokenize(GetConfig("BoardList")); else BoardList = List; for (unsigned int i=0; i Init = Tokenize(GetConfig("InitSequence", ""), ";"); for (unsigned int i=0; igetString(), *Start; // Ignore empty or illegal strings if (getCommand()->getSize() == 0) return; if( *(Command+getCommand()->getSize()-1) != '\0' || strlen(Command) == 0) return; // Shell command if (*Command == '.') { if (system(Command+1) == -1) PrintMessage("Error with system() call\n"); return; } // Parse command into tokens Parameter.clear(); while(true) { while (isspace(*Command)) Command++; // Ignore initial white spaces if(*Command=='\0') break; if (*Command == '\"') { Start = ++Command; while(*Command!='\"' && *Command!='\0') Command++; } else { Start = Command; while(!isspace(*Command) && *Command!='\0') Command++; } if(*Command != '\0') *Command++ = '\0'; Parameter.push_back(Start); } // Search for command in command list for(unsigned int n=0; n*CommandList[n].CommandPointer)(); return; } } // Command not found PrintMessage("Unknown command '%s'\n", Parameter[0].c_str()); } // // Switch SRCLK // void FAD::cmd_srclk() { for (unsigned int i=0; iSend(CMD_SRCLK_ON); else if (Match(Parameter[1],"off")) Boards[i]->Send(CMD_SRCLK_OFF); else { PrintUsage(); return; } } } // // Reset internal trigger // void FAD::cmd_reset() { for (unsigned int i=0; iSend(CMD_RESET_TRIGGER_ID); } // // Set DRS reset line // void FAD::cmd_drsreset() { for (unsigned int i=0; iSend(CMD_DRS_RST_LOW); else if (Match(Parameter[1], "high")) Boards[i]->Send(CMD_DRS_RST_HIGH); else { PrintUsage(); return; } } } // // Execute: Confirm new ROI, DAC or what ever settings // void FAD::cmd_execute() { for (unsigned int i=0; iSend(CMD_Execute); } // // Set run number // void FAD::cmd_runnumber() { unsigned short Buffer[4] = {0}; int Num; if (!ConvertToInt(Parameter[1], &Num)) { PrintMessage("Error, incorrect parameter for run number\n"); return; } Buffer[0] = htons(CMD_Write | ADDR_RUNNUMBER); Buffer[1] = htons( (unsigned short)( ((unsigned int)Num) >> 16 ) ); Buffer[2] = htons(CMD_Write | (ADDR_RUNNUMBER + 1) ); Buffer[3] = htons( (unsigned short)Num ); for (unsigned int i=0; iSend(Buffer, sizeof(Buffer)); Boards[i]->Send(CMD_Execute); } } // // Switch socket mode // "com" - command mode (only socket 0 is used for event data transmission) // "daq" - daq mode (only sockets 1 - 7 are used) void FAD::cmd_socketmode() { for (unsigned int i=0; iSend(CMD_mode_command); else if (Match(Parameter[1],"daq")) Boards[i]->Send(CMD_mode_all_sockets); else { PrintUsage(); return; } } } // // Switch SCLK // void FAD::cmd_sclk() { for (unsigned int i=0; iSend(CMD_SCLK_ON); else if (Match(Parameter[1],"off")) Boards[i]->Send(CMD_SCLK_OFF); else { PrintUsage(); return; } } } // // Switch Domino wave // void FAD::cmd_domino() { for (unsigned int i=0; iSend(CMD_DENABLE); else if (Match(Parameter[1],"off")) Boards[i]->Send(CMD_DDISABLE); else { PrintUsage(); return; } } } // // Switch DWRITE // void FAD::cmd_dwrite() { for (unsigned int i=0; iSend(CMD_DWRITE_RUN); else if (Match(Parameter[1],"off")) Boards[i]->Send(CMD_DWRITE_STOP); else { PrintUsage(); return; } } } // // Issue soft trigger // void FAD::cmd_trigger() { int Num; for (unsigned int i=0; iSend(CMD_Trigger); else if (ConvertToInt(Parameter[1], &Num)) { for (int j=0; jSend(CMD_Trigger); } else if (Match(Parameter[1],"continuous")) { Boards[i]->Send(CMD_Trigger_C); if (Parameter.size() == 3 && ConvertToInt(Parameter[2], &Num)) { if (Num == 0) Boards[i]->Send(CMD_Trigger_S); else { //Boards[i]->Send(0x2100 + (unsigned char) (1000.0/Num/12.5)); Boards[i]->Send( CMD_Write | BADDR_CONT_TRIGGER_TIME ); Boards[i]->Send((unsigned short) (1000.0/Num/12.5)); } } } else if (Match(Parameter[1],"stop")) Boards[i]->Send(CMD_Trigger_S); else if (Match(Parameter[1],"enable")) Boards[i]->Send(CMD_TRIGGERS_ON); else if (Match(Parameter[1],"disable")) Boards[i]->Send(CMD_TRIGGERS_OFF); else { PrintUsage(); break; } } } // // Set DAC // void FAD::cmd_dac() { int Value; struct Range R = {0, NDAC-1}; unsigned short Buffer[2*NDAC] = {0}; // Check ranges if(!ConvertToRange(Parameter[1], R)) { PrintMessage("Error, DAC number out of range.\n"); return; } if (!ConvertToInt(Parameter[2], &Value) || Value<0 || Value>MAX_DACVAL) { PrintMessage("Error, DAC value out of range.\n"); return; } // Prepare command buffer for (int i=R.Min; i<=R.Max; i++) { Buffer[2*i] = htons(CMD_Write | (BADDR_DAC + i)); Buffer[2*i+1] = htons(Value); } // Send command buffer for (unsigned int i=0; iSend(Buffer, sizeof(Buffer)); Boards[i]->Send(CMD_Execute); } } // // Set region-of-interest // void FAD::cmd_roi() { int Value; struct Range R = {0, NChips*NChannels-1}; unsigned short Buffer[2*NChips*NChannels] = {0}; // Check ranges if (!ConvertToRange(Parameter[1], R)) { PrintMessage("Error, ROI number out of range.\n"); return; } if (!ConvertToInt(Parameter[2], &Value) || Value<0 || Value>MAX_ROIVAL) { PrintMessage("Error, ROI value out of range.\n"); return; } // Prepare command buffer for (int i=R.Min; i<=R.Max; i++) { Buffer[2*i] = htons(CMD_Write | (BADDR_ROI + i)); Buffer[2*i+1] = htons(Value); } // Send command buffer and enable triggers again for (unsigned int i=0; iSend(Buffer+R.Min*2, (R.Max-R.Min+1)*2*sizeof(unsigned short)); Boards[i]->Send(CMD_Execute); } } // // Set addresses to value // void FAD::cmd_address() { int Value; struct Range R = {0, MAX_ADDR}; unsigned short Buffer[2*MAX_ADDR] = {0}; // Check ranges if (!ConvertToRange(Parameter[1], R)) { PrintMessage("Error, address out of range.\n"); return; } if (!ConvertToInt(Parameter[2], &Value) || Value<0 || Value>MAX_VAL) { PrintMessage("Error, value out of range.\n"); return; } // Prepare command buffer for (int i=R.Min; i<=R.Max; i++) { Buffer[2*i] = htons(CMD_Write | i); Buffer[2*i+1] = htons(Value); } // Send command buffer for (unsigned int i=0; iSend(Buffer, 2*(R.Max-R.Min+1)*sizeof(unsigned short)); } } // // Set ADC phase // void FAD::cmd_phase() { int Value; if (!ConvertToInt(Parameter[1], &Value)) { PrintMessage("Error, illegal phase value\n"); return; } // Prepare command buffer unsigned short *Buffer = new unsigned short [abs(Value)]; for (int i=0; iSend(CMD_PS_RESET); if (Value < 0) Boards[i]->Send(CMD_PS_DIRDEC); else Boards[i]->Send(CMD_PS_DIRINC); Boards[i]->Send(Buffer, abs(Value)*sizeof(unsigned short)); } delete[] Buffer; } // // Send arbitrary data to board // void FAD::cmd_send() { int Value; if (!ConvertToInt(Parameter[1], &Value) || Value<0 || Value>MAX_VAL) { PrintMessage("Error, illegal value\n"); return; } for (unsigned int i=0; iSend(Value); } /* // Set Domino mode void FAD::cmd_dmode() { if (Match(Param[1],"continuous")) SetDOMINOMode(1); else if (Match(Param[1],"single")) SetDOMINOMode(0); else PrintUsage(); } // Set Domino readout mode void FAD::cmd_rmode() { if (Match(Param[1],"first")) SetDOMINOReadMode(0); else if (Match(Param[1],"stop")) SetDOMINOReadMode(1); else PrintUsage(); } // Set Domino wave mode void FAD::cmd_wmode() { if (Match(Param[1],"run")) SetDOMINOWaveMode(1); else if (Match(Param[1],"stop")) SetDOMINOWaveMode(0); else PrintUsage(); } */ // // Start data run // void FAD::cmd_take() { time_t Time = time(NULL); struct tm *T = localtime(&Time); char Filename[500]; double Temp; // Set number of requested events NumEventsRequested = atoi(Parameter[1].c_str()); NumEvents = 0; // Open file with rwx right for owner and group, never overwrite file snprintf(Filename, sizeof(Filename),"%s/%d%02d%02dT%02d%02d%02d.raw", Parameter[2].c_str(), T->tm_year+1900, T->tm_mon+1, T->tm_mday, T->tm_hour, T->tm_min, T->tm_sec); Datafile = open(Filename,O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); if(Datafile == -1) { PrintMessage("Error: Could not open file \"%s\" (%s)\n", Filename, strerror(errno)); return; } // Check conditions for run of all active boards float MaxTempDiff = atof(GetConfig("CalibTempDiffWarn").c_str()); float MaxFreqDiff = atof(GetConfig("CalibFreqDiffWarn").c_str()); for (unsigned int i=0; iActive) continue; if (Boards[i]->ACalib.Time == -1) PrintMessage("Warning: Amplitude calibration missing for board %d\n", i); else { Temp = 0; for (unsigned int j=0; jGetStatus().Temp[j] / NTemp; if (fabs(Boards[i]->ACalib.Temp-Temp) > MaxTempDiff) PrintMessage("Warning: Calibration to current temperature difference larger than %.1f K for board %d\n", MaxTempDiff, i); if (fabs(Boards[i]->ACalib.Frequency-Boards[i]->GetStatus().Frequency) > MaxFreqDiff) PrintMessage("Warning: Calibration to current frequency difference larger than %.1f GHz for board %d\n", MaxFreqDiff, i); } } // Start run Mode = datarun; Message(INFO, "Starting run with %d events, filename '%s'", NumEventsRequested, Filename); } // // Amplitude calibration // void FAD::cmd_acalib() { // Invalidate calibration? if (Parameter.size() == 2 && Match(Parameter[1], "invalidate")) { for (unsigned int i=0; iACalib.Time = -1; return; } // Read calibration data from file? if (Parameter.size() == 2 && !ConvertToInt(Parameter[1], &NumEventsRequested)) { FILE *File; struct FADBoard::CalibData Data; bool Found = false; // Open file if ((File = fopen(Parameter[1].c_str(), "r")) == NULL) { PrintMessage("Error opening file '%s'\n", Parameter[1].c_str()); return; } // Read data and check if it applies to any board while (fread(&Data, sizeof(Data), 1, File) == 1) { for (unsigned int i=0; iGetStatus().DNA) { PrintMessage("Found calibration for board %d - %s", i, ctime(&Data.Time)); Boards[i]->ACalib = Data; Found = true; } } if (!Found) PrintMessage("Did not find calibration data for any board\n"); //Close file if (fclose(File) != 0) PrintMessage("Could not close file '%s'\n", Parameter[1].c_str()); return; } // Reading calibration from file // Set number of events required for calibration if (Parameter.size()==1 || !ConvertToInt(Parameter[1], &NumEventsRequested) || NumEventsRequested<=0) { NumEventsRequested = DEFAULT_NUM_CALIB_EVENTS; } // Start calibration by setting mode Mode = acalib; Message(INFO, "Starting amplitude calibration run with 3x%d events", NumEventsRequested); } // // Determine dynamic range // void FAD::cmd_dynrange() { Mode = dynrange; Message(INFO, "Starting determination of dynamic range"); } // // Print status // void FAD::cmd_status() { int MinCount = std::numeric_limits::max(); unsigned int SlowestBoard = 0; // ==== Print board overview ==== if (Parameter.size() == 1) { // Count active board unsigned int Count = 0, Error = 0; for (unsigned int i=0; iActive) Count++; if (!Boards[i]->CommOK) Error++; if (Boards[i]->Active && Boards[i]->Count < MinCount) { MinCount = Boards[i]->Count; SlowestBoard = i; } } PrintMessage("\rTotal boards: %d (%d with communication errors)\n", Boards.size(), Error); // Print list of active boards PrintMessage("Active (%d)\t", Count); for (unsigned int i=0; iActive) PrintMessage(" %d%s", i, Boards[i]->CommOK ? "":"!"); } PrintMessage("\nInactive (%d) ", Boards.size()-Count); for (unsigned int i=0; iActive) PrintMessage(" %d%s", i, Boards[i]->CommOK ? "":"!"); } PrintMessage("\n"); // Print current mode if (Mode == idle) PrintMessage("Current mode is IDLE\n"); else if (Mode == acalib) PrintMessage("Current mode is ACALIB (3x%d events, slowest board %d has %d events)\n", NumEventsRequested, SlowestBoard, MinCount); else if (Mode == datarun) PrintMessage("Current mode is DATARUN (%d events requested, %d events taken)\n", NumEventsRequested, NumEvents); else if (Mode == dynrange) PrintMessage("Current mode is DYNRANGE\n"); return; } // ==== Print details for given range ==== struct Range R = {0, Boards.size()}; if (!ConvertToRange(Parameter[1], R)) { PrintMessage("Error, out of range.\n"); return; } for (int i=0; i<(int) Boards.size(); i++) { if (i R.Max) continue; PrintMessage("\nBOARD #%d (%sactive) IP %s Communication %s\n", i, Boards[i]->Active ? "":"in", Boards[i]->Name, Boards[i]->CommOK ? "OK":"ERROR"); // Calibration information if (Boards[i]->ACalib.Time == -1) PrintMessage("No amplitude calibration available\n"); else PrintMessage("Calibration data: Temperature %.1f Frequency %.2f Time %s" , Boards[i]->ACalib.Temp, Boards[i]->ACalib.Frequency, ctime(&Boards[i]->ACalib.Time)); // Status information struct FADBoard::BoardStatus S = Boards[i]->GetStatus(); PrintMessage("Status: %s\n", S.Message); if (S.Update.tv_sec == -1) { PrintMessage("No event received yet, no further status information available\n"); continue; } PrintMessage("Event rate %.1f Hz Last event received %s", S.Rate, ctime(&S.Update.tv_sec)); // Board identification PrintMessage("Board ID %.4x Firmware revision %.4x Serial %llx\n", S.BoardID, S.FirmwareRevision, S.DNA); PrintMessage("Board time %g s Event counter %d\n", S.BoardTime/1.0e4 , S.EventCounter); // Other data PrintMessage("Frequency %.2f GHz (%s) Phase shift %d PLL lock %d %d %d %d\n", S.Frequency, S.RefClk_low ? "too low":"OK", S.PhaseShift, S.Lock[0], S.Lock[1], S.Lock[2], S.Lock[3]); PrintMessage("DENABLE %d DWRITE %d SPI_clk %d DCM_lock %d DCM_ready %d\n", S.denable, S.dwrite, S.spi_clk, S.DCM_lock, S.DCM_ready); PrintMessage("DAC %d %d %d %d %d %d %d %d\n", S.DAC[0], S.DAC[1], S.DAC[2], S.DAC[3], S.DAC[4], S.DAC[5], S.DAC[6], S.DAC[7] ); PrintMessage("Temperature %.2f %.2f %.2f %.2f", S.Temp[0], S.Temp[1], S.Temp[2], S.Temp[3]); for (unsigned int j=0; j= 1) { if (Parameter[1][0] == '+') Mode = 1; if (Parameter[1][0] == '-') Mode = -1; } if (Mode != 0) Parameter[1][0] = ' '; // Disable all boards if (Match(Parameter[1], "none")) { for (int i=0; i<(int) Boards.size(); i++) Boards[i]->Active = false; return; } // Evaluate given range if (!ConvertToRange(Parameter[1], R)) { PrintMessage("Error, out of range.\n"); return; } // Enable or disable boards for (int i=0; i<(int) Boards.size(); i++) { if (Mode == 0) Boards[i]->Active = false; if (i >= R.Min && i <= R.Max) { if (Mode != -1) Boards[i]->Active = true; else Boards[i]->Active = false; } } } // // Set DIM event update delay // void FAD::cmd_update() { double Delay; if (Parameter.size()==2 && ConvertToDouble(Parameter[1], &Delay) && Delay>=0) EventUpdateDelay = Delay; else PrintUsage(); } // // Print help // void FAD::cmd_help() { char *Buffer; for(unsigned int i=0; i Execute shell command\n\n" "Items in <> are mandatory, in [] optional, | indicates mutual exclusive.\n" "Strings containing spaces have to be enclosed in \"double quotes\".\n" "Ranges can be given as 'all', a single number or in the form 'a-b'.\n"); } // // Stop current operation // void FAD::cmd_stop() { static char Stop[] = "stop\n"; if (Mode == idle) { PrintMessage("Nothing to stop\n"); return; } if (Mode == acalib || Mode == dynrange) { Mode = idle; Message(INFO, "Mode set to IDLE"); } if (Mode == datarun) { // Inform event thread to stop run in case datarun active if (write(Pipe[1], Stop, strlen(Stop)) == -1) { Message(ERROR, "write() to Pipe[1] failed in FAD::cmd_cancel() (%s)", strerror(errno)); } } PrintMessage("Requested stopping of current operation\n"); } // // Exit programm // SIGTERM makes readline() return (in case command came over network) // void FAD::cmd_exit() { ExitRequest = true; pthread_kill(MainThread, SIGTERM); } // ----------------------------- // ***** Other functions ***** // ----------------------------- // // DIM exit handler (overwriting handler in Evidence class) // void FAD::exitHandler(int Code) { Message(INFO, "Exit handler called (DIM exit code %d)", Code); cmd_exit(); } // // Save amplitude calibration data to file // void FAD::SaveAmplitudeCalibration() { // Open calibration data file string Filename = string(getenv("HOME"))+"/FAD_ACal"; FILE *File = fopen(Filename.c_str(), "wb"); if (File == NULL) { PrintMessage("Could not open calibration data file '%s'\n", Filename.c_str()); return; } // Write valid calibration information for active boards for (unsigned int i=0; iActive || Boards[i]->ACalib.Time == -1) continue; if (fwrite(&Boards[i]->ACalib, sizeof(Boards[i]->ACalib), 1, File) != 1) { PrintMessage("Could not write to calibration file '%s'\n", Filename.c_str()); break; } } // Close file if (fclose(File) != 0) PrintMessage("Could not close calibration file '%s'\n", Filename.c_str()); PrintMessage("Wrote amplitude calibration to file '%s'\n", Filename.c_str()); } // // Event thread (publishes/writes M0 format) // void FAD::EventThread() { struct timeval Time; struct timeval LastUpdate; struct FADBoard::BoardStatus S; vector EventNumbers(Boards.size()); vector AcalibDone(Boards.size()); vector ActiveBoards; double Temp; string IDString; char Buffer[100]; int Ret; struct stat FileStat; float FileSizeMB = 0; RunHeader FileRHeader; gettimeofday(&LastUpdate, NULL); // Create DIM event data and number services char *EventData = new char [sizeof(RunHeader)+ Boards.size()*sizeof(BoardStructure)+sizeof(EventHeader) + Boards.size()*(NChips*NChannels*NBins*sizeof(short) + NChips*sizeof(int))]; DimService EventService(SERVER_NAME"/EventData", (char *) "C", NULL, 0); DimService EventNumService(SERVER_NAME"/EventNumber", NumEvents); DimService FileSizeService(SERVER_NAME"/FileSizeMB", FileSizeMB); // Set/allocate pointers RunHeader *RHeader = (RunHeader *) EventData; BoardStructure **BStruct = new BoardStructure * [Boards.size()]; EventHeader *EHeader; int *TriggerCell; short *Data; // Fill fixed entries in M0 RunHeader RHeader->MagicNum = MAGICNUM_CLOSED; RHeader->DataFormat = DATA_FORMAT; RHeader->RunHeaderSize = sizeof(RunHeader); RHeader->EventHeaderSize = sizeof(EventHeader); RHeader->BoardStructureSize = sizeof(BoardStructure); RHeader->SoftwareRevision = atoi(REVISION) * (strchr(REVISION, 'M')==NULL ? 1:-1); RHeader->Identification = 0; RHeader->Type = 0; // Run type: 0=data, 1=pedestal, 3=test RHeader->RunNumber = -1; RHeader->FileNumber = 0; snprintf(RHeader->Description, sizeof(RHeader->Description), "FADctrl"); RHeader->Events = 1; RHeader->StartSecond = LastUpdate.tv_sec; RHeader->StartMicrosecond = LastUpdate.tv_usec; RHeader->NChips = NChips; RHeader->NChannels = NChannels; RHeader->Samples = NBins; // Always full pipeline RHeader->Offset = 0; RHeader->NBytes = sizeof(short); // Update loop while (!ExitRequest) { // Removed processed data from IDString size_t LastLF = IDString.find_last_of("\n"); if (LastLF != string::npos) IDString = IDString.substr(LastLF+1); // Wait for data from TCP/IP reading threads if ((Ret=read(Pipe[0], Buffer, sizeof(Buffer))) == -1) Message(FATAL, "read() from Pipe[0] failed in FAD::EventThread() (%s)", strerror(errno)); // Check if pipe closed if (Ret == 0) break; IDString.append(string(Buffer, Ret)); // Active boards must not change during data taking if (Mode != datarun || NumEvents == 0) { ActiveBoards.clear(); for (unsigned int i=0; iActive) ActiveBoards.push_back(Boards[i]); } RHeader->NBoards = ActiveBoards.size(); } // Calculate pointers to EventData array for (unsigned int i=0; iName) != string::npos) AcalibDone[i] = true; if (!AcalibDone[i] && Boards[i]->Active) Done = false; } // Amplitude calibration finished? if (Done) { if (Mode == acalib) { SaveAmplitudeCalibration(); Message(INFO, "Amplitude calibration done, mode set to IDLE"); } if (Mode == dynrange) { Message(INFO, "Dynamic range measurement done, mode set to IDLE"); } Mode = idle; } } else for (unsigned int i=0; iEndSecond = Time.tv_sec; RHeader->EndMicrosecond = Time.tv_usec; EHeader->EventSize = RHeader->NBoards * (NChips*NChannels*NBins*sizeof(short) + NChips*sizeof(int)); EHeader->Second = Time.tv_sec; EHeader->Microsecond = Time.tv_usec; // Check all boards that have new data for (unsigned int Brd=0; BrdName) == string::npos) continue; // Fill M0 BoardStructure S = ActiveBoards[Brd]->GetStatus(); BStruct[Brd]->SerialNo = (U32) S.DNA; BStruct[Brd]->NomFreq = S.Frequency; BStruct[Brd]->BoardTemp = 0; for (unsigned int i=0; iBoardTemp += S.Temp[i]/NTemp; BStruct[Brd]->ScaleFactor = 1/2.048; // Update event header with ID and Type of current board EHeader->EventNumber = S.TriggerNum; EHeader->TriggerType = S.TriggerType; // Register event number for data writing below EventNumbers[Brd] = S.TriggerNum; // Write trigger cells for(unsigned int i=0; iLock(); for (unsigned int Chip=0; ChipACalib.Time == -1) Data[Count++] = ActiveBoards[Brd]->Data[Chip][Chan][i]; else { Temp = (ActiveBoards[Brd]->Data[Chip][Chan][i] - ActiveBoards[Brd]->ACalib.Baseline[Chip][Chan][(i+S.TriggerCell[Chip])%NBins]); Temp *= ActiveBoards[Brd]->ACalib.Gain[Chip][Chan][0]/ActiveBoards[Brd]->ACalib.Gain[Chip][Chan][(i+S.TriggerCell[Chip])%NBins]; //Temp -= ActiveBoards[Brd]->ACalib.Secondary[Chip][Chan][i]; Data[Count++] = (short) Temp; } } Count += NBins - S.ROI[Chip][Chan]; } ActiveBoards[Brd]->Unlock(); } // Inform TCP/IP thread of all boards that send data that processing is finished for (unsigned int Brd=0; BrdName) != string::npos) { Boards[Brd]->Lock(); Boards[Brd]->Continue = true; Boards[Brd]->Unlock(); if ((Ret = pthread_cond_signal(&Boards[Brd]->CondVar)) != 0) { Message(FATAL, "pthread_cond_signal() failed (%s)", strerror(Ret)); } } // Check if DIM service should be updated if ((Time.tv_sec-LastUpdate.tv_sec)*1e6 + Time.tv_usec-LastUpdate.tv_usec > EventUpdateDelay*1e6) { gettimeofday(&LastUpdate, NULL); EventService.updateService(EventData, sizeof(RunHeader) + sizeof(EventHeader) + ActiveBoards.size()*(sizeof(BoardStructure) + NChips*NChannels*NBins*sizeof(short) + NChips*sizeof(int))); EventNumService.updateService(); if (Mode == datarun) FileSizeService.updateService(); } // ===== Data writing === if (Mode != datarun) continue; // Check if event numbers of all active boards are the same unsigned long CommonEventNum = numeric_limits::max(); for (unsigned int i=0; iActive) { if (CommonEventNum == numeric_limits::max()) CommonEventNum = EventNumbers[i]; if (CommonEventNum != EventNumbers[i]) { CommonEventNum = numeric_limits::max(); break; } } if (CommonEventNum == numeric_limits::max()) continue; int Error = 0; // Initialize run if (NumEvents == 0) { FileSizeMB = 0; FileRHeader = *RHeader; FileRHeader.MagicNum = MAGICNUM_OPEN; FileRHeader.StartSecond = Time.tv_sec; FileRHeader.StartMicrosecond = Time.tv_usec; // Write run header and board structures if (write(Datafile, &FileRHeader, sizeof(RunHeader)) == -1) Error = errno; else if (write(Datafile, BStruct[0], FileRHeader.NBoards*sizeof(BoardStructure)) == -1) Error = errno; } // Write event header, trigger cells and ADC data to file if (write(Datafile, EHeader, sizeof(EventHeader)+FileRHeader.NBoards*NChips*(sizeof(int)+NChannels*NBins*sizeof(short))) == -1) Error = errno; NumEvents++; // Update file size if (fstat(Datafile, &FileStat) == -1) Error = errno; else FileSizeMB = FileStat.st_size/1024.0/1024.0; // Check for write errors and for correct file size if (Error !=0) { Message(ERROR, "Error writing to data data file (%s), terminating run, setting mode to IDLE", strerror(Error)); } else if ((size_t) FileStat.st_size != sizeof(RunHeader)+ FileRHeader.NBoards*sizeof(BoardStructure)+NumEvents*(sizeof(EventHeader) + FileRHeader.NBoards*(NChips*NChannels*NBins*sizeof(short) + NChips*sizeof(int)))) { Message(ERROR, "Could not write all data to file, terminating run, setting mode to IDLE"); Error = 1; } // Close file if error if (Error != 0) { if (close(Datafile) == -1) Message(ERROR, "Could not close data file (%s)", strerror(errno)); Datafile = -1; Mode = idle; continue; } // Close data file if requested or requested number of events reached if(IDString.find("stop")!=string::npos || (NumEvents==NumEventsRequested && NumEventsRequested!=0)) { // Update run header for writing FileRHeader.MagicNum = MAGICNUM_CLOSED; FileRHeader.Events = NumEvents; FileRHeader.EndSecond = Time.tv_sec; FileRHeader.EndMicrosecond = Time.tv_usec; if (lseek(Datafile, 0, SEEK_SET) == -1) { Message(ERROR, "Could not rewind file to write updated run header (%s)", strerror(errno)); } else if (write(Datafile, &FileRHeader, sizeof(RunHeader)) != sizeof(RunHeader)) { Message(ERROR, "Could not write updated run header (%s)", strerror(errno)); } // Close data file and terminate run if(close(Datafile) == -1) Message(ERROR, "Could not close data file (%s)", strerror(errno)); else PrintMessage("Data file closed (size %.1f MByte, rate %.1f MByte/s, %d events).\n", FileSizeMB, FileSizeMB/(FileRHeader.EndSecond-FileRHeader.StartSecond+(FileRHeader.EndMicrosecond-FileRHeader.StartMicrosecond)/1000000.0), NumEvents); Datafile = -1; Mode = idle; Message(INFO, "Data run ended, mode set to IDLE"); } } delete[] BStruct; delete[] EventData; } // Launch event thread inside class void FAD::LaunchEventThread(class FAD *m) { m->EventThread(); } /* // Set DOMINO mode void FAD::SetDOMINOMode(int mode) { for (int i=FirstBoard; i<=LastBoard; i++) { GetBoard(i)->SetDominoMode(mode==1 ? 1:0); PrintMessage("Domino mode of board %d switched to %s.\n",i,mode==1 ? "continuous":"single shot"); } } // Set DOMINO readout mode void FAD::SetDOMINOReadMode(int mode) { for (int i=FirstBoard; i<=LastBoard; i++) { GetBoard(i)->SetReadoutMode(mode); PrintMessage("Start readout of board %d from %s.\n",i,mode==0 ? "first bin":"stop position"); } } // Set DOMINO wave mode void FAD::SetDOMINOWaveMode(int mode) { for (int i=FirstBoard; i<=LastBoard; i++) { GetBoard(i)->SetDominoActive(mode); PrintMessage("Domino wave of board %d is %s during readout\n",i,mode==1 ? "running":"stopped"); } } */ // // Print usage text for command // void FAD::PrintUsage() { for(unsigned int i=0; iupdateService(Text); // Free old text if (ConsoleText != Error) free(ConsoleText); ConsoleText = Text; } // // Check if two strings match (min 1 character must match) // bool FAD::Match(string str, const char *cmd) { return strncasecmp(str.c_str(),cmd,strlen(str.c_str())==0 ? 1:strlen(str.c_str())) ? false:true; } // // Conversion function from string to double, int or range // // Return false if conversion did not stop on whitespace or EOL character bool FAD::ConvertToDouble(string String, double *Result) { char *EndPointer; *Result = strtod(String.c_str(), &EndPointer); if(!isspace(*EndPointer) && *EndPointer!='\0') return false; return true; } bool FAD::ConvertToInt(string String, int *Result) { char *EndPointer; *Result = (int) strtol(String.c_str(), &EndPointer, 0); if(!isspace(*EndPointer) && *EndPointer!='\0') return false; return true; } bool FAD::ConvertToRange(string String, struct FAD::Range &R) { int N=0, M=0; // Full range if (Match(String, "all")) return true; // Single number if (ConvertToInt(String, &N)) { if (N>= R.Min && N<=R.Max) { R.Max = R.Min = N; return true; } return false; } // Range a-b vector V = EvidenceServer::Tokenize(String, "-"); if (V.size()==2 && ConvertToInt(V[0], &N) && ConvertToInt(V[1], &M) && N>=R.Min && M<=R.Max) { R.Min = N; R.Max = M; return true; } return false; }