/* Copyright (C) 2022 Facility for Antiproton and Ion Research in Europe, Darmstadt SPDX-License-Identifier: GPL-3.0-only Authors: Pierre-Alain Loizeau[committer], Dominik Smith */ #include "CbmDevBuildEvents.h" /// CBM headers #include "CbmMQDefs.h" /// FAIRROOT headers #include "FairMQLogger.h" #include "FairMQProgOptions.h" #include "FairRootFileSink.h" #include "FairRootManager.h" #include "FairRunOnline.h" #include "BoostSerializer.h" #include "RootSerializer.h" /// FAIRSOFT headers (geant, boost, ...) #include "TimesliceMetaData.h" #include <boost/archive/binary_iarchive.hpp> #include <boost/serialization/utility.hpp> /// C/C++ headers #include <array> #include <iomanip> #include <stdexcept> #include <string> struct InitTaskError : std::runtime_error { using std::runtime_error::runtime_error; }; using namespace std; CbmDevBuildEvents::CbmDevBuildEvents() {} void CbmDevBuildEvents::InitTask() try { /// Read options from executable LOG(info) << "Init options for CbmDevBuildEvents."; fsOutputFileName = fConfig->GetValue<std::string>("OutFileName"); //For storage of events // Event builder algorithm params const std::vector<std::string> vsSetEvbuildWin = fConfig->GetValue<std::vector<std::string>>("SetEvbuildWin"); fsChannelNameDataInput = fConfig->GetValue<std::string>("TrigNameIn"); fsChannelNameDataOutput = fConfig->GetValue<std::string>("EvtNameOut"); fsAllowedChannels[0] = fsChannelNameDataInput; /// Prepare root output if ("" != fsOutputFileName) { fpRun = new FairRunOnline(); fpFairRootMgr = FairRootManager::Instance(); fpFairRootMgr->SetSink(new FairRootFileSink(fsOutputFileName)); if (nullptr == fpFairRootMgr->GetOutFile()) { throw InitTaskError("Could not open root file"); } LOG(info) << "Init Root Output to " << fsOutputFileName; fpFairRootMgr->InitSink(); /// Create storage objects fEventsSelOut = new std::vector<CbmDigiEvent>(); fpFairRootMgr->RegisterAny("DigiEvent", fEventsSelOut, kTRUE); fTimeSliceMetaDataArrayOut = new TClonesArray("TimesliceMetaData", 1); fpFairRootMgr->Register("TimesliceMetaData", "TS Meta Data", fTimeSliceMetaDataArrayOut, kTRUE); fpFairRootMgr->WriteFolder(); } // if( "" != fsOutputFileName ) // Get the information about created channels from the device // Check if the defined channels from the topology (by name) // are in the list of channels which are possible/allowed // for the device // The idea is to check at initilization if the devices are // properly connected. For the time beeing this is done with a // nameing convention. It is not avoided that someone sends other // data on this channel. //logger::SetLogLevel("INFO"); int noChannel = fChannels.size(); LOG(info) << "Number of defined channels: " << noChannel; for (auto const& entry : fChannels) { LOG(info) << "Channel name: " << entry.first; if (std::string::npos != entry.first.find(fsChannelNameDataInput)) { if (!IsChannelNameAllowed(entry.first)) throw InitTaskError("Channel name does not match."); OnData(entry.first, &CbmDevBuildEvents::HandleData); } } /// Extract event builder window to add if any for (std::vector<std::string>::const_iterator itStrEvbuildWin = vsSetEvbuildWin.begin(); itStrEvbuildWin != vsSetEvbuildWin.end(); ++itStrEvbuildWin) { size_t charPosDel = (*itStrEvbuildWin).find(','); if (std::string::npos == charPosDel) { LOG(info) << "CbmDevBuildEvents::InitTask => " << "Trying to set event builder window with invalid option pattern, ignored! " << " (Should be ECbmModuleId,dWinBeg,dWinEnd but instead found " << (*itStrEvbuildWin) << " )"; continue; } /// Detector Enum Tag std::string sSelDet = (*itStrEvbuildWin).substr(0, charPosDel); const ECbmModuleId selDet = GetDetectorId(sSelDet); if (ECbmModuleId::kNotExist == selDet) { LOG(info) << "CbmDevBuildEvents::InitTask => " << "Trying to set trigger window for unsupported detector, ignored! " << sSelDet; continue; } /// Window beginning charPosDel++; std::string sNext = (*itStrEvbuildWin).substr(charPosDel); charPosDel = sNext.find(','); if (std::string::npos == charPosDel) { LOG(info) << "CbmDevBuildEvents::InitTask => " << "Trying to set event builder window with invalid option pattern, ignored! " << " (Should be ECbmModuleId,dWinBeg,dWinEnd but instead found " << (*itStrEvbuildWin) << " )"; continue; } double dWinBeg = std::stod(sNext.substr(0, charPosDel)); /// Window end charPosDel++; double dWinEnd = std::stod(sNext.substr(charPosDel)); fEvbuildAlgo.SetEventWindow(selDet, dWinBeg, dWinEnd); } } catch (InitTaskError& e) { LOG(error) << e.what(); // Wrapper defined in CbmMQDefs.h to support different FairMQ versions cbm::mq::ChangeState(this, cbm::mq::Transition::ErrorFound); } ECbmModuleId CbmDevBuildEvents::GetDetectorId(std::string detName) { /// FIXME: Disable clang formatting for now as it corrupts all alignment /* clang-format off */ ECbmModuleId detId = ("kT0" == detName ? ECbmModuleId::kT0 : ("kSts" == detName ? ECbmModuleId::kSts : ("kMuch" == detName ? ECbmModuleId::kMuch : ("kTrd" == detName ? ECbmModuleId::kTrd : ("kTrd2d" == detName ? ECbmModuleId::kTrd2d : ("kTof" == detName ? ECbmModuleId::kTof : ("kRich" == detName ? ECbmModuleId::kRich : ("kPsd" == detName ? ECbmModuleId::kPsd : ECbmModuleId::kNotExist)))))))); return detId; /// FIXME: Re-enable clang formatting after formatted lines /* clang-format on */ } bool CbmDevBuildEvents::IsChannelNameAllowed(std::string channelName) { for (auto const& entry : fsAllowedChannels) { std::size_t pos1 = channelName.find(entry); if (pos1 != std::string::npos) { const vector<std::string>::const_iterator pos = std::find(fsAllowedChannels.begin(), fsAllowedChannels.end(), entry); const vector<std::string>::size_type idx = pos - fsAllowedChannels.begin(); LOG(info) << "Found " << entry << " in " << channelName; LOG(info) << "Channel name " << channelName << " found in list of allowed channel names at position " << idx; return true; } } LOG(info) << "Channel name " << channelName << " not found in list of allowed channel names."; LOG(error) << "Stop device."; return false; } // handler is called whenever a message arrives on "data", with a reference to the message and a sub-channel index (here 0) bool CbmDevBuildEvents::HandleData(FairMQParts& parts, int /*index*/) { fulNumMessages++; LOG(info) << "Received message number " << fulNumMessages << " with " << parts.Size() << " parts" << ", size0: " << parts.At(0)->GetSize(); if (0 == fulNumMessages % 10000) LOG(info) << "Received " << fulNumMessages << " messages"; /// Extract unpacked data from input message uint32_t uPartIdx = 0; /// TS CbmDigiTimeslice ts; std::string msgStrTS(static_cast<char*>(parts.At(uPartIdx)->GetData()), (parts.At(uPartIdx))->GetSize()); std::istringstream issTS(msgStrTS); boost::archive::binary_iarchive inputArchiveTS(issTS); inputArchiveTS >> ts; ++uPartIdx; /// TS metadata TimesliceMetaData* tsMetaData = new TimesliceMetaData(); RootSerializer().Deserialize(*parts.At(uPartIdx), tsMetaData); ++uPartIdx; /// Triggers std::vector<double> triggers; std::string msgStrTrig(static_cast<char*>(parts.At(uPartIdx)->GetData()), (parts.At(uPartIdx))->GetSize()); std::istringstream issTrig(msgStrTrig); boost::archive::binary_iarchive inputArchiveTrig(issTrig); inputArchiveTrig >> triggers; ++uPartIdx; //if (1 == fulNumMessages) { /// First message received (do TS metadata stuff here) //fpAlgo->SetTsParameters(0, fTsMetaDataOut->GetDuration(), fTsMetaDataOut->GetOverlapDuration()); //} LOG(debug) << "T0 Vector size: " << ts.fData.fT0.fDigis.size(); LOG(debug) << "STS Vector size: " << ts.fData.fSts.fDigis.size(); LOG(debug) << "MUCH Vector size: " << ts.fData.fMuch.fDigis.size(); LOG(debug) << "TRD Vector size: " << ts.fData.fTrd.fDigis.size(); LOG(debug) << "TOF Vector size: " << ts.fData.fTof.fDigis.size(); LOG(debug) << "RICH Vector size: " << ts.fData.fRich.fDigis.size(); LOG(debug) << "PSD Vector size: " << ts.fData.fPsd.fDigis.size(); LOG(debug) << "triggers: " << triggers.size(); /// Create events std::vector<CbmDigiEvent> vEvents = fEvbuildAlgo(ts, triggers).first; LOG(debug) << "vEvents size: " << vEvents.size(); /// Send output message if (!SendEvents(vEvents, tsMetaData)) { return false; } /// Write events to file // FIXME: poor man solution with lots of data copy until we undertand how to properly deal /// with FairMq messages ownership and memory managment if ("" != fsOutputFileName) { (*fEventsSelOut) = std::move(vEvents); LOG(debug) << "fEventSel size: " << fEventsSelOut->size(); new ((*fTimeSliceMetaDataArrayOut)[fTimeSliceMetaDataArrayOut->GetEntriesFast()]) TimesliceMetaData(std::move(*tsMetaData)); DumpTreeEntry(); fTimeSliceMetaDataArrayOut->Clear(); fEventsSelOut->clear(); } return true; } void CbmDevBuildEvents::DumpTreeEntry() { // Unpacked digis + CbmEvent output to root file /// FairRunOnline style fpFairRootMgr->StoreWriteoutBufferData(fpFairRootMgr->GetEventTime()); fpFairRootMgr->Fill(); fpFairRootMgr->DeleteOldWriteoutBufferData(); } bool CbmDevBuildEvents::SendEvents(const std::vector<CbmDigiEvent>& vEvents, const TimesliceMetaData* tsMetaData) { LOG(debug) << "Vector size: " << vEvents.size(); FairMQParts partsOut; // Prepare TS meta data FairMQMessagePtr messTsMeta(NewMessage()); RootSerializer().Serialize(*messTsMeta, tsMetaData); partsOut.AddPart(std::move(messTsMeta)); // Prepare event vector. std::stringstream ossEvt; boost::archive::binary_oarchive oaEvt(ossEvt); oaEvt << vEvents; std::string* strMsgEvt = new std::string(ossEvt.str()); partsOut.AddPart(NewMessage( const_cast<char*>(strMsgEvt->c_str()), // data strMsgEvt->length(), // size [](void*, void* object) { delete static_cast<std::string*>(object); }, strMsgEvt)); // object that manages the data if (Send(partsOut, fsChannelNameDataOutput) < 0) { LOG(error) << "Problem sending data to " << fsChannelNameDataOutput; return false; } return true; } void CbmDevBuildEvents::Finish() { if ("" != fsOutputFileName) { // Clean closure of output to root file fpFairRootMgr->Write(); fpFairRootMgr->CloseSink(); } fbFinishDone = kTRUE; } CbmDevBuildEvents::~CbmDevBuildEvents() { /// Close things properly if not alredy done if (!fbFinishDone) Finish(); if (fEventsSelOut) { delete fEventsSelOut; } if (fpRun) { delete fpRun; } if (fTimeSliceMetaDataArrayOut) { delete fTimeSliceMetaDataArrayOut; } }