/********************************************************************\ 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, false, 1, "[+|-]" ,"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"}, {"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 region-of-interest to value"}, {"dac", &FAD::cmd_dac, true, 2, " ", "Set DAC numbers in range to value"}, {"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, write to directory"}, {"acalib", &FAD::cmd_acalib, true, 0, "[n|invalidate|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"}, {"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", "0.5").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 == '.') { system(Command+1); 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 run number // void FAD::cmd_runnumber() { int Num; if (!ConvertToInt(Parameter[1], &Num)) { PrintMessage("Error, incorrect parameter for run number\n"); return; } for (unsigned int i=0; iSend(CMD_Write | ADDR_RUNNUMBER ); Boards[i]->Send((unsigned short) (Num>>16)); // Write the HIGH-word first Boards[i]->Send(CMD_Write | (ADDR_RUNNUMBER+1) ); Boards[i]->Send((unsigned short) Num); // now write the LOW-word } } // // Switch socket mode // "com" - command mode (only socket 0 is used) // "daq" - daq mode (only sockets 1 - 7 are used) // // Note that socket 0 is always used to issue commands // 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)); } } // // 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); } // Disable triggers for all boards and wait (workaround for firmware bug) for (unsigned int i=0; iSend(CMD_TRIGGERS_OFF); usleep(500000); // Send command buffer and enable triggers again for (unsigned int i=0; iSend(Buffer, sizeof(Buffer)); Boards[i]->Send(CMD_TRIGGERS_ON); } } // // 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); 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); } // // 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); 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] = ' '; // 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 = 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() { if (Mode != idle) cmd_stop(); ExitRequest = true; // Wait to allow console input to arrive at readline() usleep(10000); 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, RunStart; struct timeval LastUpdate; struct FADBoard::BoardStatus S; vector EventNumbers(Boards.size()); vector AcalibDone(Boards.size()); double Temp; string IDString; char Buffer[100]; int Ret; unsigned long long FileSize = 0; gettimeofday(&LastUpdate, NULL); RunStart = LastUpdate; // only to avoid 'uninitialized' warning from compiler // Create DIM event data and number services int EventSize = sizeof(RunHeader)+ Boards.size()*sizeof(BoardStructure)+sizeof(EventHeader) + Boards.size()*(NChips*NChannels*NBins*sizeof(short) + NChips*sizeof(int)); char *EventData = new char [EventSize]; memset(EventData, 0, EventSize); DimService EventService(SERVER_NAME"/EventData", (char *) "C", NULL, 0); DimService EventNumService(SERVER_NAME"/EventNumber", NumEvents); // Calculate pointers to EventData array RunHeader *RHeader = (RunHeader *) EventData; BoardStructure **BStruct = new BoardStructure * [Boards.size()]; for (unsigned int i=0; iDataFormat = 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->NBoards = Boards.size(); RHeader->NChips = NChips; RHeader->NChannels = NChannels; RHeader->Samples = NBins; // Always full pipeline RHeader->Offset = 0; RHeader->NBytes = sizeof(short); // M0 EventHeader EHeader->EventSize = Boards.size()*(NChips*NChannels*NBins*sizeof(short) + NChips*sizeof(int)); // 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)); // If amplitude calibration mode, check if board finished procedure if (Mode == acalib) { bool Done = true; for (unsigned int i=0; iName) != string::npos) AcalibDone[i] = true; if (!AcalibDone[i] && Boards[i]->Active) Done = false; } // Amplitude calibration finished? if (Done) { SaveAmplitudeCalibration(); Mode = idle; Message(INFO, "Amplitude calibration done, mode set to IDLE"); } } else for (unsigned int i=0; iMagicNum = MAGICNUM_CLOSED; RHeader->EndSecond = Time.tv_sec; RHeader->EndMicrosecond = Time.tv_usec; EHeader->Second = Time.tv_sec; EHeader->Microsecond = Time.tv_usec; // Close data file if requested or requested number of events reached if((IDString.find("stop")!=string::npos || NumEvents==NumEventsRequested) && Mode==datarun) { // Update run header RHeader->Events = NumEvents; RHeader->StartSecond = RunStart.tv_sec; RHeader->StartMicrosecond = RunStart.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, RHeader, 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).\n", FileSize/1024.0/1024); Datafile = -1; Mode = idle; Message(INFO, "Data run ended, mode set to IDLE"); } // These values might have changed while close file RHeader->StartSecond = Time.tv_sec; RHeader->StartMicrosecond = Time.tv_usec; RHeader->Events = 1; // Check all boards that have new data for (unsigned int Brd=0; BrdName) == string::npos) continue; // Fill M0 BoardStructure S = Boards[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++] = Boards[Brd]->Data[Chip][Chan][i]; else { Temp = (Boards[Brd]->Data[Chip][Chan][i] - Boards[Brd]->ACalib.Baseline[Chip][Chan][(i+S.TriggerCell[Chip])%NBins]); Temp *= Boards[Brd]->ACalib.Gain[Chip][Chan][0]/Boards[Brd]->ACalib.Gain[Chip][Chan][(i+S.TriggerCell[Chip])%NBins]; //Temp -= Boards[Brd]->ACalib.Secondary[Chip][Chan][i]; Data[Count++] = (short) Temp; } } Count += NBins - S.ROI[Chip][Chan]; } // Inform TCP/IP thread that data has been processed 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)); } } // Loop over boards // 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, EventSize); EventNumService.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; // Write also run header if this is the first event int Offset; if (NumEvents == 0) { RHeader->MagicNum = MAGICNUM_OPEN; RunStart = Time; Offset = 0; FileSize = 0; } else Offset = sizeof(RunHeader) + Boards.size()*sizeof(BoardStructure); // Write data to file if(write(Datafile, EventData+Offset, EventSize-Offset) != (ssize_t) EventSize-Offset) { Message(ERROR, "Could not write all data to file, terminating run, setting mode to IDLE (%s)", strerror(errno)); // Close file if error if (close(Datafile) == -1) Message(ERROR, "Could not close data file (%s)", strerror(errno)); Datafile = -1; Mode = idle; continue; } NumEvents++; FileSize += EventSize-Offset; } 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, M; // 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; }