2121#include <vector>
2222#include <limits>
2323#include <algorithm>
24+ #include "rapidjson/document.h"
25+ #include "rapidjson/istreamwrapper.h"
2426#include "TCanvas.h"
2527#include "TH1.h"
2628#include "TGraph.h"
2931#include "DataFormatsParameters/GRPECSObject.h"
3032#include "DetectorsCommonDataFormats/DetID.h"
3133#include "DataFormatsMID/ColumnData.h"
34+ #include "MIDBase/ColumnDataHandler.h"
3235#include "MIDGlobalMapping/ExtendedMappingInfo.h"
3336#include "MIDGlobalMapping/GlobalMapper.h"
3437#include "MIDFiltering/ChannelMasksHandler.h"
38+
39+ // ...
3540#if !defined(__CLING__ ) || defined(__ROOTCLING__ )
3641#include "CCDB/BasicCCDBManager.h"
3742#endif
3843
3944static const std ::string sPathQCQuality = "qc/MID/MO/MIDQuality/Trends/global/MIDQuality/MIDQuality" ;
4045
46+ /// @brief Reject list object
47+ struct RejectListStruct {
48+ long start = 0 ; /// Start validity
49+ long end = 0 ; /// End validity
50+ std ::vector < o2 ::mid ::ColumnData > rejectList {}; /// Bad channels
51+ };
52+
4153/// @brief Get timestamp in milliseconds
4254/// @param timestamp Input timestamp (in s or ms)
4355/// @return Timestamp in ms
@@ -168,25 +180,38 @@ std::vector<o2::mid::ColumnData> getRejectList(std::vector<o2::mid::ColumnData>
168180 return badChannels ;
169181}
170182
183+ /// @brief Gets the run duration with a safety marging
184+ /// @param ccdbApi CCDB api
185+ /// @param marging margin in milliseconds
186+ /// @return Pair with the timestamps of start-margin and end+margin for the run
187+ std ::pair < int64_t , int64_t > getRunDuration (const o2 ::ccdb ::CcdbApi & ccdbApi , int runNumber , int64_t margin = 120000 )
188+ {
189+ auto runRange = o2 ::ccdb ::BasicCCDBManager ::getRunDuration (ccdbApi , runNumber );
190+ runRange .first -= margin ; // Subtract margin
191+ runRange .second += margin ; // Add margin
192+ return runRange ;
193+ }
194+
171195/// @brief Builds the reject list for the selected timestamp
172196/// @param timestamp Timestamp for query
173197/// @param qcdbApi QCDB api
174198/// @param ccdbApi CCDB api
175199/// @param outCCDBApi api of the CCDB where the reject list will be uploaded
176200/// @return Reject list
177- std :: vector < o2 :: mid :: ColumnData > build_rejectlist (long timestamp , const o2 ::ccdb ::CcdbApi & qcdbApi , const o2 ::ccdb ::CcdbApi & ccdbApi , const o2 :: ccdb :: CcdbApi & outCCDBApi )
201+ RejectListStruct build_rejectlist (long timestamp , const o2 ::ccdb ::CcdbApi & qcdbApi , const o2 ::ccdb ::CcdbApi & ccdbApi )
178202{
179203 std ::map < std ::string , std ::string > metadata ;
204+ RejectListStruct rl ;
180205 auto* qcQuality = qcdbApi .retrieveFromTFileAny < TCanvas > (sPathQCQuality , metadata , getTSMS (timestamp ));
181206 if (!qcQuality ) {
182207 std ::cerr << "Cannot find QC quality for " << tsToString (timestamp ) << std ::endl ;
183- return {} ;
208+ return rl ;
184209 }
185210 // Find the first and last timestamp where the quality was bad (if any)
186211 auto badTSRange = findTSRange (qcQuality );
187212 if (badTSRange .second == 0 ) {
188213 std ::cout << "All good" << std ::endl ;
189- return {} ;
214+ return rl ;
190215 }
191216 // Search for the last timestamp for which the run quality was good
192217 auto goodTSRange = findTSRange (qcQuality , false );
@@ -196,18 +221,15 @@ std::vector<o2::mid::ColumnData> build_rejectlist(long timestamp, const o2::ccdb
196221 if (!grpecs .isDetReadOut (o2 ::detectors ::DetID ::MID )) {
197222 std ::cout << "Error: we are probably reading a parallel run" << std ::endl ;
198223 grpecs .print ();
199- return {} ;
224+ return rl ;
200225 }
201226 if (grpecs .getRunType () != o2 ::parameters ::GRPECS ::PHYSICS ) {
202227 std ::cout << "This is not a physics run: skip" << std ::endl ;
203228 grpecs .print ();
204- return {} ;
229+ return rl ;
205230 }
206231
207- auto runRange = o2 ::ccdb ::BasicCCDBManager ::getRunDuration (ccdbApi , grpecs .getRun ());
208- long margin = 120000 ; // Add a two minutes safety margin
209- runRange .first -= margin ; // Subtract margin
210- runRange .second += margin ; // Add margin
232+ auto runRange = getRunDuration (ccdbApi , grpecs .getRun ());
211233
212234 // Search for hits histogram in the period where the QC quality was bad
213235 auto tsVector = findObjectsTSInPeriod (badTSRange .first , badTSRange .second , qcdbApi , "qc/MID/MO/QcTaskMIDDigits/Hits" );
@@ -221,15 +243,15 @@ std::vector<o2::mid::ColumnData> build_rejectlist(long timestamp, const o2::ccdb
221243 auto infos = gm .buildStripsInfo ();
222244 auto badChannels = findBadChannels (occupancy , infos );
223245 auto badChannelsCCDB = * ccdbApi .retrieveFromTFileAny < std ::vector < o2 ::mid ::ColumnData >>("MID/Calib/BadChannels" , metadata , getTSMS (timestamp ));
224- auto rejectList = getRejectList (badChannels , badChannelsCCDB );
225- if (rejectList .empty ()) {
246+ rl . rejectList = getRejectList (badChannels , badChannelsCCDB );
247+ if (rl . rejectList .empty ()) {
226248 std ::cout << "Warning: reject list was empty. It probably means that an entire board is already masked in calibration for run " << grpecs .getRun () << std ::endl ;
227- return {} ;
249+ return rl ;
228250 }
229251
230252 // Print some useful information
231253 std ::cout << "Reject list:" << std ::endl ;
232- for (auto& col : rejectList ) {
254+ for (auto& col : rl . rejectList ) {
233255 std ::cout << col << std ::endl ;
234256 }
235257 std ::cout << "Run number: " << grpecs .getRun () << std ::endl ;
@@ -239,41 +261,120 @@ std::vector<o2::mid::ColumnData> build_rejectlist(long timestamp, const o2::ccdb
239261 std ::cout << "Bad: " << timeRangeToString (badTSRange .first , badTSRange .second ) << std ::endl ;
240262
241263 // Set the start of the reject list to the last timestamp in which the occupancy was ok
242- auto startRL = goodTSRange .second ;
264+ rl . start = goodTSRange .second ;
243265 if (goodTSRange .first == 0 ) {
244266 // If the quality was bad for the full run, set the start of the reject list to the SOR
245267 std ::cout << "CAVEAT: no good TS found. Will use SOT instead" << std ::endl ;
246- startRL = runRange .first ;
268+ rl . start = runRange .first ;
247269 }
248270 // Set the end of the reject list to the end of run
249- auto endRL = runRange .second ;
250- // Ask if you want to upload the object to the CCDB
251- std ::cout << "Upload reject list with validity: " << startRL << " - " << endRL << " to " << outCCDBApi .getURL () << "? [y/n]" << std ::endl ;
252- std ::string answer ;
253- std ::cin >> answer ;
254- if (answer == "y" ) {
255- std ::cout << "Storing RejectList valid from " << startRL << " to " << endRL << std ::endl ;
256- outCCDBApi .storeAsTFileAny (& rejectList , "MID/Calib/RejectList" , metadata , startRL , endRL );
271+ rl .end = runRange .second ;
272+ return rl ;
273+ }
274+
275+ /// @brief Loads the reject list from a json file
276+ /// @param ccdbApi CCDB api
277+ /// @param filename json filename
278+ /// @return Reject list structure
279+ RejectListStruct load_from_json (const o2 ::ccdb ::CcdbApi & ccdbApi , const char * filename = "rejectlist.json" )
280+ {
281+ // Open the JSON file
282+ std ::cout << "Reading reject list from file " << filename << std ::endl ;
283+ RejectListStruct rl ;
284+ std ::ifstream inFile (filename );
285+ if (!inFile .is_open ()) {
286+ std ::cerr << "Could not open the file!" << std ::endl ;
287+ return rl ;
257288 }
258- return rejectList ;
289+
290+ // Create an IStreamWrapper for file input stream
291+ rapidjson ::IStreamWrapper isw (inFile );
292+
293+ rapidjson ::Document doc ;
294+ if (doc .ParseStream (isw ).HasParseError ()) {
295+ std ::cerr << "Problem parsing " << filename << std ::endl ;
296+ return rl ;
297+ }
298+ auto startRange = getRunDuration (ccdbApi , doc ["startRun" ].GetInt ());
299+ auto endRange = getRunDuration (ccdbApi , doc ["endRun" ].GetInt ());
300+ rl .start = startRange .first ;
301+ rl .end = endRange .second ;
302+ std ::cout << "Manual RL validity: " << timeRangeToString (rl .start , rl .end ) << std ::endl ;
303+ auto rlArray = doc ["rejectList" ].GetArray ();
304+ for (auto& ar : rlArray ) {
305+ o2 ::mid ::ColumnData col ;
306+ col .deId = ar ["deId" ].GetInt ();
307+ col .columnId = ar ["columnId" ].GetInt ();
308+ auto patterns = ar ["patterns" ].GetArray ();
309+ for (size_t iar = 0 ; iar < 5 ; ++ iar ) {
310+ col .patterns [iar ] = std ::strtol (patterns [iar ].GetString (), NULL , 16 );
311+ }
312+ rl .rejectList .emplace_back (col );
313+ std ::cout << col << std ::endl ;
314+ }
315+ return rl ;
259316}
260317
261- /// @brief Builds the reject list for the selected timestamp
262- /// @param timestamp Timestamp for query
263- /// @param qcdbUrl QCDB URL
264- /// @param ccdbUrl CCDB URL
265- /// @param outCCDBUrl URL of the CCDB where the reject list will be uploaded
266- /// @return Reject list
267- std ::vector < o2 ::mid ::ColumnData > build_rejectlist (long timestamp , const char * qcdbUrl = "http://ali-qcdb-gpn.cern.ch:8083" , const char * ccdbUrl = "http://alice-ccdb.cern.ch" , const char * outCCDBUrl = "http://localhost:8080" )
318+ /// @brief Merges the manual and automatic reject lists
319+ /// @param manualRL Manual reject list from json file
320+ /// @param rls Reject list from QCDB and CCDB
321+ /// @return Merged reject list
322+ std ::vector < RejectListStruct > merge_rejectlists (const RejectListStruct & manualRL , const std ::vector < RejectListStruct > & rls )
268323{
269- // Get the QC quality object for the selected timestamp
270- o2 ::ccdb ::CcdbApi qcdbApi ;
271- qcdbApi .init (qcdbUrl );
272- o2 ::ccdb ::CcdbApi ccdbApi ;
273- ccdbApi .init (ccdbUrl );
274- o2 ::ccdb ::CcdbApi outCCDBApi ;
275- outCCDBApi .init (outCCDBUrl );
276- return build_rejectlist (timestamp , qcdbApi , ccdbApi , outCCDBApi );
324+ std ::vector < RejectListStruct > merged ;
325+ if (rls .empty ()) {
326+ merged .emplace_back (manualRL );
327+ return merged ;
328+ }
329+ o2 ::mid ::ColumnDataHandler ch ;
330+ RejectListStruct tmpRL ;
331+ long lastEnd = manualRL .start ;
332+ for (auto & rl : rls ) {
333+ std ::cout << "Checking rl with validity: " << timeRangeToString (rl .start , rl .end ) << std ::endl ;
334+ if (rl .start >= manualRL .start && rl .end <= manualRL .end ) {
335+ // The period is included in the validity of the manual reject list
336+ if (rl .start > lastEnd ) {
337+ // Fill holes between periods
338+ tmpRL = manualRL ;
339+ tmpRL .start = lastEnd ;
340+ tmpRL .end = rl .start ;
341+ merged .emplace_back (tmpRL );
342+ std ::cout << "Adding manual RL with validity: " << timeRangeToString (tmpRL .start , tmpRL .end ) << std ::endl ;
343+ }
344+ lastEnd = rl .end ;
345+
346+ // merge
347+ ch .clear ();
348+ ch .merge (rl .rejectList );
349+ ch .merge (manualRL .rejectList );
350+ tmpRL = rl ;
351+ tmpRL .rejectList = ch .getMerged ();
352+ std ::sort (tmpRL .rejectList .begin (), tmpRL .rejectList .end (), [](const o2 ::mid ::ColumnData & col1 , const o2 ::mid ::ColumnData & col2 ) { return o2 ::mid ::getColumnDataUniqueId (col1 .deId , col1 .columnId ) < o2 ::mid ::getColumnDataUniqueId (col2 .deId , col2 .columnId ); });
353+ merged .emplace_back (tmpRL );
354+ std ::cout << "Merging RL with validity: " << timeRangeToString (tmpRL .start , tmpRL .end ) << std ::endl ;
355+ // std::cout << "Before: " << std::endl;
356+ // for (auto& col : rl.rejectList) {
357+ // std::cout << col << std::endl;
358+ // }
359+ // std::cout << "After: " << std::endl;
360+ // for (auto& col : tmpRL.rejectList) {
361+ // std::cout << col << std::endl;
362+ // }
363+ } else {
364+ if (rl .start > manualRL .end && lastEnd < manualRL .end ) {
365+ // Close manual period
366+ tmpRL = manualRL ;
367+ tmpRL .start = lastEnd ;
368+ merged .emplace_back (tmpRL );
369+ std ::cout << "Adding manual RL with validity: " << timeRangeToString (tmpRL .start , tmpRL .end ) << std ::endl ;
370+ lastEnd = manualRL .end ;
371+ }
372+ // Add current reject list as it is
373+ merged .emplace_back (rl );
374+ std ::cout << "Adding RL with validity: " << timeRangeToString (rl .start , rl .end ) << std ::endl ;
375+ }
376+ }
377+ return merged ;
277378}
278379
279380/// @brief Builds the reject list iin a time range
@@ -282,17 +383,38 @@ std::vector<o2::mid::ColumnData> build_rejectlist(long timestamp, const char* qc
282383/// @param qcdbUrl QCDB URL
283384/// @param ccdbUrl CCDB URL
284385/// @param outCCDBUrl URL of the CCDB where the reject lists will be uploaded
285- void build_rejectlist (long start , long end , const char * qcdbUrl = "http://ali-qcdb-gpn.cern.ch:8083" , const char * ccdbUrl = "http://alice-ccdb.cern.ch" , const char * outCCDBUrl = "http://localhost:8080" )
386+ void build_rejectlist (long start , long end , const char * qcdbUrl = "http://ali-qcdb-gpn.cern.ch:8083" , const char * ccdbUrl = "http://alice-ccdb.cern.ch" , const char * outCCDBUrl = "http://localhost:8080" , const char * json_rejectlist = "" )
286387{
287388 // Query the MID QC quality objects
288389 o2 ::ccdb ::CcdbApi qcdbApi ;
289390 qcdbApi .init (qcdbUrl );
290391 o2 ::ccdb ::CcdbApi ccdbApi ;
291392 ccdbApi .init (ccdbUrl );
292- o2 ::ccdb ::CcdbApi outCCDBApi ;
293- outCCDBApi .init (outCCDBUrl );
393+ std ::vector < RejectListStruct > rls ;
294394 auto objectsTS = findObjectsTSInPeriod (start , end , qcdbApi , sPathQCQuality .c_str ());
295395 for (auto ts : objectsTS ) {
296- build_rejectlist (ts , qcdbApi , ccdbApi , outCCDBApi );
396+ auto rl = build_rejectlist (ts , qcdbApi , ccdbApi );
397+ if (rl .start != rl .end ) {
398+ rls .emplace_back (rl );
399+ }
400+ }
401+
402+ if (!std ::string (json_rejectlist ).empty ()) {
403+ auto rlManual = load_from_json (ccdbApi , json_rejectlist );
404+ rls = merge_rejectlists (rlManual , rls );
405+ }
406+
407+ o2 ::ccdb ::CcdbApi outCCDBApi ;
408+ outCCDBApi .init (outCCDBUrl );
409+ std ::map < std ::string , std ::string > metadata ;
410+ for (auto& rl : rls ) {
411+ // Ask if you want to upload the object to the CCDB
412+ std ::cout << "Upload reject list with validity: " << rl .start << " - " << rl .end << " to " << outCCDBApi .getURL () << "? [y/n]" << std ::endl ;
413+ std ::string answer ;
414+ std ::cin >> answer ;
415+ if (answer == "y" ) {
416+ std ::cout << "Storing RejectList valid from " << rl .start << " to " << rl .end << std ::endl ;
417+ outCCDBApi .storeAsTFileAny (& rl .rejectList , "MID/Calib/RejectList" , metadata , rl .start , rl .end );
418+ }
297419 }
298420}
0 commit comments