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
@@ -174,25 +186,38 @@ std::vector<o2::mid::ColumnData> getRejectList(std::vector<o2::mid::ColumnData>
174186 return badChannels ;
175187}
176188
189+ /// @brief Gets the run duration with a safety marging
190+ /// @param ccdbApi CCDB api
191+ /// @param marging margin in milliseconds
192+ /// @return Pair with the timestamps of start-margin and end+margin for the run
193+ std ::pair < int64_t , int64_t > getRunDuration (const o2 ::ccdb ::CcdbApi & ccdbApi , int runNumber , int64_t margin = 120000 )
194+ {
195+ auto runRange = o2 ::ccdb ::BasicCCDBManager ::getRunDuration (ccdbApi , runNumber );
196+ runRange .first -= margin ; // Subtract margin
197+ runRange .second += margin ; // Add margin
198+ return runRange ;
199+ }
200+
177201/// @brief Builds the reject list for the selected timestamp
178202/// @param timestamp Timestamp for query
179203/// @param qcdbApi QCDB api
180204/// @param ccdbApi CCDB api
181205/// @param outCCDBApi api of the CCDB where the reject list will be uploaded
182206/// @return Reject list
183- std :: vector < o2 :: mid :: ColumnData > build_rejectlist (long timestamp , const o2 ::ccdb ::CcdbApi & qcdbApi , const o2 ::ccdb ::CcdbApi & ccdbApi , const o2 :: ccdb :: CcdbApi & outCCDBApi )
207+ RejectListStruct build_rejectlist (long timestamp , const o2 ::ccdb ::CcdbApi & qcdbApi , const o2 ::ccdb ::CcdbApi & ccdbApi )
184208{
185209 std ::map < std ::string , std ::string > metadata ;
210+ RejectListStruct rl ;
186211 auto* qcQuality = qcdbApi .retrieveFromTFileAny < TCanvas > (sPathQCQuality , metadata , getTSMS (timestamp ));
187212 if (!qcQuality ) {
188213 std ::cerr << "Cannot find QC quality for " << tsToString (timestamp ) << std ::endl ;
189- return {} ;
214+ return rl ;
190215 }
191216 // Find the first and last timestamp where the quality was bad (if any)
192217 auto badTSRange = findTSRange (qcQuality );
193218 if (badTSRange .second == 0 ) {
194219 std ::cout << "All good" << std ::endl ;
195- return {} ;
220+ return rl ;
196221 }
197222 // Search for the last timestamp for which the run quality was good
198223 auto goodTSRange = findTSRange (qcQuality , false );
@@ -202,18 +227,15 @@ std::vector<o2::mid::ColumnData> build_rejectlist(long timestamp, const o2::ccdb
202227 if (!grpecs .isDetReadOut (o2 ::detectors ::DetID ::MID )) {
203228 std ::cout << "Error: we are probably reading a parallel run" << std ::endl ;
204229 grpecs .print ();
205- return {} ;
230+ return rl ;
206231 }
207232 if (grpecs .getRunType () != o2 ::parameters ::GRPECS ::PHYSICS ) {
208233 std ::cout << "This is not a physics run: skip" << std ::endl ;
209234 grpecs .print ();
210- return {} ;
235+ return rl ;
211236 }
212237
213- auto runRange = o2 ::ccdb ::BasicCCDBManager ::getRunDuration (ccdbApi , grpecs .getRun ());
214- long margin = 120000 ; // Add a two minutes safety margin
215- runRange .first -= margin ; // Subtract margin
216- runRange .second += margin ; // Add margin
238+ auto runRange = getRunDuration (ccdbApi , grpecs .getRun ());
217239
218240 // Search for hits histogram in the period where the QC quality was bad
219241 auto tsVector = findObjectsTSInPeriod (badTSRange .first , badTSRange .second , qcdbApi , "qc/MID/MO/QcTaskMIDDigits/Hits" );
@@ -227,15 +249,15 @@ std::vector<o2::mid::ColumnData> build_rejectlist(long timestamp, const o2::ccdb
227249 auto infos = gm .buildStripsInfo ();
228250 auto badChannels = findBadChannels (occupancy , infos );
229251 auto badChannelsCCDB = * ccdbApi .retrieveFromTFileAny < std ::vector < o2 ::mid ::ColumnData >>("MID/Calib/BadChannels" , metadata , getTSMS (timestamp ));
230- auto rejectList = getRejectList (badChannels , badChannelsCCDB );
231- if (rejectList .empty ()) {
252+ rl . rejectList = getRejectList (badChannels , badChannelsCCDB );
253+ if (rl . rejectList .empty ()) {
232254 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 ;
233- return {} ;
255+ return rl ;
234256 }
235257
236258 // Print some useful information
237259 std ::cout << "Reject list:" << std ::endl ;
238- for (auto& col : rejectList ) {
260+ for (auto& col : rl . rejectList ) {
239261 std ::cout << col << std ::endl ;
240262 }
241263 std ::cout << "Run number: " << grpecs .getRun () << std ::endl ;
@@ -245,41 +267,120 @@ std::vector<o2::mid::ColumnData> build_rejectlist(long timestamp, const o2::ccdb
245267 std ::cout << "Bad: " << timeRangeToString (badTSRange .first , badTSRange .second ) << std ::endl ;
246268
247269 // Set the start of the reject list to the last timestamp in which the occupancy was ok
248- auto startRL = goodTSRange .second ;
270+ rl . start = goodTSRange .second ;
249271 if (goodTSRange .first == 0 ) {
250272 // If the quality was bad for the full run, set the start of the reject list to the SOR
251273 std ::cout << "CAVEAT: no good TS found. Will use SOT instead" << std ::endl ;
252- startRL = runRange .first ;
274+ rl . start = runRange .first ;
253275 }
254276 // Set the end of the reject list to the end of run
255- auto endRL = runRange .second ;
256- // Ask if you want to upload the object to the CCDB
257- std ::cout << "Upload reject list with validity: " << startRL << " - " << endRL << " to " << outCCDBApi .getURL () << "? [y/n]" << std ::endl ;
258- std ::string answer ;
259- std ::cin >> answer ;
260- if (answer == "y" ) {
261- std ::cout << "Storing RejectList valid from " << startRL << " to " << endRL << std ::endl ;
262- outCCDBApi .storeAsTFileAny (& rejectList , "MID/Calib/RejectList" , metadata , startRL , endRL );
277+ rl .end = runRange .second ;
278+ return rl ;
279+ }
280+
281+ /// @brief Loads the reject list from a json file
282+ /// @param ccdbApi CCDB api
283+ /// @param filename json filename
284+ /// @return Reject list structure
285+ RejectListStruct load_from_json (const o2 ::ccdb ::CcdbApi & ccdbApi , const char * filename = "rejectlist.json" )
286+ {
287+ // Open the JSON file
288+ std ::cout << "Reading reject list from file " << filename << std ::endl ;
289+ RejectListStruct rl ;
290+ std ::ifstream inFile (filename );
291+ if (!inFile .is_open ()) {
292+ std ::cerr << "Could not open the file!" << std ::endl ;
293+ return rl ;
263294 }
264- return rejectList ;
295+
296+ // Create an IStreamWrapper for file input stream
297+ rapidjson ::IStreamWrapper isw (inFile );
298+
299+ rapidjson ::Document doc ;
300+ if (doc .ParseStream (isw ).HasParseError ()) {
301+ std ::cerr << "Problem parsing " << filename << std ::endl ;
302+ return rl ;
303+ }
304+ auto startRange = getRunDuration (ccdbApi , doc ["startRun" ].GetInt ());
305+ auto endRange = getRunDuration (ccdbApi , doc ["endRun" ].GetInt ());
306+ rl .start = startRange .first ;
307+ rl .end = endRange .second ;
308+ std ::cout << "Manual RL validity: " << timeRangeToString (rl .start , rl .end ) << std ::endl ;
309+ auto rlArray = doc ["rejectList" ].GetArray ();
310+ for (auto& ar : rlArray ) {
311+ o2 ::mid ::ColumnData col ;
312+ col .deId = ar ["deId" ].GetInt ();
313+ col .columnId = ar ["columnId" ].GetInt ();
314+ auto patterns = ar ["patterns" ].GetArray ();
315+ for (size_t iar = 0 ; iar < 5 ; ++ iar ) {
316+ col .patterns [iar ] = std ::strtol (patterns [iar ].GetString (), NULL , 16 );
317+ }
318+ rl .rejectList .emplace_back (col );
319+ std ::cout << col << std ::endl ;
320+ }
321+ return rl ;
265322}
266323
267- /// @brief Builds the reject list for the selected timestamp
268- /// @param timestamp Timestamp for query
269- /// @param qcdbUrl QCDB URL
270- /// @param ccdbUrl CCDB URL
271- /// @param outCCDBUrl URL of the CCDB where the reject list will be uploaded
272- /// @return Reject list
273- 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" )
324+ /// @brief Merges the manual and automatic reject lists
325+ /// @param manualRL Manual reject list from json file
326+ /// @param rls Reject list from QCDB and CCDB
327+ /// @return Merged reject list
328+ std ::vector < RejectListStruct > merge_rejectlists (const RejectListStruct & manualRL , const std ::vector < RejectListStruct > & rls )
274329{
275- // Get the QC quality object for the selected timestamp
276- o2 ::ccdb ::CcdbApi qcdbApi ;
277- qcdbApi .init (qcdbUrl );
278- o2 ::ccdb ::CcdbApi ccdbApi ;
279- ccdbApi .init (ccdbUrl );
280- o2 ::ccdb ::CcdbApi outCCDBApi ;
281- outCCDBApi .init (outCCDBUrl );
282- return build_rejectlist (timestamp , qcdbApi , ccdbApi , outCCDBApi );
330+ std ::vector < RejectListStruct > merged ;
331+ if (rls .empty ()) {
332+ merged .emplace_back (manualRL );
333+ return merged ;
334+ }
335+ o2 ::mid ::ColumnDataHandler ch ;
336+ RejectListStruct tmpRL ;
337+ long lastEnd = manualRL .start ;
338+ for (auto & rl : rls ) {
339+ std ::cout << "Checking rl with validity: " << timeRangeToString (rl .start , rl .end ) << std ::endl ;
340+ if (rl .start >= manualRL .start && rl .end <= manualRL .end ) {
341+ // The period is included in the validity of the manual reject list
342+ if (rl .start > lastEnd ) {
343+ // Fill holes between periods
344+ tmpRL = manualRL ;
345+ tmpRL .start = lastEnd ;
346+ tmpRL .end = rl .start ;
347+ merged .emplace_back (tmpRL );
348+ std ::cout << "Adding manual RL with validity: " << timeRangeToString (tmpRL .start , tmpRL .end ) << std ::endl ;
349+ }
350+ lastEnd = rl .end ;
351+
352+ // merge
353+ ch .clear ();
354+ ch .merge (rl .rejectList );
355+ ch .merge (manualRL .rejectList );
356+ tmpRL = rl ;
357+ tmpRL .rejectList = ch .getMerged ();
358+ 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 ); });
359+ merged .emplace_back (tmpRL );
360+ std ::cout << "Merging RL with validity: " << timeRangeToString (tmpRL .start , tmpRL .end ) << std ::endl ;
361+ // std::cout << "Before: " << std::endl;
362+ // for (auto& col : rl.rejectList) {
363+ // std::cout << col << std::endl;
364+ // }
365+ // std::cout << "After: " << std::endl;
366+ // for (auto& col : tmpRL.rejectList) {
367+ // std::cout << col << std::endl;
368+ // }
369+ } else {
370+ if (rl .start > manualRL .end && lastEnd < manualRL .end ) {
371+ // Close manual period
372+ tmpRL = manualRL ;
373+ tmpRL .start = lastEnd ;
374+ merged .emplace_back (tmpRL );
375+ std ::cout << "Adding manual RL with validity: " << timeRangeToString (tmpRL .start , tmpRL .end ) << std ::endl ;
376+ lastEnd = manualRL .end ;
377+ }
378+ // Add current reject list as it is
379+ merged .emplace_back (rl );
380+ std ::cout << "Adding RL with validity: " << timeRangeToString (rl .start , rl .end ) << std ::endl ;
381+ }
382+ }
383+ return merged ;
283384}
284385
285386/// @brief Builds the reject list in a time range
@@ -288,17 +389,38 @@ std::vector<o2::mid::ColumnData> build_rejectlist(long timestamp, const char* qc
288389/// @param qcdbUrl QCDB URL
289390/// @param ccdbUrl CCDB URL
290391/// @param outCCDBUrl URL of the CCDB where the reject lists will be uploaded
291- 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" )
392+ 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 = "" )
292393{
293394 // Query the MID QC quality objects
294395 o2 ::ccdb ::CcdbApi qcdbApi ;
295396 qcdbApi .init (qcdbUrl );
296397 o2 ::ccdb ::CcdbApi ccdbApi ;
297398 ccdbApi .init (ccdbUrl );
298- o2 ::ccdb ::CcdbApi outCCDBApi ;
299- outCCDBApi .init (outCCDBUrl );
399+ std ::vector < RejectListStruct > rls ;
300400 auto objectsTS = findObjectsTSInPeriod (start , end , qcdbApi , sPathQCQuality .c_str ());
301401 for (auto ts : objectsTS ) {
302- build_rejectlist (ts , qcdbApi , ccdbApi , outCCDBApi );
402+ auto rl = build_rejectlist (ts , qcdbApi , ccdbApi );
403+ if (rl .start != rl .end ) {
404+ rls .emplace_back (rl );
405+ }
406+ }
407+
408+ if (!std ::string (json_rejectlist ).empty ()) {
409+ auto rlManual = load_from_json (ccdbApi , json_rejectlist );
410+ rls = merge_rejectlists (rlManual , rls );
411+ }
412+
413+ o2 ::ccdb ::CcdbApi outCCDBApi ;
414+ outCCDBApi .init (outCCDBUrl );
415+ std ::map < std ::string , std ::string > metadata ;
416+ for (auto& rl : rls ) {
417+ // Ask if you want to upload the object to the CCDB
418+ std ::cout << "Upload reject list with validity: " << rl .start << " - " << rl .end << " to " << outCCDBApi .getURL () << "? [y/n]" << std ::endl ;
419+ std ::string answer ;
420+ std ::cin >> answer ;
421+ if (answer == "y" ) {
422+ std ::cout << "Storing RejectList valid from " << rl .start << " to " << rl .end << std ::endl ;
423+ outCCDBApi .storeAsTFileAny (& rl .rejectList , "MID/Calib/RejectList" , metadata , rl .start , rl .end );
424+ }
303425 }
304426}
0 commit comments