1515#include < stdexcept>
1616#include < vector>
1717#include < string>
18+ #include < fstream>
1819#include < algorithm>
1920#include < cassert>
2021#include < cmath>
3031#include " Framework/DataProcessorSpec.h"
3132#include " Framework/WorkflowSpec.h"
3233#include " Framework/InputRecordWalker.h"
34+ #include " Framework/DataRefUtils.h"
3335#include " DPLUtils/RawParser.h"
3436#include " Headers/DataHeader.h"
3537#include " Headers/DataHeaderHelpers.h"
4547#include " TPCBase/Utils.h"
4648#include " TPCBase/RDHUtils.h"
4749#include " TPCBase/Mapper.h"
50+ #include " TPCWorkflow/ProcessingHelpers.h"
4851
4952using namespace o2 ::framework;
5053using o2::constants::lhc::LHCMaxBunches;
@@ -65,9 +68,13 @@ class IDCToVectorDevice : public o2::framework::Task
6568 void init (o2::framework::InitContext& ic) final
6669 {
6770 // set up ADC value filling
68- if (ic.options ().get <bool >(" write-debug" )) {
69- mDebugStream = std::make_unique<o2::utils::TreeStreamRedirector>(" idc_vector_debug.root" , " recreate" );
70- }
71+ mWriteDebug = ic.options ().get <bool >(" write-debug" );
72+ mWriteDebugOnError = ic.options ().get <bool >(" write-debug-on-error" );
73+ mWriteRawDataOnError = ic.options ().get <bool >(" write-raw-data-on-error" );
74+ mRawDataType = ic.options ().get <int >(" raw-data-type" );
75+
76+ mDebugStreamFileName = ic.options ().get <std::string>(" debug-file-name" ).data ();
77+ mRawOutputFileName = ic.options ().get <std::string>(" raw-file-name" ).data ();
7178
7279 mSwapLinks = ic.options ().get <bool >(" swap-links" );
7380
@@ -111,13 +118,32 @@ class IDCToVectorDevice : public o2::framework::Task
111118
112119 void run (o2::framework::ProcessingContext& pc) final
113120 {
121+ const auto runNumber = processing_helpers::getRunNumber (pc);
114122 std::vector<InputSpec> filter = {{" check" , ConcreteDataTypeMatcher{o2::header::gDataOriginTPC , " RAWDATA" }, Lifetime::Timeframe}}; // TODO: Change to IDC when changed in DD
115123 const auto & mapper = Mapper::instance ();
116124
125+ // open files if necessary
126+ if ((mWriteDebug || mWriteDebugOnError ) && !mDebugStream ) {
127+ const auto debugFileName = fmt::format (mDebugStreamFileName , fmt::arg (" run" , runNumber));
128+ LOGP (info, " creating debug stream {}" , debugFileName);
129+ mDebugStream = std::make_unique<o2::utils::TreeStreamRedirector>(debugFileName.data (), " recreate" );
130+ }
131+
132+ if (mWriteRawDataOnError && !mRawOutputFile .is_open ()) {
133+ std::string_view rawType = (mRawDataType < 2 ) ? " tf" : " raw" ;
134+ if (mRawDataType == 4 ) {
135+ rawType = " idc.raw" ;
136+ }
137+ const auto rawFileName = fmt::format (mRawOutputFileName , fmt::arg (" run" , runNumber), fmt::arg (" raw_type" , rawType));
138+ LOGP (info, " creating raw debug file {}" , rawFileName);
139+ mRawOutputFile .open (rawFileName, std::ios::binary);
140+ }
141+
117142 uint32_t heartbeatOrbit = 0 ;
118143 uint32_t heartbeatBC = 0 ;
119144 uint32_t tfCounter = 0 ;
120145 bool first = true ;
146+ bool hasErrors = false ;
121147
122148 CalPad* pedestals = mPedestal .get ();
123149
@@ -187,7 +213,8 @@ class IDCToVectorDevice : public o2::framework::Task
187213 } else if (infoIt == infoVec.end ()) {
188214 auto & lastInfo = infoVec.back ();
189215 if ((orbit - lastInfo.heartbeatOrbit ) != mNOrbitsIDC ) {
190- LOGP (error, " received packet with invalid jump in idc orbit ({} - {} == {} != {})" , orbit, lastInfo.heartbeatOrbit , orbit - lastInfo.heartbeatOrbit , mNOrbitsIDC );
216+ LOGP (error, " received packet with invalid jump in idc orbit ({} - {} == {} != {})" , orbit, lastInfo.heartbeatOrbit , int (orbit) - int (lastInfo.heartbeatOrbit ), mNOrbitsIDC );
217+ hasErrors = true ;
191218 }
192219 infoVec.emplace_back (orbit, bc);
193220 infoIt = infoVec.end () - 1 ;
@@ -265,26 +292,48 @@ class IDCToVectorDevice : public o2::framework::Task
265292 }
266293 }
267294
268- if (mDebugStream ) {
295+ hasErrors |= snapshotIDCs (pc.outputs (), tfCounter);
296+
297+ if (mWriteDebug || (mWriteDebugOnError && hasErrors)) {
269298 writeDebugOutput (tfCounter);
270299 }
271- snapshotIDCs (pc.outputs ());
300+
301+ if (mWriteRawDataOnError && hasErrors) {
302+ writeRawData (pc.inputs ());
303+ }
304+
305+ // clear output
306+ initIDC ();
272307 }
273308
274- void
275- endOfStream (o2::framework::EndOfStreamContext& ec) final
309+ void closeFiles ()
276310 {
277- LOGP (info, " endOfStream " );
278- ec. services (). get <ControlService>(). readyToQuit (QuitRequest::Me);
311+ LOGP (info, " closeFiles " );
312+
279313 if (mDebugStream ) {
280314 // set some default aliases
281315 auto & stream = (*mDebugStream ) << " idcs" ;
282316 auto & tree = stream.getTree ();
283317 tree.SetAlias (" sector" , " int(cru/10)" );
284318 mDebugStream ->Close ();
319+ mDebugStream .reset (nullptr );
320+ mRawOutputFile .close ();
285321 }
286322 }
287323
324+ void stop () final
325+ {
326+ LOGP (info, " stop" );
327+ closeFiles ();
328+ }
329+
330+ void endOfStream (o2::framework::EndOfStreamContext& ec) final
331+ {
332+ LOGP (info, " endOfStream" );
333+ // ec.services().get<ControlService>().readyToQuit(QuitRequest::Me);
334+ closeFiles ();
335+ }
336+
288337 private:
289338 // / IDC information for each cru
290339 struct IDCInfo {
@@ -307,58 +356,71 @@ class IDCToVectorDevice : public o2::framework::Task
307356 const int mNOrbitsIDC {12 }; // /< number of orbits over which IDCs are integrated, TODO: take from IDC header
308357 const int mTimeStampsPerIntegrationInterval {(LHCMaxBunches * mNOrbitsIDC ) / LHCBCPERTIMEBIN }; // /< number of time stamps for each integration interval (5346)
309358 const uint32_t mMaxIDCPerTF {uint32_t (std::ceil (256 .f / mNOrbitsIDC ))}; // /< maximum number of IDCs expected per TF, TODO: better way to get max number of orbits
359+ int mRawDataType {0 }; // /< type of raw data to dump in case of errors
310360 bool mSwapLinks {false }; // /< swap links to circumvent bug in FW
361+ bool mWriteDebug {false }; // /< write a debug output
362+ bool mWriteDebugOnError {false }; // /< write a debug output in case of errors
363+ bool mWriteRawDataOnError {false }; // /< write raw data in case of errors
311364 std::vector<uint32_t > mCRUs ; // /< CRUs expected for this device
312365 std::unordered_map<uint32_t , std::vector<float >> mIDCvectors ; // /< decoded IDCs per cru for each pad in the region over all IDC packets in the TF
313366 std::unordered_map<uint32_t , std::vector<IDCInfo>> mIDCInfos ; // /< IDC packet information within the TF
367+ std::string mDebugStreamFileName ; // /< name of the debug stream output file
314368 std::unique_ptr<o2::utils::TreeStreamRedirector> mDebugStream ; // /< debug output streamer
315369 std::unique_ptr<CalPad> mPedestal {}; // /< noise and pedestal values
370+ std::ofstream mRawOutputFile ; // /< raw output file
371+ std::string mRawOutputFileName ; // /< name of the raw output file
316372
317373 // ____________________________________________________________________________
318- void snapshotIDCs (DataAllocator& output)
374+ bool snapshotIDCs (DataAllocator& output, uint32_t tfCounter )
319375 {
320376 LOGP (debug, " snapshotIDCs" );
321377
322378 // check integrety of data between CRUs
323- size_t orbitsInTF = 0 ;
379+ size_t packetsInTF = 0 ;
324380 std::vector<IDCInfo> const * infVecComp = nullptr ;
325381 std::vector<uint64_t > orbitBCInfo;
382+ bool hasErrors = false ;
383+
384+ for (const auto & [cru, infVec] : mIDCInfos ) {
385+ packetsInTF = std::max (infVec.size (), packetsInTF);
386+ }
326387
327388 for (const auto & [cru, infVec] : mIDCInfos ) {
328389
329390 for (const auto & inf : infVec) {
330391 if (!inf.hasBothEPs ()) {
331- LOGP (fatal, " IDC CRU {:3}: data missing at ({:8}, {:4}) for one or both end points {:02b}" , cru, inf.heartbeatOrbit , inf.heartbeatBC , inf.epSeen );
392+ LOGP (error, " IDC CRU {:3}: data missing at ({:8}, {:4}) for one or both end points {:02b} in TF {}" , cru, inf.heartbeatOrbit , inf.heartbeatBC , inf.epSeen , tfCounter);
393+ hasErrors = true ;
332394 }
333395 }
334396
335397 if (!infVecComp) {
336398 infVecComp = &infVec;
337- orbitsInTF = infVec.size ();
338399 std::for_each (infVec.begin (), infVec.end (), [&orbitBCInfo](const auto & inf) { orbitBCInfo.emplace_back ((uint64_t (inf.heartbeatOrbit ) << 32 ) + uint64_t (inf.heartbeatBC )); });
339400 continue ;
340401 }
341402
342- if (orbitsInTF != infVec.size ()) {
343- LOGP (fatal, " IDC CRU {:3}: unequal number of IDC values {} != {}" , cru, orbitsInTF, infVec.size ());
403+ if (packetsInTF != infVec.size ()) {
404+ LOGP (error, " IDC CRU {:3}: number of IDC packets {} does not match max over all CRUs {} in TF {}" , cru, packetsInTF, infVec.size (), tfCounter);
405+ hasErrors = true ;
344406 }
345407
346408 if (!std::equal (infVecComp->begin (), infVecComp->end (), infVec.begin ())) {
347- LOGP (fatal, " IDC CRU {:3}: mismatch in orbits" );
409+ LOGP (error, " IDC CRU {:3}: mismatch in orbit numbers" , cru);
410+ hasErrors = true ;
348411 }
349412 }
350413
351414 // send data
352415 for (auto & [cru, idcVec] : mIDCvectors ) {
353- idcVec.resize (Mapper::PADSPERREGION [CRU (cru).region ()] * orbitsInTF );
416+ idcVec.resize (Mapper::PADSPERREGION [CRU (cru).region ()] * packetsInTF );
354417 const header::DataHeader::SubSpecificationType subSpec{cru << 7 };
355418 LOGP (debug, " Sending IDCs for CRU {} of size {}" , cru, idcVec.size ());
356419 output.snapshot (Output{gDataOriginTPC , " IDCVECTOR" , subSpec}, idcVec);
357420 output.snapshot (Output{gDataOriginTPC , " IDCORBITS" , subSpec}, orbitBCInfo);
358421 }
359422
360- // clear output
361- initIDC ();
423+ return hasErrors;
362424 }
363425
364426 // ____________________________________________________________________________
@@ -444,6 +506,64 @@ class IDCToVectorDevice : public o2::framework::Task
444506 }
445507 }
446508 }
509+
510+ void writeRawData (InputRecord& inputs)
511+ {
512+ if (!mRawOutputFile .is_open ()) {
513+ return ;
514+ }
515+
516+ using DataHeader = o2::header::DataHeader;
517+
518+ std::vector<InputSpec> filter = {{" check" , ConcreteDataTypeMatcher{o2::header::gDataOriginTPC , " RAWDATA" }, Lifetime::Timeframe}}; // TODO: Change to IDC when changed in DD
519+ for (auto const & ref : InputRecordWalker (inputs, filter)) {
520+ auto dh = DataRefUtils::getHeader<header::DataHeader*>(ref);
521+ // LOGP(info, "write header: {}/{}/{}, payload size: {} / {}", dh->dataOrigin, dh->dataDescription, dh->subSpecification, dh->payloadSize, ref.payloadSize);
522+ if (((mRawDataType == 1 ) || (mRawDataType == 3 )) && (dh->payloadSize == 2 * sizeof (o2::header::RAWDataHeader))) {
523+ continue ;
524+ }
525+
526+ if (mRawDataType < 2 ) {
527+ mRawOutputFile .write (ref.header , sizeof (DataHeader));
528+ }
529+ if (mRawDataType < 4 ) {
530+ mRawOutputFile .write (ref.payload , ref.payloadSize );
531+ }
532+
533+ if (mRawDataType == 4 ) {
534+ const gsl::span<const char > raw = inputs.get <gsl::span<char >>(ref);
535+ try {
536+ o2::framework::RawParser parser (raw.data (), raw.size ());
537+ for (auto it = parser.begin (), end = parser.end (); it != end; ++it) {
538+ const auto size = it.size ();
539+ // skip empty packages (HBF open)
540+ if (size == 0 ) {
541+ continue ;
542+ }
543+
544+ auto * rdhPtr = it.get_if <o2::header::RAWDataHeaderV6>();
545+ if (!rdhPtr) {
546+ throw std::runtime_error (" could not get RDH from packet" );
547+ }
548+
549+ // ---| extract hardware information to do the processing |---
550+ const auto feeId = (FEEIDType)RDHUtils::getFEEID (*rdhPtr);
551+ const auto link = rdh_utils::getLink (feeId);
552+ const auto detField = RDHUtils::getDetectorField (*rdhPtr);
553+
554+ // only select IDCs
555+ if ((detField != (decltype (detField))RawDataType::IDC ) || (link != rdh_utils::IDCLinkID)) {
556+ continue ;
557+ }
558+
559+ // write out raw data
560+ mRawOutputFile .write ((const char *)it.raw (), rdhPtr->memorySize );
561+ }
562+ } catch (...) {
563+ }
564+ }
565+ }
566+ }
447567};
448568
449569o2::framework::DataProcessorSpec getIDCToVectorSpec (const std::string inputSpec, std::vector<uint32_t > const & crus)
@@ -463,7 +583,12 @@ o2::framework::DataProcessorSpec getIDCToVectorSpec(const std::string inputSpec,
463583 outputs,
464584 AlgorithmSpec{adaptFromTask<device>(crus)},
465585 Options{
466- {" write-debug" , VariantType::Bool, false , {" write a debug output tree." }},
586+ {" write-debug" , VariantType::Bool, false , {" write a debug output tree" }},
587+ {" write-debug-on-error" , VariantType::Bool, false , {" write a debug output tree in case errors occurred" }},
588+ {" debug-file-name" , VariantType::String, " /tmp/idc_vector_debug.{run}.root" , {" name of the debug output file" }},
589+ {" write-raw-data-on-error" , VariantType::Bool, false , {" dump raw data in case errors occurred" }},
590+ {" raw-file-name" , VariantType::String, " /tmp/idc_debug.{run}.{raw_type}" , {" name of the raw output file" }},
591+ {" raw-data-type" , VariantType::Int, 0 , {" Which raw data to dump: 0-full TPC with DH, 1-full TPC with DH skip empty, 2-full TPC no DH, 3-full TPC no DH skip empty, 4-IDC raw only" }},
467592 {" pedestal-url" , VariantType::String, " ccdb-default" , {" ccdb-default: load from NameConf::getCCDBServer() OR ccdb url (must contain 'ccdb' OR pedestal file name" }},
468593 {" swap-links" , VariantType::Bool, false , {" swap links to circumvent bug in FW" }},
469594 } // end Options
0 commit comments