/********************************************************************\ DAQReadout.cc Main DAQ routines. Sebastian Commichau, Oliver Grimm \********************************************************************/ #include "DAQReadout.h" #include "SlowData.h" #define TMPNAME "/tmp/__tmp__drsdaq__" // ..for log file truncation static const char* daq_state_str[] = {"active", "stopped"}; static const char* daq_runtype_str[] = {"data", "pedestal", "reserved", "test"}; static const struct CL_Struct { const char *Name; void (DAQReadout::*CommandPointer)(); bool NeedNotBusy; const char *Parameters; const char *Help; } CommandList[] = {{"board", &DAQReadout::cmd_board, true, " [j] | " ,"Address board i, boards i-j, all boards"}, {"status", &DAQReadout::cmd_status, false, "[daq|drs]", "Show DAQ/DRS status information"}, {"frequency", &DAQReadout::cmd_freq, true, " [reg]", "Set DRS sampling frequency (regulated)"}, {"calib", &DAQReadout::cmd_calib, true, " ", "Response calibration"}, {"trigger", &DAQReadout::cmd_trigger, true, "", "Hardware trigger on or off"}, {"delayed", &DAQReadout::cmd_delayed, true, "", "Switch delayed start on or off"}, {"wmode", &DAQReadout::cmd_wmode, true, "<0|1>", "Set DRS wave mode"}, {"rmode", &DAQReadout::cmd_rmode, true, "<0|1>", "Set DRS readout mode"}, {"mode", &DAQReadout::cmd_mode, true, "", "Set DRS single shot or continuous mode"}, {"read", &DAQReadout::cmd_read, false, " [res]", "Read current data (and restart if DAQ not active)"}, {"take", &DAQReadout::cmd_take, false, " [n] [source]", "Start run (data, pedestal or test) with n events"}, {"events", &DAQReadout::cmd_events, false, "", "Number of events in current run"}, {"start", &DAQReadout::cmd_start, true, "", "Start DRS and DAQ without disk writing (feedback will be called)"}, {"stop", &DAQReadout::cmd_stop, false, "", "Issue soft trigger and stop DAQ"}, {"test", &DAQReadout::cmd_test, true, "[2e] [n]", "Test read access of VMEbus (n blocks)"}, {"regtest", &DAQReadout::cmd_regtest, true, "", "DRS register test"}, {"ramtest", &DAQReadout::cmd_ramtest, true, "", "DRS RAM integrity and speed test"}, {"led", &DAQReadout::cmd_led, true, "", "Turn LED on or off"}, {"config", &DAQReadout::cmd_config, false, "", "Print drsdaq configuration"}, {"serial", &DAQReadout::cmd_serial, true, " ", "Set serial# of board to (experts only)"}, {"disk", &DAQReadout::cmd_disk, false, "" ,"Remaining disk space"}, {"uptime", &DAQReadout::cmd_uptime, false, "", "Get DAQ uptime"}, {"exit", &DAQReadout::cmd_exit, false, "", "Exit program"}, {"fmode", &DAQReadout::cmd_fmode, false, "[off|active|targ]", "Set or get feedback mode"}, {"faverage", &DAQReadout::cmd_faverage, false, "[n]", "Set ot get number of averages for feedback"}, {"fgain", &DAQReadout::cmd_fgain, false, "[gain]", "Set ot get feedback gain"}, {"ftarget", &DAQReadout::cmd_ftarget, false, "[brd chip chan]", "Set or get target value (also 'all' supported)"}, {"fresponse", &DAQReadout::cmd_fresponse, false, "[voltage]", "Start response measurement with given voltage difference"}, {"fconfig", &DAQReadout::cmd_fconfig, false, "", "Print feedback configuration"}, {"help", &DAQReadout::cmd_help, false, "", "Print help"}}; // ----------------------------------------------- // ***** Constructor: Class initialisation ***** // ----------------------------------------------- // DAQReadout::DAQReadout(const char *Configfile) { time(&StartTime); // Start time of DAQ // Initialize status structure daq_state = stopped; daq_runtype = data; Socket = -1; Exit = false; CalibrationRead = false; NumEvents = 0; NumEventsRequested = 0; NumBoards = 0; FirstBoard = 0; LastBoard = -1; CmdFromSocket = false; ConfigOK = true; // Read configuration file (if any item missing, program will be terminated in main() routine) FILE *File; if ((File = fopen(Configfile,"r")) == NULL) { printf("Error: Could not open drsdaq configuration file '%s'\n", Configfile); ConfigOK = false; return; } else { printf("Reading drsdaq configuration file %s\n", Configfile); ConfigOK &= ReadCard("LogFile", fLogFile, 's', File); ConfigOK &= ReadCard("SlowDataPath", fSlowDataPath, 's', File); ConfigOK &= ReadCard("MaxLogLines", &fMaxLogLines, 'U', File); ConfigOK &= ReadCard("RawDataPath", fRawDataPath, 's', File); ConfigOK &= ReadCard("FirstSample", &fFirstSample, 'I', File); ConfigOK &= ReadCard("Samples", &fSamples, 'U', File); ConfigOK &= ReadCard("MinDiskSpaceMB", &fMinDiskSpaceMB, 'U', File); ConfigOK &= ReadCard("MaxFileSizeMB", &fMaxFileSizeMB, 'I', File); ConfigOK &= ReadCard("CCPort", &fCCPort, 'I', File); ConfigOK &= ReadCard("FirstVMESlot", &fFirstVMESlot, 'I', File); ConfigOK &= ReadCard("LastVMESlot", &fLastVMESlot, 'I', File); ConfigOK &= ReadCard("HVFeedbackConfig", fHVFeedbackConfig, 's', File); ConfigOK &= ReadCard("DefaultFrequency", &fDefaultFrequency , 'd', File); fclose(File); if (!ConfigOK) return; } if (fFirstSample < 0 || fFirstSample > kNumberOfBins || fSamples > kNumberOfBins) { PrintMessage("Warning: Sample range in configuration beyond limits, setting to full range\n"); fFirstSample = kNumberOfBins; fSamples = kNumberOfBins; } // Truncate log file to given number of lines char ShellCmd[MAX_COM_SIZE]; snprintf(ShellCmd, sizeof(ShellCmd), "tail --lines=%u %s >%s; cp %s %s; rm %s", fMaxLogLines, fLogFile, TMPNAME, TMPNAME, fLogFile, TMPNAME); if (system(ShellCmd) != 0) printf("Warning: Could not truncate log file '%s' to %u lines\n", fLogFile, fMaxLogLines); // Open log file and log configuration if ((Logfile = fopen(fLogFile, "a")) == NULL) printf("Warning: Could not open log file '%s'\n", fLogFile); PrintMessage(MsgToLog,"********** Logging started **********\n"); PrintConfig(MsgToLog); // Create DRS instance and perform initial scan drs = new DRS(); drs->SetFirstVMESlot(fFirstVMESlot); drs->SetLastVMESlot(fLastVMESlot); drs->InitialScan(); // Allocate headers and initialise to zero RHeader = new RunHeader; memset(RHeader, 0, sizeof(RunHeader)); EHeader = new EventHeader; memset(EHeader, 0, sizeof(EventHeader)); // Scan for DRS boards DRSFreq = new float [drs->GetNumberOfBoards()]; if (drs->GetNumberOfBoards()==0) PrintMessage("No DRS boards found - check VME crate and configuration file!\n"); for (int i=0; iGetNumberOfBoards(); i++) { PrintMessage("Init. mezz. board %2d on VME slot %2d %s, serial #%d, firmware revision %d\n", i, (drs->GetBoard(i)->GetSlotNumber() >> 1)+2, ((drs->GetBoard(i)->GetSlotNumber() & 1) == 0) ? "upper" : "lower", drs->GetBoard(i)->GetCMCSerialNumber(), drs->GetBoard(i)->GetFirmwareVersion()); NumBoards++; LastBoard++; drs->GetBoard(i)->Init(); drs->GetBoard(i)->SetRotation(false); DRSFreq[i] = 0; } BStruct = new BoardStructure [NumBoards == 0 ? 1:drs->GetNumberOfBoards()]; memset(BStruct, 0, sizeof(BoardStructure)*(NumBoards == 0 ? 1:drs->GetNumberOfBoards())); WaveForm = new short [NumBoards == 0 ? 1:NumBoards][kNumberOfChips][kNumberOfChannels][kNumberOfBins]; TriggerCell = new int [NumBoards == 0 ? 1:NumBoards][kNumberOfChips] (); // Zero initialised // Create instance of HV feedback (must be called after CMC board detection) HVFB = new HVFeedback(this, fHVFeedbackConfig); // Create instance of slow data class for DAQ SlowDataClass = new SlowData("DAQ", fSlowDataPath); if(SlowDataClass->ErrorCode != 0) { PrintMessage("Warning: Could not open DAQ slowdata file (%s)\n", strerror(SlowDataClass->ErrorCode)); } SlowDataClass->NewEntry("Runinfo-Info", "Run information written after completion or termination of run (Status can be OK or Error): Runnumber Status Runtype Events Files Description "); } // ------------------------ // ***** Destructor ***** // ------------------------ DAQReadout::~DAQReadout() { delete SlowDataClass; delete RHeader; delete EHeader; delete drs; delete HVFB; delete[] DRSFreq; delete[] BStruct; delete[] WaveForm; delete[] TriggerCell; if(Logfile) { if(!fclose(Logfile)) printf("Closing logfile\n"); else perror("Error closing logfile"); } } // -------------------------------- // ***** Command evaluation ***** // -------------------------------- void DAQReadout::CommandControl(char *Command) { if (strlen(Command)==0) return; // Ignore empty commands if(Command[0]=='.') { // Shell command system(&(Command[1])); return; } for(int i=0; i*CommandList[CmdNumber].CommandPointer)(); return; } PrintMessage("Unknown command '%s'\n",Param[0]); return; } // Get uptime void DAQReadout::cmd_uptime() { time_t ActualT; time (&ActualT); PrintMessage("%d:%02d:%02d\n", (int) difftime(ActualT, StartTime)/3600, ((int) difftime(ActualT, StartTime)/60)%60, (int) difftime(ActualT, StartTime)%60); } // Print disk space void DAQReadout::cmd_disk() { PrintMessage("Free disk space (%s) [MB]: %lu\n", fRawDataPath, CheckDisk(fRawDataPath)); } // Print current number of events void DAQReadout::cmd_events() { if(daq_state != active) PrintMessage("DAQ not active\n"); else PrintMessage("Current number of events: %d (of %d requested)\n", NumEvents, NumEventsRequested); } // Print DAQ configuration void DAQReadout::cmd_config() { PrintConfig(CmdFromSocket ? MsgToSocket : MsgToConsole); } // Start DAQ void DAQReadout::cmd_take() { if(!Match(Param[1],"test")) { if (daq_state==active || NumBoards==0) { PrintMessage("DAQ is busy or no boards available\n"); return; } if (!IsDRSFreqSet()) { PrintMessage("Setting default frequency\n"); SetDRSFrequency(fDefaultFrequency, false); CalibrationRead = false; } if (!CalibrationRead && !ReadCalibration()) { PrintMessage("Cannot start run if response calibration not read\n"); return; } } if (Match(Param[1],"data")) { HWTrigger(1); daq_runtype = data; } else if (Match(Param[1],"pedestal")) { HWTrigger(0); daq_runtype = pedestal; } else if (Match(Param[1],"test")) { daq_runtype = test; } else { PrintUsage(); return; } if (NParam==3) NumEventsRequested = atoi(Param[2]); if (NParam==4) { NumEventsRequested = atoi(Param[2]); strncpy(RHeader->Description, Param[3], sizeof(RHeader->Description)); } else snprintf(RHeader->Description,sizeof(RHeader->Description),"DUMMY"); // Determine new run number using the file RUN_NUM_FILE FILE *RunNumFile = fopen(RUN_NUM_FILE,"r+"); if(RunNumFile == NULL) { PrintMessage("Error: Could not open file '%s' that contains the last run number (%s)\n",RUN_NUM_FILE,strerror(errno)); return; } if(fscanf(RunNumFile,"%u", &RunNumber) != 1 ) { PrintMessage("Error: Could not read run number from file '%s'\n",RUN_NUM_FILE); fclose(RunNumFile); return; } RunNumber++; rewind(RunNumFile); if((fprintf(RunNumFile,"%.8u ",RunNumber) < 0) || (fclose(RunNumFile)!=0)) { PrintMessage("Error: Could not write to or close run number file '%s'\n",RUN_NUM_FILE); PrintMessage("*** This is a serious error because run numbers will get mixed. Fix it. DAQ will terminate.\n"); throw; } // Create DAQ thread if ((pthread_create(&thread_DAQ, NULL, (void * (*)(void *)) DAQ,(void *) this)) != 0) PrintMessage("pthread_create failed with DAQ thread (%s)\n",strerror(errno)); else { daq_state = active; Stop = false; pthread_detach(thread_DAQ); } } // Start DRS void DAQReadout::cmd_start() { if (IsDRSFreqSet()) { StartDRS(); PrintMessage("Domino wave started\n"); // Create DAQ thread if ((pthread_create(&thread_DAQ_Silent, NULL, (void * (*)(void *)) DAQ_Silent,(void *) this)) != 0) PrintMessage("pthread_create failed with DAQ_Silent thread (%s)\n",strerror(errno)); else { daq_state = active; Stop = false; pthread_detach(thread_DAQ_Silent); } } } // RAM test void DAQReadout::cmd_ramtest() { for (int i=FirstBoard; i<=LastBoard; i++) { PrintMessage("RAM integrity and speed test (board #%d):\n",i); (drs->GetBoard(i))->RAMTest(3); } } // Register test void DAQReadout::cmd_regtest() { for (int i=FirstBoard; i<=LastBoard; i++) { PrintMessage("Register test (board #%d):\n",i); (drs->GetBoard(i))->RegisterTest(); } } // Test VME transfer void DAQReadout::cmd_test() { int Type=-1, i; if (Match(Param[1], "2eblt64")) Type = 2; else if (Match(Param[1], "blt32")) Type = 0; else if (Match(Param[1], "blt64")) Type = 1; else { PrintMessage("Unknown type for testing\n"); return; } if (NumBoards) for (i=FirstBoard; i<=LastBoard; i++) { PrintMessage("BLT test started (board #%d)\n",i); (drs->GetBoard(i))->TestRead(Param[2][0] && atoi(Param[2])<=10000 && atoi(Param[2])>0 ? atoi(Param[2]):1, Type); } else PrintMessage("No DRS boards available\n"); } // Stop DAQ void DAQReadout::cmd_stop() { if(!daq_state==active && !IsDRSBusy()) PrintMessage("Nothing to stop\n"); if (daq_state==active) StopRun(); if (IsDRSBusy()) { StopDRS(); PrintMessage("Domino wave stopped\n"); } } // Read current data // For socket transmission: all numbers must be separated by exactly one // whitespace; the first number is the number of numbers that follow, the // second number the sampling frequency in GHz, the third the conversion factor void DAQReadout::cmd_read() { if(NumBoards==0) { PrintMessage("No mezzanine boards available\n"); return; } if (NParam<4) { PrintUsage(); return; } if (atoi(Param[1])>LastBoard || atoi(Param[1])=kNumberOfChannels) { PrintMessage("Error: Channel number out of range\n"); return; } if (atoi(Param[2])<0 || atoi(Param[2])>=kNumberOfChips) { PrintMessage("Error: Chip number out of range\n"); return; } if (daq_state!=active) { if (!CalibrationRead) ReadCalibration(); if(NParam==5) StopDRS(); ReadCalibratedDRSData(); if(NParam==5) StartDRS(); } PrintMessage(CmdFromSocket ? MsgToSocket:MsgToConsole|MsgToLog, "==START== %d %.2f %.2f ",kNumberOfBins+2,DRSFreq[atoi(Param[1])],drs->GetBoard(atoi(Param[1]))->GetPrecision()); for (int k=0; k LastBoard)) PrintMessage("Board number out of range (%d...%d)!\n",FirstBoard,LastBoard); else if (atoi(Param[2]) < 100 || atoi(Param[2]) >= 1000) PrintMessage("Serial number out of range (100...999)!\n"); else { PrintMessage("Flashing EEPROM of board %d...\n", atoi(Param[1])); (drs->GetBoard(atoi(Param[1])))->FlashEEPROM(atoi(Param[2])); } } else PrintMessage("You are not allowed to change the serial number!\n"); } // Do internal calibration void DAQReadout::cmd_calib() { char str[MAX_COM_SIZE]; if (NParam!=2 || !atof(Param[1])) { PrintUsage(); return; } if (!IsDRSFreqSet()) { PrintMessage("Set sampling frequency first\n"); return; } getcwd(str, sizeof(str)); strcat(str,"/calib"); for (int i=FirstBoard; i<=LastBoard; i++) { PrintMessage("Creating calibration of board %d (serial number %d)\n", i, drs->GetBoard(i)->GetCMCSerialNumber()); drs->GetBoard(i)->EnableTcal(1); if (drs->GetBoard(i)->GetChipVersion() == 3) drs->GetBoard(i)->GetResponseCalibration()->SetCalibrationParameters(1,21,0,20,0,0,0,0,0); else drs->GetBoard(i)->GetResponseCalibration()->SetCalibrationParameters(1,36,110,20,19,40,15,atof(Param[1]),0); drs->GetBoard(i)->SetCalibrationDirectory(str); for (int j=0; jGetBoard(i)->GetResponseCalibration()->ResetCalibration(); while (!drs->GetBoard(i)->GetResponseCalibration()->RecordCalibrationPoints(j)) {} PrintMessage("Calibration points recorded.\n"); while (!drs->GetBoard(i)->GetResponseCalibration()->FitCalibrationPoints(j)) {} PrintMessage("Calibration points fitted.\n"); while (!drs->GetBoard(i)->GetResponseCalibration()->OffsetCalibration(j)) {} PrintMessage("Offset calibration done.\n"); if (!drs->GetBoard(i)->GetResponseCalibration()->WriteCalibration(j)) break; } } // Loop over boards PrintMessage("End of calibration\n"); CalibrationRead = false; } // Set DRS sampling frequency void DAQReadout::cmd_freq() { if (NParam>=2 && atof(Param[1])) { SetDRSFrequency(atof(Param[1]), NParam==2 ? false : true); CalibrationRead = false; } else PrintUsage(); } // Set LED void DAQReadout::cmd_led() { if (Match(Param[1], "on") || Match(Param[1], "off")) for (int i=FirstBoard; i<=LastBoard; i++) (drs->GetBoard(i))->SetLED(Match(Param[1], "on") ? 1 : 0); else PrintUsage(); } // Print status void DAQReadout::cmd_status() { double freq; if(NParam==1 || Match(Param[1],"daq")) { PrintMessage("********** DAQ STATUS **********\n" " DAQ: %s\n" " Run number: %d\n" " Run type: %s\n" " Event: %d\n" " Requested events per run: %d\n" " Storage directory: %s\n" " Disk space: %lu MByte\n" " Socket state: %s\n" " Total number of CMC boards: %d\n" " Active CMC boards: %d\n", daq_state_str[daq_state], daq_state==active ? (int) RunNumber:-1, daq_runtype_str[daq_runtype], NumEvents, NumEventsRequested, fRawDataPath, CheckDisk(fRawDataPath), Socket==-1 ? "disconnected":"connected", NumBoards, LastBoard - FirstBoard + 1); for (int i=FirstBoard;i<=LastBoard;i++) PrintMessage(" Frequency of board %d set: %s\n",i,(DRSFreq[i]!=0 ? "yes":"no")); } if(NParam==1 || Match(Param[1],"drs")) { PrintMessage("\n********** DRS STATUS **********\n"); if (NumBoards) { for (int i=FirstBoard; i<=LastBoard; i++) { PrintMessage(" Mezz. board index: %d\n" " Slot: %d %s\n",i,((drs->GetBoard(i))->GetSlotNumber() >> 1)+2,((drs->GetBoard(i))->GetSlotNumber() & 1)==0 ? "upper":"lower"); PrintMessage(" Chip version: DRS%d\n" " Board version: %d\n" " Serial number: %d\n" " Firmware revision: %d\n" " Temperature: %1.1lf C\n" " Status reg.: 0X%08X\n", (drs->GetBoard(i))->GetChipVersion(), (drs->GetBoard(i))->GetCMCVersion(), (drs->GetBoard(i))->GetCMCSerialNumber(), (drs->GetBoard(i))->GetFirmwareVersion(), (drs->GetBoard(i))->GetTemperature(), (drs->GetBoard(i))->GetStatusReg()); if ((drs->GetBoard(i))->GetStatusReg() & BIT_RUNNING) PrintMessage(" Domino wave running\n"); if ((drs->GetBoard(i))->GetStatusReg() & BIT_NEW_FREQ1) PrintMessage(" New Freq1 ready\n"); if ((drs->GetBoard(i))->GetStatusReg() & BIT_NEW_FREQ2) PrintMessage(" New Freq2 ready\n"); PrintMessage(" Control reg.: 0X%08X\n", (drs->GetBoard(i))->GetCtrlReg()); if ((drs->GetBoard(i))->GetCtrlReg() & BIT_AUTOSTART) PrintMessage(" AUTOSTART enabled\n"); if ((drs->GetBoard(i))->GetCtrlReg() & BIT_DMODE) PrintMessage(" DMODE circular\n"); else PrintMessage(" DMODE single shot\n"); if ((drs->GetBoard(i))->GetCtrlReg() & BIT_LED) PrintMessage(" LED\n"); if ((drs->GetBoard(i))->GetCtrlReg() & BIT_TCAL_EN) PrintMessage(" TCAL enabled\n"); if ((drs->GetBoard(i))->GetCtrlReg() & BIT_ZERO_SUPP) PrintMessage(" ZERO_SUPP enabled\n"); if ((drs->GetBoard(i))->GetCtrlReg() & BIT_FREQ_AUTO_ADJ) PrintMessage(" FREQ_AUTO_ADJ enabled\n"); if ((drs->GetBoard(i))->GetCtrlReg() & BIT_ENABLE_TRIGGER) PrintMessage(" ENABLE_TRIGGER\n"); if ((drs->GetBoard(i))->GetCtrlReg() & BIT_LONG_START_PULSE) PrintMessage(" LONG_START_PULSE\n"); if ((drs->GetBoard(i))->GetCtrlReg() & BIT_DELAYED_START) PrintMessage(" DELAYED_START\n"); if ((drs->GetBoard(i))->GetCtrlReg() & BIT_ACAL_EN) PrintMessage(" ACAL enabled\n"); PrintMessage(" Trigger bus: 0X%08X\n", (drs->GetBoard(i))->GetTriggerBus()); if ((drs->GetBoard(i))->IsBusy()) { (drs->GetBoard(i))->ReadFrequency(0, &freq); PrintMessage(" Frequency0: %1.4lf GHz\n", freq); (drs->GetBoard(i))->ReadFrequency(1, &freq); PrintMessage(" Frequency1: %1.4lf GHz\n", freq); } else PrintMessage(" Domino wave stopped\n"); } } else PrintMessage("No DRS boards available!\n\n"); } } // Adress DRS boards void DAQReadout::cmd_board() { if (Match(Param[1],"all")) { FirstBoard = 0; LastBoard = drs->GetNumberOfBoards()-1; } else if (NParam==2 && atoi(Param[1])>=0 && atoi(Param[1])=0 && atoi(Param[1])0 && atoi(Param[2]) Execute shell command\n\n" "Items in <> are mandatory, in [] optional, | indicates mutual exclusive or.\n" "Test data can also be written if no DRS boards are available.\n" "Strings containing spaces have to be enclosed in \"double quotes\".\n"); } // Exit programm void DAQReadout::cmd_exit() { if (CmdFromSocket) { PrintMessage("Exit command not allowed over socket.\n"); return; } if (daq_state==active) PrintMessage("Issue \"stop\" first to stop daq\n"); else { Exit = true; if(SocketThread != NULL) pthread_kill(*SocketThread, SIGUSR1); } } // Set/get mode of feedback void DAQReadout::cmd_fmode() { if(Match(Param[1],"off")) HVFB->SetFBMode(FB_Off); if(Match(Param[1],"active")) HVFB->SetFBMode(FB_Active); if(Match(Param[1],"targets")) HVFB->SetFBMode(FB_Targets); HVFB->GetFBMode(); } // Set/get current number of events void DAQReadout::cmd_faverage() { if(NParam==1) PrintMessage("Current number of feedback events: %u (acting when %u events are reached)\n", HVFB->GetCurrentCount(), HVFB->GetNumAverages()); else if(atoi(Param[1])>=0) HVFB->SetNumAverages(atoi(Param[1])); else PrintUsage(); } // Set/get feedback gain void DAQReadout::cmd_fgain() { if(NParam==2) HVFB->SetGain(atof(Param[1])); PrintMessage("Feedback gain is %.2f\n", HVFB->GetGain()); } // Set/get target value void DAQReadout::cmd_ftarget() { if(NParam==1) HVFB->GetTargets(); else if(NParam!=5) PrintUsage(); else for (int i=FirstBoard; i<=LastBoard; i++) for (int j=0; jSetTarget(i,j,k,atof(Param[4])); } // Start response measurement void DAQReadout::cmd_fresponse() { if(NParam==1) HVFB->GetResponse(); else if(atof(Param[1])) HVFB->MeasureResponse(atof(Param[1])); else PrintUsage(); } // Print feedback configuration void DAQReadout::cmd_fconfig() { HVFB->PrintConfig(CmdFromSocket ? MsgToSocket : MsgToConsole); } // ---------------------------------------------- // ***** Utility function for DRS control ***** // ---------------------------------------------- // Start domino wave void DAQReadout::StartDRS() { for (int i=FirstBoard; i<=LastBoard; i++) drs->GetBoard(i)->StartDomino(); } // Stop domino wave void DAQReadout::StopDRS() { for (int i=FirstBoard; i<=LastBoard; i++) drs->GetBoard(i)->SoftTrigger(); } // Transfer data to memory void DAQReadout::ReadCalibratedDRSData() { for (int i=FirstBoard; i<=LastBoard; i++) { (drs->GetBoard(i))->TransferWaves(kNumberOfChannels*kNumberOfChips); for (int j=0; jGetBoard(i)->GetWave(0, j, WaveForm[i][0][j], true); // Chip #1 TriggerCell[i][0] = drs->GetBoard(i)->GetTriggerCell((unsigned int) 0); drs->GetBoard(i)->GetWave(1, j, WaveForm[i][1][j], true); // Chip #2 TriggerCell[i][1] = drs->GetBoard(i)->GetTriggerCell((unsigned int) 1); } } } // Read calibration file bool DAQReadout::ReadCalibration() { char dir[MAX_COM_SIZE]; getcwd(dir, sizeof(dir)); strcat(dir,"/calib"); for (int i=FirstBoard; i<=LastBoard; i++) { (drs->GetBoard(i))->SetCalibrationDirectory(dir); PrintMessage("Reading response calibration file for board %d from: \"%s\"\n", i, dir); for (int Chip=0; ChipGetBoard(i)->GetResponseCalibration()->ReadCalibration(Chip)==false) { CalibrationRead = false; return false; } } CalibrationRead = true; return true; } // Stop DAQ void DAQReadout::StopRun() { if(daq_state != active) PrintMessage("DAQ is not active.\n"); else { Stop = true; PrintMessage("DAQ will stop.\n"); } } // Set DOMINO mode void DAQReadout::SetDOMINOMode(int mode) { if (NumBoards) for (int i=FirstBoard; i<=LastBoard; i++) { (drs->GetBoard(i))->SetDominoMode(mode==1 ? 1:0); PrintMessage("Domino mode of board %d switched to %s.\n",i,mode==1 ? "continuous":"single shot"); } else PrintMessage("No DRS boards available\n"); } // Set DOMINO readout mode void DAQReadout::SetDOMINOReadMode(int mode) { if (NumBoards) for (int i=FirstBoard; i<=LastBoard; i++) { (drs->GetBoard(i))->SetReadoutMode(mode==1 ? 1:0); PrintMessage("Start readout of board %d from %s.\n",i,mode==1 ? "first bin":"stop position"); } else PrintMessage("No DRS boards available\n"); } // Set DOMINO wave mode void DAQReadout::SetDOMINOWaveMode(int mode) { if (NumBoards) for (int i=FirstBoard; i<=LastBoard; i++) { (drs->GetBoard(i))->SetDominoActive(mode==1 ? 1:0); PrintMessage("Domino wave of board %d is %s during readout\n",i,mode==1 ? "running":"stopped"); } else PrintMessage("No DRS boards available\n"); } // Delayed start on/off void DAQReadout::SetDelayedStart(int mode) { if (NumBoards) for (int i=FirstBoard; i<=LastBoard; i++) { (drs->GetBoard(i))->SetDelayedStart(mode==1 ? 1:0); PrintMessage("Delayed start of board %d is %s\n",i,mode==1 ? "on":"off"); } else PrintMessage("No DRS boards available\n"); } // Enable hardware trigger of all boards void DAQReadout::HWTrigger(int mode) { if (NumBoards) for (int i=FirstBoard; i<=LastBoard; i++) { drs->GetBoard(i)->EnableTrigger(mode==1 ? 1:0); PrintMessage("Hardware trigger of board %d %s\n",i,mode==1 ? "enabled":"disabled"); } else PrintMessage("No DRS boards available\n"); } // Set DRS sampling frequency void DAQReadout::SetDRSFrequency(double freq, bool Regulation) { double currentfreq; PrintMessage("Setting frequency %s regulation:\n",Regulation ? "with":"without"); for (int i=FirstBoard; i<=LastBoard; i++) { drs->GetBoard(i)->SetDebug(1); if (Regulation ? drs->GetBoard(i)->RegulateFrequency(freq) : drs->GetBoard(i)->SetFrequency(freq)) { drs->GetBoard(i)->ReadFrequency(0, ¤tfreq); DRSFreq[i] = freq; PrintMessage("Domino wave of board %d is running at %1.3lf GHz\n",i,currentfreq); } else { DRSFreq[i] = 0; PrintMessage("Warning: Domino wave of board %d has changed but not reached the requested value\n",i); } } } // Check if DRS is sampling bool DAQReadout::IsDRSBusy() { for (int i=FirstBoard; i<=LastBoard; i++) if ((drs->GetBoard(i))->IsBusy()) return true; return false; } // Check if DRS frequency is set bool DAQReadout::IsDRSFreqSet() { for (int i=FirstBoard; i<=LastBoard; i++) if (DRSFreq[i]==0) { PrintMessage("DRS sampling frequency of board %d not set!\n",i); return false; } return true; } // Open new raw data file bool DAQReadout::OpenRawFile() { time_t rawtime; struct tm *timeinfo; char RunDate[MAX_COM_SIZE], Buffer[MAX_COM_SIZE]; // Write run date to status structure (if after 13:00, use next day) time(&rawtime); timeinfo = gmtime(&rawtime); if(timeinfo->tm_hour>=13) rawtime += 12*60*60; timeinfo = gmtime(&rawtime); snprintf(RunDate,sizeof(RunDate),"%d%02d%02d",timeinfo->tm_year+1900,timeinfo->tm_mon + 1,timeinfo->tm_mday); // Create direcory if not existing (ignore error if already existing) and change to it snprintf(Buffer, sizeof(Buffer), "%s/%s", fRawDataPath, RunDate); if(mkdir(Buffer, S_IRWXU|S_IRWXG)==-1 && errno!=EEXIST) { PrintMessage("\rError: Could not create direcory \"%s\" (%s)\n", Buffer, strerror(errno)); return false; } // Generate filename snprintf(FileName,sizeof(FileName),"%s/%s/%s_D1_%.8u.%.3u_%c_%s.raw", fRawDataPath, RunDate, RunDate,RunNumber,FileNumber,toupper(daq_runtype_str[daq_runtype][0]),RHeader->Description); // Open file with rwx right for owner and group, never overwrite file Rawfile = open(FileName,O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); if(Rawfile==-1) { PrintMessage("\rError: Could not open file \"%s\" (%s)\n", FileName, strerror(errno)); return false; } return true; } // Write run header and board structures (revision number is zero for svn modified working copy) bool DAQReadout::WriteRunHeader() { struct timeval Time; RHeader->MagicNum = MAGICNUM_OPEN; RHeader->DataFormat = DATA_FORMAT; RHeader->SoftwareRevision = atoi(REVISION) * (strchr(REVISION, 'M')==NULL ? 1:-1); RHeader->RunHeaderSize = sizeof(RunHeader); RHeader->EventHeaderSize = sizeof(EventHeader); RHeader->BoardStructureSize = sizeof(BoardStructure); RHeader->Identification = IDENTIFICATION; RHeader->Type = daq_runtype; RHeader->RunNumber = RunNumber; RHeader->FileNumber = FileNumber; gettimeofday(&Time, NULL); RHeader->StartSecond = Time.tv_sec; RHeader->StartMicrosecond = Time.tv_usec; RHeader->NBoards = NumBoards==0 && daq_runtype==test ? 1 : (LastBoard - FirstBoard) + 1; RHeader->NChips = kNumberOfChips; RHeader->NChannels = kNumberOfChannels; RHeader->NBytes = sizeof(short); RHeader->Offset = fFirstSample; RHeader->Samples = fSamples; if(write(Rawfile, RHeader, sizeof(RunHeader)) != sizeof(RunHeader)) { PrintMessage("Error: Could not write run header, terminating run (%s)\n", strerror(errno)); return false; } for (int i=FirstBoard; i<=LastBoard; i++) { BStruct[i].SerialNo = drs->GetBoard(i)->GetCMCSerialNumber(); BStruct[i].BoardTemp = drs->GetBoard(i)->GetTemperature(); BStruct[i].NomFreq = DRSFreq[i]; BStruct[i].ScaleFactor = drs->GetBoard(i)->GetPrecision(); } // In case no boards are available, dummy data is written for one board structure if (NumBoards == 0) { BStruct[0].NomFreq = 1; BStruct[0].ScaleFactor = 0.1; } if(write(Rawfile, &BStruct[FirstBoard], sizeof(BoardStructure)*(LastBoard-FirstBoard+1+(NumBoards==0))) != (ssize_t) sizeof(BoardStructure)*(LastBoard-FirstBoard+1+(NumBoards==0))) { PrintMessage("Error: Could not write (all) board structures, terminating run (%s)\n", strerror(errno)); return false; } return true; } // Update run header before closing file bool DAQReadout::UpdateRunHeader(unsigned int Events, bool Error) { struct timeval Time; RHeader->MagicNum = Error==false ? MAGICNUM_CLOSED:MAGICNUM_ERROR; RHeader->Events = Events; gettimeofday(&Time, NULL); RHeader->EndSecond = Time.tv_sec; RHeader->EndMicrosecond = Time.tv_usec; if(lseek(Rawfile,0,SEEK_SET)==-1) { PrintMessage("Error: Could not rewind file to write updated run header, terminating run (%s)\n", strerror(errno)); return false; } if(write(Rawfile, RHeader, sizeof(RunHeader)) != sizeof(RunHeader)) { PrintMessage("Error: Could not write updated run header, terminating run (%s)\n", strerror(errno)); return false; } return true; } // Write event bool DAQReadout::WriteEvent() { // Event header struct timeval Time; gettimeofday(&Time, NULL); EHeader->EventNumber = NumEvents; EHeader->TriggerType = daq_runtype==data ? 0 : 1; EHeader->Second = Time.tv_sec; EHeader->Microsecond = Time.tv_usec; EHeader->EventSize = sizeof(short)*RHeader->NBoards*RHeader->NChips*RHeader->NChannels*RHeader->Samples + sizeof(int)*RHeader->NBoards*RHeader->NChips; if(write(Rawfile, EHeader, sizeof(EventHeader)) != sizeof(EventHeader)) { PrintMessage("Error: Could not write event header, terminating run (%s)\n", strerror(errno)); return false; } // Event data (It is required that at least three chunks can be written with writev(), therefore // IOV_MAX>=3 is checked at startup unsigned int Start, Count = 0; ssize_t WriteResult, Size = 0; struct iovec DataPart[IOV_MAX]; // First chunk: trigger cells DataPart[Count].iov_base = (char *) TriggerCell + FirstBoard*kNumberOfChips*sizeof(int); // TriggerCell is without cast a pointer to an 8-byte unit (two ints) ! DataPart[Count++].iov_len = RHeader->NBoards * kNumberOfChips * sizeof(int); Size += DataPart[Count-1].iov_len; // Remaining chunks: ADC data (two chucks per channel if wrap around of pipeline occurred) for (int i=FirstBoard; (i<=LastBoard + (NumBoards==0)); i++) { for (unsigned int k=0; kNChips; k++) { Start = (TriggerCell[i][k]-fFirstSample+kNumberOfBins) % kNumberOfBins; // Start bin for this chip for (unsigned int l=0; lNChannels; l++) { DataPart[Count].iov_base = &WaveForm[i][k][l][Start]; DataPart[Count++].iov_len = (Start+fSamples=IOV_MAX-1 || (l==(RHeader->NChannels-1) && k==(RHeader->NChips-1) && i==(LastBoard+(NumBoards==0)))) { if ((WriteResult=writev(Rawfile, DataPart, Count)) != (int) Size) { if (WriteResult==-1) PrintMessage("Error: Could not write event data, terminating run (%s)\n", strerror(errno)); else PrintMessage("Error: Could only write %u out of %u bytes of event data, terminating run\n", WriteResult,Count*DataPart[0].iov_len); return false; } Count = 0; Size = 0; } } // Channels } // Chips } // Boards return true; } // Print configuration to target void DAQReadout::PrintConfig(int Target) { PrintMessage(Target, "LogFile: %s\tMaxLogLines: %u\tRawDataPath: %s\n" "DefaultFrequency: %.2f\tFirstSample: %d\tSamples: %u\n" "MinDiskSpaceMB: %u\tMaxFileSizeMB: %d\tCCPort: %d\n" "FirstVMESlot: %d\t\tLastVMESlot: %d\n" "SlowDataPath: %s\tHVFeedbackConfig: %s\n", fLogFile,fMaxLogLines,fRawDataPath,fDefaultFrequency,fFirstSample,fSamples,fMinDiskSpaceMB, fMaxFileSizeMB,fCCPort,fFirstVMESlot,fLastVMESlot,fSlowDataPath,fHVFeedbackConfig); } // Print usage text for command void DAQReadout::PrintUsage() { PrintMessage("Usage: %s %s\n", CommandList[CmdNumber].Name, CommandList[CmdNumber].Parameters); } // Print message to selected target void DAQReadout::PrintMessage(int Target, const char *Format, ...) { va_list ArgumentPointer; va_start(ArgumentPointer, Format); DoPrintMessage(Format, ArgumentPointer, Target); va_end(ArgumentPointer); } // Print message to log file, and screen or socket (depending on command origin) void DAQReadout::PrintMessage(const char *Format, ...) { va_list ArgumentPointer; va_start(ArgumentPointer, Format); if(CmdFromSocket) DoPrintMessage(Format, ArgumentPointer, MsgToSocket|MsgToLog); else DoPrintMessage(Format, ArgumentPointer, MsgToConsole|MsgToLog); va_end(ArgumentPointer); } // Function doing the actual printing work // Note: Be careful when overloading variadic functions. va_list is // in gcc an int, which can be interpreted as char *... void DAQReadout::DoPrintMessage(const char *Format, va_list ArgumentPointer, int Target) { static char Textbuffer[MAX_COM_SIZE]; // static: it is only allocated once memset(Textbuffer, 0, sizeof(Textbuffer)); vsnprintf(Textbuffer, sizeof(Textbuffer), Format, ArgumentPointer); // Print to console if(Target & MsgToConsole) { if(strlen(Textbuffer)>0 && Textbuffer[strlen(Textbuffer)-1]=='\n') { printf("\r%s%s", Textbuffer, Prompt); // New prompt fflush(stdout); } else printf("%s", Textbuffer); } // Print to log file if((Target & MsgToLog) && Logfile!=NULL) { time_t Time; strftime(Textbuffer+strlen(Textbuffer)+1,MAX_COM_SIZE-strlen(Textbuffer)-1, "%d/%m/%y %X", localtime(&(Time=time(NULL)))); fprintf(Logfile, "%s: %s", Textbuffer+strlen(Textbuffer)+1, Textbuffer); fflush(Logfile); } // Print to socket if((Target & MsgToSocket) && Socket!=-1) write(Socket, Textbuffer, strlen(Textbuffer)); } // --------------------------------------- // ***** Various utility functions ***** // --------------------------------------- // Check if two strings match (min 1 character must match) bool Match(const char *str, const char *cmd) { return strncasecmp(str,cmd,strlen(str)==0 ? 1:strlen(str)) ? false:true; } // Return current available storage space in given directory int CheckDisk(char *Directory) { struct statfs FileSystemStats; statfs(Directory, &FileSystemStats); return FileSystemStats.f_bavail / 1024 * (FileSystemStats.f_bsize / 1024); } // Parse command line for white space and double-quote separated tokens int ParseInput(char* Command, const char *Param[]) { int Count=0; while(Count1 read array) while ((card_val=strtok(NULL," \t\n")) != NULL && Count++NumEvents = 0; m->FileNumber = 0; m->HVFB->ClearAverages(); gettimeofday(&StartTime, NULL); m->PrintMessage("\rStarting run #%d (%s) with %u event(s)\n",m->RunNumber,daq_runtype_str[m->daq_runtype],m->NumEventsRequested); do { // Check if enough disk space is left if (CheckDisk(m->fRawDataPath) <= m->fMinDiskSpaceMB+m->fMaxFileSizeMB) { m->PrintMessage("\rError: Disk space after next file (max. %d MByte) below %d MByte\n",m->fMaxFileSizeMB,m->fMinDiskSpaceMB); break; } // Init run header, open raw file, write run header if (!m->OpenRawFile()) break; m->PrintMessage("\rData file \"%s\" opened.\n",m->FileName); EventsInFile = 0; FileSize = 0; WriteError |= !m->WriteRunHeader(); if (m->daq_runtype != test) m->StartDRS(); // Take data until finished, stopped or file too large while ((m->NumEventsNumEventsRequested || m->NumEventsRequested==0) && !m->Stop && FileSize/1024/1024fMaxFileSizeMB && !WriteError) { if (m->daq_runtype == data) while (m->IsDRSBusy()); // Wait for hardware trigger (if DAQ stopped, DRS will not be busy anymore) else if (m->daq_runtype == pedestal) m->StopDRS(); // ..or for software trigger // Read event data via VME or generate test data (for one board if no boards available) if (m->daq_runtype != test) { m->ReadCalibratedDRSData(); m->StartDRS(); // Restart here: writing data is in parallel to waiting for next trigger } else { double Period = ((double) rand())/RAND_MAX*20; for (long unsigned int i=0; iRHeader->NBoards*kNumberOfChips*kNumberOfChannels*kNumberOfBins; i++) *((short *) m->WaveForm+i) = (short) (sin(i/Period)*1000); } // Write event to disk and update file size EventsInFile++; m->NumEvents++; WriteError |= !m->WriteEvent(); if((FileSize = lseek(m->Rawfile, 0, SEEK_CUR)) == -1) { m->PrintMessage("Error: Could not determine file size, terminating run (%s)\n", strerror(errno)); WriteError = true; } // Call feedback to process event m->HVFB->ProcessEvent(); } // Write updated run header, close file RunSize += FileSize; WriteError |= !m->UpdateRunHeader(EventsInFile, WriteError); if(close(m->Rawfile)==-1) m->PrintMessage("Error: Could not close data file (%s)\n", strerror(errno)); else m->PrintMessage("Data file closed (size %lu MByte).\n", FileSize/1024/1024); m->FileNumber += 1; } while((m->NumEventsNumEventsRequested || m->NumEventsRequested==0) && !m->Stop && !WriteError); m->StopDRS(); // Print run summary to screen if(!WriteError) m->PrintMessage("\rRun #%d (%s) %s, %d events\n",m->RunNumber,daq_runtype_str[m->daq_runtype],(m->NumEvents == m->NumEventsRequested) ? "completed":"stopped",m->NumEvents); else m->PrintMessage("\rRun #%d (%s) aborted due to error after %d events\n",m->RunNumber,daq_runtype_str[m->daq_runtype],m->NumEvents); // Write run summary to slow data file m->SlowDataClass->NewEntry("Runinfo"); m->SlowDataClass->AddToEntry("%d %s %s %d %d %s", m->RunNumber, WriteError?"Error":"OK", daq_runtype_str[m->daq_runtype], m->NumEvents, m->FileNumber, m->RHeader->Description); if(m->SlowDataClass->ErrorCode != 0) { m->PrintMessage("Error, could not write DAQ data to file (%s), file closed\n", strerror(m->SlowDataClass->ErrorCode)); } // Print run statistics if (m->NumEvents>0 && !WriteError) { gettimeofday(&StopTime, NULL); float RunTime = StopTime.tv_sec-StartTime.tv_sec + (StopTime.tv_usec-StartTime.tv_usec)*1e-6; m->PrintMessage("Time for run %.2f seconds, trigger rate %.2f Hz.\n", RunTime, m->NumEvents/RunTime); m->PrintMessage("Run size %llu MByte, data rate %.1f MByte/s.\n", RunSize/1024/1024, RunSize/1024.0/1024/RunTime); } m->daq_state = stopped; } /********************************************************************\ DAQ Thread - no disk writing, only hardware trigger, for feedback tests \********************************************************************/ void DAQ_Silent(DAQReadout *m) { m->PrintMessage("\rData taking started\n"); do { // Start DRS and wait for hardware trigger m->StartDRS(); while (m->IsDRSBusy()); // Read event data via VME and call feedback m->ReadCalibratedDRSData(); m->HVFB->ProcessEvent(); } while(!m->Stop); m->PrintMessage("\rData taking stopped\n"); m->daq_state = stopped; }