diff --git a/Detectors/DCS/CMakeLists.txt b/Detectors/DCS/CMakeLists.txt index 82e52dc169480..83da3d6cdc4bf 100644 --- a/Detectors/DCS/CMakeLists.txt +++ b/Detectors/DCS/CMakeLists.txt @@ -59,6 +59,12 @@ o2_add_executable( PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsDCS) +o2_add_executable( + sim-workflow + COMPONENT_NAME dcs + SOURCES testWorkflow/dcs-sim-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsDCS) + if(OpenMP_CXX_FOUND) target_compile_definitions(${targetName} PRIVATE WITH_OPENMP) target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) diff --git a/Detectors/DCS/src/DataPointGenerator.cxx b/Detectors/DCS/src/DataPointGenerator.cxx index 062d996452798..b0b64b550985a 100644 --- a/Detectors/DCS/src/DataPointGenerator.cxx +++ b/Detectors/DCS/src/DataPointGenerator.cxx @@ -13,6 +13,7 @@ #include "DetectorsDCS/DataPointCreator.h" #include "DetectorsDCS/DataPointCompositeObject.h" #include "DetectorsDCS/StringUtils.h" +#include "Framework/Logger.h" #include #include #include @@ -23,16 +24,27 @@ namespace { std::pair getDate(const std::string& refDate) { + uint32_t seconds; if (refDate.empty()) { auto current = std::time(nullptr); auto t = std::localtime(¤t); - uint32_t seconds = mktime(t); + seconds = mktime(t); } else { std::tm t{}; std::istringstream ss(refDate); ss >> std::get_time(&t, "%Y-%b-%d %H:%M:%S"); - seconds = mktime(&t); + if (ss.fail()) { // let's see if it was passed as a TDatime + std::tm tt{}; + std::istringstream sss(refDate); + sss >> std::get_time(&tt, "%a %b %d %H:%M:%S %Y"); + if (sss.fail()) { + LOG(ERROR) << "We cannot parse the date"; + } + seconds = mktime(&tt); + } else { + seconds = mktime(&t); + } } uint16_t msec = 5; return std::make_pair(seconds, msec); diff --git a/Detectors/DCS/src/DetectorsDCSLinkDef.h b/Detectors/DCS/src/DetectorsDCSLinkDef.h index da9a282797cba..c5233fb26feef 100644 --- a/Detectors/DCS/src/DetectorsDCSLinkDef.h +++ b/Detectors/DCS/src/DetectorsDCSLinkDef.h @@ -21,5 +21,6 @@ #pragma link C++ class std::unordered_map < o2::dcs::DataPointIdentifier, o2::dcs::DataPointValue> + ; #pragma link C++ function o2::dcs::expandAlias(const std::string&); #pragma link C++ function o2::dcs::expandAliases(const std::vector&); +#pragma link C++ class std::unordered_map < o2::dcs::DataPointIdentifier, std::string> + ; #endif diff --git a/Detectors/DCS/testWorkflow/DCSRandomDataGeneratorSpec.h b/Detectors/DCS/testWorkflow/DCSRandomDataGeneratorSpec.h index 9874df180c2dc..51f09c2d7570d 100644 --- a/Detectors/DCS/testWorkflow/DCSRandomDataGeneratorSpec.h +++ b/Detectors/DCS/testWorkflow/DCSRandomDataGeneratorSpec.h @@ -18,9 +18,11 @@ #include "Framework/DeviceSpec.h" #include "Framework/Logger.h" #include "Framework/Task.h" +#include #include #include #include +#include using namespace o2::framework; @@ -54,9 +56,18 @@ using HintType = std::variant, std::vector generateIntegers(size_t size, int min, int max) { std::uniform_int_distribution distribution(min, max); - std::mt19937 generator; - std::vector data(size); - std::generate(data.begin(), data.end(), [&]() { return distribution(generator); }); + std::mt19937 generator(std::random_device{}()); + std::vector data; + while (data.size() != size) { + data.emplace_back(distribution(generator)); + std::sort(begin(data), end(data)); + auto last = std::unique(begin(data), end(data)); // make sure we do not duplicate + data.erase(last, end(data)); + } + std::shuffle(begin(data), end(data), generator); + for (auto i = 0; i < data.size(); ++i) { + LOG(INFO) << "Generating randomly DP at index " << data[i]; + } return data; } @@ -68,12 +79,20 @@ std::vector generateIntegers(size_t size, int min, int max) * @returns a vector of DataPointCompositeObjects */ std::vector generate(const std::vector hints, - float fraction = 1.0) + float fraction = 1.0, + uint64_t tfid = 0) { std::vector dataPoints; - auto GenerateVisitor = [](const auto& t) { - return o2::dcs::generateRandomDataPoints({t.aliasPattern}, t.minValue, t.maxValue); + TDatime d; + auto dsec = d.Convert(); + dsec += tfid; + d.Set(dsec); + + std::string refDate = d.AsString(); + + auto GenerateVisitor = [refDate](const auto& t) { + return o2::dcs::generateRandomDataPoints({t.aliasPattern}, t.minValue, t.maxValue, refDate); }; for (const auto& hint : hints) { @@ -84,7 +103,8 @@ std::vector generate(const std::vector tmp; + tmp.swap(dataPoints); dataPoints.clear(); for (auto i : indices) { dataPoints.push_back(tmp[i]); @@ -114,11 +134,29 @@ class DCSRandomDataGenerator : public o2::framework::Task mMaxCyclesNoFullMap = ic.options().get("max-cycles-no-full-map"); // create the list of DataPointHints to be used by the generator - mDataPointHints.emplace_back(DataPointHint{"TestChar_0", 'A', 'z'}); + // each detector should create his own when running the tests + + /*mDataPointHints.emplace_back(DataPointHint{"TestChar_0", 'A', 'z'}); mDataPointHints.emplace_back(DataPointHint{"TestDouble_[0..3]", 0, 1700}); mDataPointHints.emplace_back(DataPointHint{"TestInt_[0..50000{:d}]", 0, 1234}); mDataPointHints.emplace_back(DataPointHint{"TestBool_[00..03]", 0, 1}); mDataPointHints.emplace_back(DataPointHint{"TestString_0", "ABC", "ABCDEF"}); + */ + // for TOF + // for test, we use less DPs that official ones + mTOFDataPointHints.emplace_back(DataPointHint{"tof_hv_vp_[00..02]", 0, 50.}); + mTOFDataPointHints.emplace_back(DataPointHint{"tof_hv_vn_[00..02]", 0, 50.}); + mTOFDataPointHints.emplace_back(DataPointHint{"tof_hv_ip_[00..02]", 0, 50.}); + mTOFDataPointHints.emplace_back(DataPointHint{"tof_hv_in_[00..02]", 0, 50.}); + mTOFDataPointHints.emplace_back(DataPointHint{"TOF_FEACSTATUS_[00..01]", 0, 255}); + mTOFDataPointHints.emplace_back(DataPointHint{"TOF_HVSTATUS_SM[00..01]MOD[0..1]", 0, 524287}); + // for TOF, official list + //mTOFDataPointHints.emplace_back(DataPointHint{"tof_hv_vp_[00..89]", 0, 50.}); + //mTOFDataPointHints.emplace_back(DataPointHint{"tof_hv_vn_[00..89]", 0, 50.}); + //mTOFDataPointHints.emplace_back(DataPointHint{"tof_hv_ip_[00..89]", 0, 50.}); + //mTOFDataPointHints.emplace_back(DataPointHint{"tof_hv_in_[00..89]", 0, 50.}); + //mTOFDataPointHints.emplace_back(DataPointHint{"TOF_FEACSTATUS_[00..71]", 0, 255}); + //mTOFDataPointHints.emplace_back(DataPointHint{"TOF_HVSTATUS_SM[00..17]MOD[0..4]", 0, 524287}); } void run(o2::framework::ProcessingContext& pc) final @@ -135,12 +173,13 @@ class DCSRandomDataGenerator : public o2::framework::Task // fraction is one if we generate FBI (Full Buffer Image) float fraction = (generateFBI ? 1.0 : mDeltaFraction); - auto dpcoms = generate(mDataPointHints, fraction); + TDatime d; + auto dpcoms = generate(mDataPointHints, fraction, tfid); + auto tofdpcoms = generate(mTOFDataPointHints, fraction, tfid); - // the output must always get both FBI and Delta, but one of them is empty. - std::vector empty; - pc.outputs().snapshot(Output{"DCS", "DATAPOINTS", 0, Lifetime::Timeframe}, generateFBI ? dpcoms : empty); - pc.outputs().snapshot(Output{"DCS", "DATAPOINTSdelta", 0, Lifetime::Timeframe}, generateFBI ? empty : dpcoms); + LOG(INFO) << "***************** TF " << tfid << " has generated " << tofdpcoms.size() << " DPs for TOF"; + pc.outputs().snapshot(Output{"DCS", "DATAPOINTS", 0, Lifetime::Timeframe}, dpcoms); + pc.outputs().snapshot(Output{"DCS", "TOFDATAPOINTS", 0, Lifetime::Timeframe}, tofdpcoms); mTFs++; } @@ -150,6 +189,7 @@ class DCSRandomDataGenerator : public o2::framework::Task uint64_t mMaxCyclesNoFullMap; float mDeltaFraction; std::vector mDataPointHints; + std::vector mTOFDataPointHints; }; } // namespace @@ -159,7 +199,7 @@ DataProcessorSpec getDCSRandomDataGeneratorSpec() return DataProcessorSpec{ "dcs-random-data-generator", Inputs{}, - Outputs{{{"outputDCS"}, "DCS", "DATAPOINTS"}, {{"outputDCSdelta"}, "DCS", "DATAPOINTSdelta"}}, + Outputs{{{"outputDCS"}, "DCS", "DATAPOINTS"}, {{"outputDCSTOF"}, "DCS", "TOFDATAPOINTS"}}, AlgorithmSpec{adaptFromTask()}, Options{ {"max-timeframes", VariantType::Int64, 99999999999ll, {"max TimeFrames to generate"}}, diff --git a/Detectors/DCS/testWorkflow/dcs-sim-workflow.cxx b/Detectors/DCS/testWorkflow/dcs-sim-workflow.cxx new file mode 100644 index 0000000000000..b378578105ebf --- /dev/null +++ b/Detectors/DCS/testWorkflow/dcs-sim-workflow.cxx @@ -0,0 +1,35 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DetectorsDCS/DataPointIdentifier.h" +#include "DetectorsDCS/DataPointValue.h" +#include "Framework/TypeTraits.h" +#include +#include "Framework/DataProcessorSpec.h" +#include "DCSRandomDataGeneratorSpec.h" + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + specs.emplace_back(getDCSRandomDataGeneratorSpec()); + return specs; +} diff --git a/Detectors/TOF/CMakeLists.txt b/Detectors/TOF/CMakeLists.txt index 7ac94855f7b2d..3afd67c327f02 100644 --- a/Detectors/TOF/CMakeLists.txt +++ b/Detectors/TOF/CMakeLists.txt @@ -15,5 +15,6 @@ add_subdirectory(reconstruction) add_subdirectory(compression) if(BUILD_TESTING) add_subdirectory(prototyping) + add_subdirectory(calibration/macros) endif() add_subdirectory(workflow) diff --git a/Detectors/TOF/base/include/TOFBase/Geo.h b/Detectors/TOF/base/include/TOFBase/Geo.h index 66dcc3feea9e8..f98be5d4d012d 100644 --- a/Detectors/TOF/base/include/TOFBase/Geo.h +++ b/Detectors/TOF/base/include/TOFBase/Geo.h @@ -43,6 +43,7 @@ class Geo static void getPos(Int_t* det, Float_t* pos); static void getVolumePath(const Int_t* ind, Char_t* path); static Int_t getStripNumberPerSM(Int_t iplate, Int_t istrip); + static void getStripAndModule(Int_t iStripPerSM, Int_t& iplate, Int_t& istrip); // Return the module and strip per module corresponding to the strip number per SM static Float_t getAngles(Int_t iplate, Int_t istrip) { return ANGLES[iplate][istrip]; } static Float_t getHeights(Int_t iplate, Int_t istrip) { return HEIGHTS[iplate][istrip]; } diff --git a/Detectors/TOF/base/src/Geo.cxx b/Detectors/TOF/base/src/Geo.cxx index 79c12f697d934..6bad7895f0c09 100644 --- a/Detectors/TOF/base/src/Geo.cxx +++ b/Detectors/TOF/base/src/Geo.cxx @@ -723,3 +723,32 @@ Int_t Geo::getIndexFromEquipment(Int_t icrate, Int_t islot, Int_t ichain, Int_t { return 0; // to be implemented } + +void Geo::getStripAndModule(Int_t iStripPerSM, Int_t& iplate, Int_t& istrip) +{ + // + // Convert the serial number of the TOF strip number iStripPerSM [0,90] + // in module number iplate [0,4] and strip number istrip [0,14/18]. + // Copied from AliRoot TOF::AliTOFGeometry + // + + if (iStripPerSM < 0 || iStripPerSM >= NSTRIPC + NSTRIPB + NSTRIPA + NSTRIPB + NSTRIPC) { + iplate = -1; + istrip = -1; + } else if (iStripPerSM < NSTRIPC) { + iplate = 0; + istrip = iStripPerSM; + } else if (iStripPerSM >= NSTRIPC && iStripPerSM < NSTRIPC + NSTRIPB) { + iplate = 1; + istrip = iStripPerSM - NSTRIPC; + } else if (iStripPerSM >= NSTRIPC + NSTRIPB && iStripPerSM < NSTRIPC + NSTRIPB + NSTRIPA) { + iplate = 2; + istrip = iStripPerSM - NSTRIPC - NSTRIPB; + } else if (iStripPerSM >= NSTRIPC + NSTRIPB + NSTRIPA && iStripPerSM < NSTRIPC + NSTRIPB + NSTRIPA + NSTRIPB) { + iplate = 3; + istrip = iStripPerSM - NSTRIPC - NSTRIPB - NSTRIPA; + } else if (iStripPerSM >= NSTRIPC + NSTRIPB + NSTRIPA + NSTRIPB && iStripPerSM < NSTRIPC + NSTRIPB + NSTRIPA + NSTRIPB + NSTRIPC) { + iplate = 4; + istrip = iStripPerSM - NSTRIPC - NSTRIPB - NSTRIPA - NSTRIPB; + } +} diff --git a/Detectors/TOF/calibration/CMakeLists.txt b/Detectors/TOF/calibration/CMakeLists.txt index 75c7ce9d792a8..fc969a2108616 100644 --- a/Detectors/TOF/calibration/CMakeLists.txt +++ b/Detectors/TOF/calibration/CMakeLists.txt @@ -15,9 +15,11 @@ o2_add_library(TOFCalibration src/LHCClockCalibrator.cxx src/TOFChannelCalibrator.cxx src/TOFCalibCollector.cxx + src/TOFDCSProcessor.cxx PUBLIC_LINK_LIBRARIES O2::DataFormatsTOF O2::TOFBase O2::CCDB O2::DetectorsCalibration + O2::DetectorsDCS ROOT::Minuit ms_gsl::ms_gsl) @@ -28,7 +30,8 @@ o2_target_root_dictionary(TOFCalibration include/TOFCalibration/LHCClockCalibrator.h include/TOFCalibration/TOFChannelCalibrator.h include/TOFCalibration/TOFCalibCollector.h - include/TOFCalibration/CollectCalibInfoTOF.h) + include/TOFCalibration/CollectCalibInfoTOF.h + include/TOFCalibration/TOFDCSProcessor.h) o2_add_executable(data-generator-workflow @@ -73,3 +76,10 @@ o2_add_executable(tof-collect-calib-workflow PUBLIC_LINK_LIBRARIES O2::Framework O2::TOFCalibration O2::DetectorsCalibration) + +o2_add_executable(tof-dcs-workflow + COMPONENT_NAME calibration + SOURCES testWorkflow/tof-dcs-data-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::TOFCalibration + O2::DetectorsDCS) diff --git a/Detectors/TOF/calibration/include/TOFCalibration/TOFDCSProcessor.h b/Detectors/TOF/calibration/include/TOFCalibration/TOFDCSProcessor.h new file mode 100644 index 0000000000000..120e08a24ab49 --- /dev/null +++ b/Detectors/TOF/calibration/include/TOFCalibration/TOFDCSProcessor.h @@ -0,0 +1,160 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef DETECTOR_TOFDCSPROCESSOR_H_ +#define DETECTOR_TOFDCSPROCESSOR_H_ + +#include +#include +#include +#include +#include +#include "Framework/Logger.h" +#include "DetectorsDCS/DataPointCompositeObject.h" +#include "DetectorsDCS/DataPointIdentifier.h" +#include "DetectorsDCS/DataPointValue.h" +#include "DetectorsDCS/DeliveryType.h" +#include "CCDB/CcdbObjectInfo.h" +#include "CommonUtils/MemFileHelper.h" +#include "CCDB/CcdbApi.h" +#include +#include "TOFBase/Geo.h" + +/// @brief Class to process DCS data points + +namespace o2 +{ +namespace tof +{ + +using DPID = o2::dcs::DataPointIdentifier; +using DPVAL = o2::dcs::DataPointValue; +using DPCOM = o2::dcs::DataPointCompositeObject; + +struct TOFDCSinfo { + std::pair firstValue; // first value seen by the TOF DCS processor + std::pair lastValue; // last value seen by the TOF DCS processor + std::pair midValue; // mid value seen by the TOF DCS processor + std::pair maxChange; // maximum variation seen by the TOF DCS processor + TOFDCSinfo() + { + firstValue = std::make_pair(0, -999999999); + lastValue = std::make_pair(0, -999999999); + midValue = std::make_pair(0, -999999999); + maxChange = std::make_pair(0, -999999999); + } + void makeEmpty() + { + firstValue.first = lastValue.first = midValue.first = maxChange.first = 0; + firstValue.second = lastValue.second = midValue.second = maxChange.second = -999999999; + } + void print() const; + + ClassDefNV(TOFDCSinfo, 1); +}; + +struct TOFFEACinfo { + std::array stripInSM = {-1, -1, -1, -1, -1, -1}; + int32_t firstPadX = -1; + int32_t lastPadX = -1; +}; + +class TOFDCSProcessor +{ + + public: + using TFType = uint64_t; + using CcdbObjectInfo = o2::ccdb::CcdbObjectInfo; + using DQDoubles = std::deque; + + static constexpr int NFEACS = 8; + + TOFDCSProcessor() = default; + ~TOFDCSProcessor() = default; + + void init(const std::vector& pids); + + //int process(const std::vector& dps); + int process(const gsl::span dps); + int processDP(const DPCOM& dpcom); + virtual uint64_t processFlags(uint64_t flag, const char* pid); + + void finalize(); + void getStripsConnectedToFEAC(int nDDL, int nFEAC, TOFFEACinfo& info) const; + void updateFEACCCDB(); + void updateHVCCDB(); + + const CcdbObjectInfo& getccdbDPsInfo() const { return mccdbDPsInfo; } + CcdbObjectInfo& getccdbDPsInfo() { return mccdbDPsInfo; } + const std::unordered_map& getTOFDPsInfo() const { return mTOFDCS; } + + const CcdbObjectInfo& getccdbLVInfo() const { return mccdbLVInfo; } + CcdbObjectInfo& getccdbLVInfo() { return mccdbLVInfo; } + const std::bitset& getLVStatus() const { return mFeac; } + const bool isLVUpdated() const { return mUpdateFeacStatus; } + + const CcdbObjectInfo& getccdbHVInfo() const { return mccdbHVInfo; } + CcdbObjectInfo& getccdbHVInfo() { return mccdbHVInfo; } + const std::bitset& getHVStatus() const { return mHV; } + const bool isHVUpdated() const { return mUpdateHVStatus; } + + template + void prepareCCDBobjectInfo(T& obj, CcdbObjectInfo& info, const std::string& path, TFType tf, + const std::map& md); + + void setTF(TFType tf) { mTF = tf; } + void useVerboseMode() { mVerbose = true; } + + private: + std::unordered_map mTOFDCS; // this is the object that will go to the CCDB + std::unordered_map mPids; // contains all PIDs for the processor, the bool + // will be true if the DP was processed at least once + std::unordered_map> mDpsdoublesmap; // this is the map that will hold the DPs for the + // double type (voltages and currents) + + std::array, Geo::kNDDL> mFeacInfo; // contains the strip/pad info per FEAC + std::array, Geo::kNDDL> mPrevFEACstatus; // previous FEAC status + std::bitset mFeac; // bitset with feac status per channel + bool mUpdateFeacStatus = false; // whether to update the FEAC status in CCDB or not + std::bitset mHV; // bitset with HV status per channel + std::array, Geo::NSECTORS>, Geo::NPLATES> mPrevHVstatus; // previous HV status + bool mUpdateHVStatus = false; // whether to update the HV status in CCDB or not + CcdbObjectInfo mccdbDPsInfo; + CcdbObjectInfo mccdbLVInfo; + CcdbObjectInfo mccdbHVInfo; + TFType mStartTF; // TF index for processing of first processed TF, used to store CCDB object + TFType mTF = 0; // TF index for processing, used to store CCDB object + bool mStartTFset = false; + + bool mVerbose = false; + + ClassDefNV(TOFDCSProcessor, 0); +}; + +template +void TOFDCSProcessor::prepareCCDBobjectInfo(T& obj, CcdbObjectInfo& info, const std::string& path, TFType tf, + const std::map& md) +{ + + // prepare all info to be sent to CCDB for object obj + auto clName = o2::utils::MemFileHelper::getClassName(obj); + auto flName = o2::ccdb::CcdbApi::generateFileName(clName); + info.setPath(path); + info.setObjectType(clName); + info.setFileName(flName); + info.setStartValidityTimestamp(tf); + info.setEndValidityTimestamp(99999999999999); + info.setMetaData(md); +} + +} // namespace tof +} // namespace o2 + +#endif diff --git a/Detectors/TOF/calibration/macros/CMakeLists.txt b/Detectors/TOF/calibration/macros/CMakeLists.txt new file mode 100644 index 0000000000000..47348b684982f --- /dev/null +++ b/Detectors/TOF/calibration/macros/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright CERN and copyright holders of ALICE O2. This software is distributed +# under the terms of the GNU General Public License v3 (GPL Version 3), copied +# verbatim in the file "COPYING". +# +# See http://alice-o2.web.cern.ch/license for full licensing information. +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization or +# submit itself to any jurisdiction. + +o2_add_test( + make-CCDB-Entry-For-DCS + SOURCES makeCCDBEntryForDCS.cxx + COMPONENT_NAME tof + PUBLIC_LINK_LIBRARIES O2::DetectorsDCS O2::CCDB) diff --git a/Detectors/TOF/calibration/macros/makeCCDBEntryForDCS.cxx b/Detectors/TOF/calibration/macros/makeCCDBEntryForDCS.cxx new file mode 100644 index 0000000000000..e7d07c858dd9e --- /dev/null +++ b/Detectors/TOF/calibration/macros/makeCCDBEntryForDCS.cxx @@ -0,0 +1,52 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include "TFile.h" +#include "CCDB/CcdbApi.h" +#include "DetectorsDCS/AliasExpander.h" +#include "DetectorsDCS/DeliveryType.h" +#include "DetectorsDCS/DataPointIdentifier.h" + +#include +#include + +using DPID = o2::dcs::DataPointIdentifier; + +int main(int argc, char* argv[]) //const std::string url = "http://localhost:8080") +{ + + std::string url(argv[0]); + // macro to populate CCDB for TOF with the configuration for DCS + std::unordered_map dpid2DataDesc; + std::vector aliases = {"tof_hv_vp_[00..89]", "tof_hv_vn_[00..89]", "tof_hv_ip_[00..89]", "tof_hv_in_[00..89]"}; + std::vector aliasesInt = {"TOF_FEACSTATUS_[00..71]", "TOF_HVSTATUS_SM[00..01]MOD[0..1]"}; + std::vector expaliases = o2::dcs::expandAliases(aliases); + std::vector expaliasesInt = o2::dcs::expandAliases(aliasesInt); + + DPID dpidtmp; + for (size_t i = 0; i < expaliases.size(); ++i) { + DPID::FILL(dpidtmp, expaliases[i], o2::dcs::DeliveryType::RAW_DOUBLE); + dpid2DataDesc[dpidtmp] = "TOFDATAPOINTS"; + } + for (size_t i = 0; i < expaliasesInt.size(); ++i) { + DPID::FILL(dpidtmp, expaliasesInt[i], o2::dcs::DeliveryType::RAW_INT); + dpid2DataDesc[dpidtmp] = "TOFDATAPOINTS"; + } + + o2::ccdb::CcdbApi api; + api.init(url); // or http://localhost:8080 for a local installation + std::map md; + long ts = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + api.storeAsTFileAny(&dpid2DataDesc, "TOF/DCSconfig", md, ts); + + return 0; +} diff --git a/Detectors/TOF/calibration/src/TOFCalibrationLinkDef.h b/Detectors/TOF/calibration/src/TOFCalibrationLinkDef.h index 9b7043720f9f0..cdfabd09f3ae7 100644 --- a/Detectors/TOF/calibration/src/TOFCalibrationLinkDef.h +++ b/Detectors/TOF/calibration/src/TOFCalibrationLinkDef.h @@ -33,4 +33,9 @@ #pragma link C++ class o2::calibration::TimeSlot < o2::tof::TOFCalibInfoSlot> + ; #pragma link C++ class o2::calibration::TimeSlotCalibration < o2::dataformats::CalibInfoTOF, o2::tof::TOFCalibInfoSlot> + ; +#pragma link C++ class std::bitset < o2::tof::Geo::NCHANNELS> + ; +#pragma link C++ struct std::pair < uint64_t, double> + ; +#pragma link C++ struct o2::tof::TOFDCSinfo + ; +#pragma link C++ class std::unordered_map < o2::dcs::DataPointIdentifier, o2::tof::TOFDCSinfo> + ; + #endif diff --git a/Detectors/TOF/calibration/src/TOFDCSProcessor.cxx b/Detectors/TOF/calibration/src/TOFDCSProcessor.cxx new file mode 100644 index 0000000000000..9908076fd1a69 --- /dev/null +++ b/Detectors/TOF/calibration/src/TOFDCSProcessor.cxx @@ -0,0 +1,561 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include "Rtypes.h" +#include +#include +#include +#include +#include +#include + +using namespace o2::tof; +using namespace o2::dcs; + +using DeliveryType = o2::dcs::DeliveryType; +using DPID = o2::dcs::DataPointIdentifier; +using DPVAL = o2::dcs::DataPointValue; + +ClassImp(o2::tof::TOFDCSinfo); + +void TOFDCSinfo::print() const +{ + LOG(INFO) << "First Value: timestamp = " << firstValue.first << ", value = " << firstValue.second; + LOG(INFO) << "Last Value: timestamp = " << lastValue.first << ", value = " << lastValue.second; + LOG(INFO) << "Mid Value: timestamp = " << midValue.first << ", value = " << midValue.second; + LOG(INFO) << "Max Change: timestamp = " << maxChange.first << ", value = " << maxChange.second; +} + +//__________________________________________________________________ + +void TOFDCSProcessor::init(const std::vector& pids) +{ + // fill the array of the DPIDs that will be used by TOF + // pids should be provided by CCDB + + for (const auto& it : pids) { + mPids[it] = false; + mTOFDCS[it].makeEmpty(); + } + + for (int iddl = 0; iddl < Geo::kNDDL; ++iddl) { + for (int ifeac = 0; ifeac < NFEACS; ++ifeac) { + getStripsConnectedToFEAC(iddl, ifeac, mFeacInfo[iddl][ifeac]); + } + } +} + +//__________________________________________________________________ + +int TOFDCSProcessor::process(const gsl::span dps) +{ + + // first we check which DPs are missing - if some are, it means that + // the delta map was sent + if (mVerbose) { + LOG(INFO) << "\n\n\nProcessing new TF\n-----------------"; + } + if (!mStartTFset) { + mStartTF = mTF; + mStartTFset = true; + } + + std::unordered_map mapin; + for (auto& it : dps) { + mapin[it.id] = it.data; + } + for (auto& it : mPids) { + const auto& el = mapin.find(it.first); + if (el == mapin.end()) { + LOG(DEBUG) << "DP " << it.first << " not found in map"; + } else { + LOG(DEBUG) << "DP " << it.first << " found in map"; + } + } + + mUpdateFeacStatus = false; // by default, we do not foresee a new entry in the CCDB for the FEAC + mUpdateHVStatus = false; // by default, we do not foresee a new entry in the CCDB for the HV + + // now we process all DPs, one by one + for (const auto& it : dps) { + // we process only the DPs defined in the configuration + const auto& el = mPids.find(it.id); + if (el == mPids.end()) { + LOG(INFO) << "DP " << it.id << " not found in TOFDCSProcessor, we will not process it"; + continue; + } + processDP(it); + mPids[it.id] = true; + } + + if (mUpdateFeacStatus) { + updateFEACCCDB(); + } + + if (mUpdateHVStatus) { + updateHVCCDB(); + } + + return 0; +} + +//__________________________________________________________________ + +int TOFDCSProcessor::processDP(const DPCOM& dpcom) +{ + + // processing single DP + + auto& dpid = dpcom.id; + const auto& type = dpid.get_type(); + auto& val = dpcom.data; + if (mVerbose) { + if (type == RAW_DOUBLE) { + LOG(INFO); + LOG(INFO) << "Processing DP = " << dpcom << ", with value = " << o2::dcs::getValue(dpcom); + } else if (type == RAW_INT) { + LOG(INFO); + LOG(INFO) << "Processing DP = " << dpcom << ", with value = " << o2::dcs::getValue(dpcom); + } + } + auto flags = val.get_flags(); + if (processFlags(flags, dpid.get_alias()) == 0) { + // now I need to access the correct element + if (type == RAW_DOUBLE) { + // for these DPs, we will store the first, last, mid value, plus the value where the maximum variation occurred + auto& dvect = mDpsdoublesmap[dpid]; + LOG(DEBUG) << "mDpsdoublesmap[dpid].size() = " << dvect.size(); + auto etime = val.get_epoch_time(); + if (dvect.size() == 0 || + etime != dvect.back().get_epoch_time()) { // we check + // that we did not get the + // same timestamp as the + // latest one + dvect.push_back(val); + } + } + + if (type == RAW_INT) { + // for these DPs, we need some processing + if (std::strstr(dpid.get_alias(), "FEACSTATUS") != nullptr) { // DP is FEACSTATUS + std::string aliasStr(dpid.get_alias()); + // extracting DDL number (regex is quite slow, using this way) + const auto offs = std::strlen("TOF_FEACSTATUS_"); + std::size_t const nn = aliasStr.find_first_of("0123456789", offs); + std::size_t const mm = aliasStr.find_first_not_of("0123456789", nn); + std::string ddlStr = aliasStr.substr(nn, mm != std::string::npos ? mm - nn : mm); + auto iddl = std::stoi(ddlStr); + std::bitset<8> feacstatus(o2::dcs::getValue(dpcom)); + if (mVerbose) { + LOG(INFO) << "DDL: " << iddl << ": Prev FEAC = " << mPrevFEACstatus[iddl] << ", new = " << feacstatus; + } + if (feacstatus == mPrevFEACstatus[iddl]) { + if (mVerbose) { + LOG(INFO) << "Same FEAC status as before, we do nothing"; + } + return 0; + } + if (mVerbose) { + LOG(INFO) << "Something changed in LV for DDL " << iddl << ", we need to check what"; + } + mUpdateFeacStatus = true; + int plate = -1, strip = -1; + int det[5] = {iddl / 4, -1, -1, -1, -1}; + for (auto ifeac = 0; ifeac < NFEACS; ++ifeac) { // we have one bit per FEAC + auto singlefeacstatus = feacstatus[ifeac]; + for (int istrip = 0; istrip < 6; ++istrip) { + if (mFeacInfo[iddl][ifeac].stripInSM[istrip] == -1) { + continue; + } + for (int ipadz = 0; ipadz < Geo::NPADZ; ++ipadz) { + for (int ipadx = mFeacInfo[iddl][ifeac].firstPadX; ipadx <= mFeacInfo[iddl][ifeac].lastPadX; ++ipadx) { + Geo::getStripAndModule(mFeacInfo[iddl][ifeac].stripInSM[istrip], plate, strip); + det[1] = plate; + det[2] = strip; + det[3] = ipadz; + det[4] = ipadx; + int channelIdx = Geo::getIndex(det); + if (mFeac[channelIdx] != singlefeacstatus) { + mFeac[channelIdx] = singlefeacstatus; + } + } + } + } + } // end loop on FEACs + if (mVerbose) { + LOG(INFO) << "Updating previous FEAC status for DDL " << iddl; + } + mPrevFEACstatus[iddl] = feacstatus; + } // end processing current DP, when it is of type FEACSTATUS + + if (std::strstr(dpid.get_alias(), "HVSTATUS") != nullptr) { // DP is HVSTATUS + std::string aliasStr(dpid.get_alias()); + // extracting SECTOR and PLATE number (regex is quite slow, using this way) + const auto offs = std::strlen("TOF_HVSTATUS_SM"); + std::size_t const nn = aliasStr.find_first_of("0123456789", offs); + std::size_t const mm = aliasStr.find_first_not_of("0123456789", nn); + std::size_t const oo = aliasStr.find_first_of("0123456789", mm); + std::size_t const pp = aliasStr.find_first_not_of("0123456789", oo); + std::string sectorStr = aliasStr.substr(nn, mm != std::string::npos ? mm - nn : mm); + auto isect = std::stoi(sectorStr); + std::string plateStr = aliasStr.substr(oo, pp != std::string::npos ? pp - oo : pp); + auto iplat = std::stoi(plateStr); + std::bitset<19> hvstatus(o2::dcs::getValue(dpcom)); + if (mVerbose) { + LOG(INFO) << "Sector: " << isect << ", plate = " << iplat << ": Prev HV = " + << mPrevHVstatus[iplat][isect] << ", new = " << hvstatus; + } + if (hvstatus == mPrevHVstatus[iplat][isect]) { + if (mVerbose) { + LOG(INFO) << "Same HV status as before, we do nothing"; + } + return 0; + } + if (mVerbose) { + LOG(INFO) << "Something changed in HV for Sect " << isect << " and plate " + << iplat << ", we need to check what"; + } + mUpdateHVStatus = true; + int det[5] = {isect, iplat, -1, -1, -1}; + auto nStrips = (iplat == 2 ? Geo::NSTRIPA : (iplat == 0 || iplat == 4) ? Geo::NSTRIPC : Geo::NSTRIPB); + for (auto istrip = 0; istrip < nStrips; ++istrip) { + auto singlestripHV = hvstatus[istrip]; + for (int ipadz = 0; ipadz < Geo::NPADZ; ++ipadz) { + for (int ipadx = 0; ipadx < Geo::NPADX; ++ipadx) { + det[2] = istrip; + det[3] = ipadz; + det[4] = ipadx; + int channelIdx = Geo::getIndex(det); + if (mHV[channelIdx] != singlestripHV) { + mHV[channelIdx] = singlestripHV; + } + } + } + } // end loop on strips + if (mVerbose) { + LOG(INFO) << "Updating previous HV status for Sector: " << isect << ", plate = " << iplat; + } + mPrevHVstatus[iplat][isect] = hvstatus; + } //end processing current DP, when it is of type HVSTATUS + } + } + return 0; +} + +//______________________________________________________________________ + +uint64_t TOFDCSProcessor::processFlags(const uint64_t flags, const char* pid) +{ + + // function to process the flag. the return code zero means that all is fine. + // anything else means that there was an issue + + // for now, I don't know how to use the flags, so I do nothing + + if (flags & DataPointValue::KEEP_ALIVE_FLAG) { + LOG(DEBUG) << "KEEP_ALIVE_FLAG active for DP " << pid; + } + if (flags & DataPointValue::END_FLAG) { + LOG(DEBUG) << "END_FLAG active for DP " << pid; + } + if (flags & DataPointValue::FBI_FLAG) { + LOG(DEBUG) << "FBI_FLAG active for DP " << pid; + } + if (flags & DataPointValue::NEW_FLAG) { + LOG(DEBUG) << "NEW_FLAG active for DP " << pid; + } + if (flags & DataPointValue::DIRTY_FLAG) { + LOG(DEBUG) << "DIRTY_FLAG active for DP " << pid; + } + if (flags & DataPointValue::TURN_FLAG) { + LOG(DEBUG) << "TURN_FLAG active for DP " << pid; + } + if (flags & DataPointValue::WRITE_FLAG) { + LOG(DEBUG) << "WRITE_FLAG active for DP " << pid; + } + if (flags & DataPointValue::READ_FLAG) { + LOG(DEBUG) << "READ_FLAG active for DP " << pid; + } + if (flags & DataPointValue::OVERWRITE_FLAG) { + LOG(DEBUG) << "OVERWRITE_FLAG active for DP " << pid; + } + if (flags & DataPointValue::VICTIM_FLAG) { + LOG(DEBUG) << "VICTIM_FLAG active for DP " << pid; + } + if (flags & DataPointValue::DIM_ERROR_FLAG) { + LOG(DEBUG) << "DIM_ERROR_FLAG active for DP " << pid; + } + if (flags & DataPointValue::BAD_DPID_FLAG) { + LOG(DEBUG) << "BAD_DPID_FLAG active for DP " << pid; + } + if (flags & DataPointValue::BAD_FLAGS_FLAG) { + LOG(DEBUG) << "BAD_FLAGS_FLAG active for DP " << pid; + } + if (flags & DataPointValue::BAD_TIMESTAMP_FLAG) { + LOG(DEBUG) << "BAD_TIMESTAMP_FLAG active for DP " << pid; + } + if (flags & DataPointValue::BAD_PAYLOAD_FLAG) { + LOG(DEBUG) << "BAD_PAYLOAD_FLAG active for DP " << pid; + } + if (flags & DataPointValue::BAD_FBI_FLAG) { + LOG(DEBUG) << "BAD_FBI_FLAG active for DP " << pid; + } + + return 0; +} + +//______________________________________________________________________ + +void TOFDCSProcessor::finalize() +{ + + // here we create the object to then be sent to CCDB + LOG(INFO) << "Finalizing"; + union Converter { + uint64_t raw_data; + double double_value; + } converter0, converter1; + + for (const auto& it : mPids) { + const auto& type = it.first.get_type(); + if (type == o2::dcs::RAW_DOUBLE) { + auto& tofdcs = mTOFDCS[it.first]; + if (it.second == true) { // we processed the DP at least 1x + auto& dpvect = mDpsdoublesmap[it.first]; + tofdcs.firstValue.first = dpvect[0].get_epoch_time(); + converter0.raw_data = dpvect[0].payload_pt1; + tofdcs.firstValue.second = converter0.double_value; + tofdcs.lastValue.first = dpvect.back().get_epoch_time(); + converter0.raw_data = dpvect.back().payload_pt1; + tofdcs.lastValue.second = converter0.double_value; + // now I will look for the max change + if (dpvect.size() > 1) { + auto deltatime = dpvect.back().get_epoch_time() - dpvect[0].get_epoch_time(); + if (deltatime < 60000) { + // if we did not cover at least 1 minute, + // max variation is defined as the difference between first and last value + converter0.raw_data = dpvect[0].payload_pt1; + converter1.raw_data = dpvect.back().payload_pt1; + double delta = std::abs(converter0.double_value - converter1.double_value); + tofdcs.maxChange.first = deltatime; // is it ok to do like this, as in Run 2? + tofdcs.maxChange.second = delta; + } else { + for (auto i = 0; i < dpvect.size() - 1; ++i) { + for (auto j = i + 1; j < dpvect.size(); ++j) { + auto deltatime = dpvect[j].get_epoch_time() - dpvect[i].get_epoch_time(); + if (deltatime >= 60000) { // we check every min; epoch_time in ms + converter0.raw_data = dpvect[i].payload_pt1; + converter1.raw_data = dpvect[j].payload_pt1; + double delta = std::abs(converter0.double_value - converter1.double_value); + if (delta > tofdcs.maxChange.second) { + tofdcs.maxChange.first = deltatime; // is it ok to do like this, as in Run 2? + tofdcs.maxChange.second = delta; + } + } + } + } + } + // mid point + auto midIdx = dpvect.size() / 2 - 1; + tofdcs.midValue.first = dpvect[midIdx].get_epoch_time(); + converter0.raw_data = dpvect[midIdx].payload_pt1; + tofdcs.midValue.second = converter0.double_value; + } else { + tofdcs.maxChange.first = dpvect[0].get_epoch_time(); + converter0.raw_data = dpvect[0].payload_pt1; + tofdcs.maxChange.second = converter0.double_value; + tofdcs.midValue.first = dpvect[0].get_epoch_time(); + converter0.raw_data = dpvect[0].payload_pt1; + tofdcs.midValue.second = converter0.double_value; + } + } + if (mVerbose) { + LOG(INFO) << "PID = " << it.first.get_alias(); + tofdcs.print(); + } + } + } + std::map md; + prepareCCDBobjectInfo(mTOFDCS, mccdbDPsInfo, "TOF/DCSDPs", mStartTF, md); + + return; +} + +//______________________________________________________________________ + +void TOFDCSProcessor::updateFEACCCDB() +{ + + // we need to update a CCDB for the FEAC status --> let's prepare the CCDBInfo + + if (mVerbose) { + LOG(INFO) << "At least one FEAC changed status --> we will update CCDB"; + } + std::map md; + prepareCCDBobjectInfo(mFeac, mccdbLVInfo, "TOF/LVStatus", mTF, md); + return; +} + +//______________________________________________________________________ + +void TOFDCSProcessor::updateHVCCDB() +{ + + // we need to update a CCDB for the HV status --> let's prepare the CCDBInfo + + if (mVerbose) { + LOG(INFO) << "At least one HV changed status --> we will update CCDB"; + } + std::map md; + prepareCCDBobjectInfo(mHV, mccdbHVInfo, "TOF/HVStatus", mTF, md); + return; +} + +//_______________________________________________________________________ + +void TOFDCSProcessor::getStripsConnectedToFEAC(int nDDL, int nFEAC, TOFFEACinfo& info) const +{ + + // + // Taken from AliRoot/TOF/AliTOFLvHvDataPoints.cxx + // + // FEAC-strip mapping: + // return the strips and first PadX numbers + // connected to the FEAC number nFEAC in the crate number nDDL + // + + switch (nDDL % 4) { + case 0: + info.firstPadX = 0; + info.lastPadX = Geo::NPADX / 2 - 1; + + if (nFEAC <= 2) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC; + } + } else if (nFEAC == 3) { + for (int ii = 0; ii < 5; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC; + } + } else if (nFEAC == 4) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC - 1; + } + } else if (nFEAC == 5) { + for (int ii = 0; ii < 5; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC - 1; + } + } else if (nFEAC == 6) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC - 2; + } + } else if (nFEAC == 7) { + for (int ii = 0; ii < 5; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC - 2; + } + } + + break; + case 1: + info.firstPadX = Geo::NPADX / 2; + info.lastPadX = Geo::NPADX - 1; + + if (nFEAC <= 2) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC; + } + } else if (nFEAC == 3) { + for (int ii = 0; ii < 5; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC; + } + } else if (nFEAC == 4) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC - 1; + } + } else if (nFEAC == 5) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC - 1; + } + } else if (nFEAC == 6) { + for (int ii = 0; ii < 5; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC - 1; + } + } else if (nFEAC == 7) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = ii + 6 * nFEAC - 2; + } + } + + break; + case 2: + info.firstPadX = Geo::NPADX / 2; + info.lastPadX = Geo::NPADX - 1; + + if (nFEAC <= 2) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC); + } + } else if (nFEAC == 3) { + for (int ii = 0; ii < 5; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC); + } + } else if (nFEAC == 4) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC - 1); + } + } else if (nFEAC == 5) { + for (int ii = 0; ii < 5; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC - 1); + } + } else if (nFEAC == 6) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC - 2); + } + } else if (nFEAC == 7) { + for (int ii = 0; ii < 5; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC - 2); + } + } + + break; + case 3: + info.firstPadX = 0; + info.lastPadX = Geo::NPADX / 2 - 1; + + if (nFEAC <= 2) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC); + } + } else if (nFEAC == 3) { + for (int ii = 0; ii < 5; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC); + } + } else if (nFEAC == 4) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC - 1); + } + } else if (nFEAC == 5) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC - 1); + } + } else if (nFEAC == 6) { + for (int ii = 0; ii < 5; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC - 1); + } + } else if (nFEAC == 7) { + for (int ii = 0; ii < 6; ++ii) { + info.stripInSM[ii] = 90 - (ii + 6 * nFEAC - 2); + } + } + + break; + } +} diff --git a/Detectors/TOF/calibration/testWorkflow/README.md b/Detectors/TOF/calibration/testWorkflow/README.md new file mode 100644 index 0000000000000..3b902da31a6da --- /dev/null +++ b/Detectors/TOF/calibration/testWorkflow/README.md @@ -0,0 +1,94 @@ + + +# TOF calibration workflows + +## DCS DP processing: + +Local example workflow with local CCDB (running on port 8080) : + +This will read the list of DPs to be associated to TOF from CCDB (remove +`--use-ccdb-to-configure` if you don't want this, but use hardcoded +aliases. You can specify the path of CCDB also with `--ccdb-path`. +YOu can also specify to run in verbose mode (`--use-verbose-mode`) + +```shell +o2-dcs-sim-workflow --max-timeframes 3 --delta-fraction 0.5 -b | +o2-calibration-tof-dcs-workflow --use-ccdb-to-configure -b | +o2-calibration-ccdb-populator-workflow --ccdb-path="http://localhost:8080" -b +``` +To populate locally a DCS entry for the configuration, run the: + +`O2/Detectors/TOF/calibration/macros/makeCCDBEntryForDCS.C` + +macro. + + +## LHC phase: + +This will process the LHC phase simulated by the Generator workflow + +```shell +LHC phase +o2-calibration-data-generator-workflow --lanes 10 --mean-latency 100000 --max-timeframes 500 | +o2-calibration-lhc-clockphase-workflow --tf-per-slot 20 | +o2-calibration-ccdb-populator-workflow --ccdb-path localhost:8080 +``` + +## TOF channel calibration: + +To obtain the TOF channel offsets (at end of processing). Input from simulation, but is should work if attached to reco+calib flow + +* simulating reading from ccdb, and using it, with "-b", in "test" mode --> to use this, we need an appropriate CCDB object in the CCDB + +```shell +o2-calibration-data-generator-workflow --lanes 10 --mean-latency 100000 --max-timeframes 500 --do-TOF-channel-calib --do-TOF-channel-calib-in-test-mode -b | +o2-testworkflows-tof-dummy-ccdb -b | +o2-calibration-tof-channel-calib-workflow --min-entries 50 --do-TOF-channel-calib-in-test-mode --use-ccdb -b | +o2-calibration-ccdb-populator-workflow --ccdb-path localhost:8080 -b +``` + +* simulating reading from ccdb, but not using it, with "-b", in "test" mode --> to use this, we need an appropriate CCDB object in the CCDB + +``` +o2-calibration-data-generator-workflow --lanes 10 --mean-latency 100000 --max-timeframes 500 --do-TOF-channel-calib --do-TOF-channel-calib-in-test-mode -b | +o2-testworkflows-tof-dummy-ccdb -b +| o2-calibration-tof-channel-calib-workflow --min-entries 50 --do-TOF-channel-calib-in-test-mode -b +| o2-calibration-ccdb-populator-workflow --ccdb-path localhost:8080 -b +``` + +* Using the workflow that has both LHCclockPhase and TOFChannelCalib; for now I can enable only one, or the CCDB populator will not work + +```shell +o2-calibration-data-generator-workflow --lanes 10 --mean-latency 100000 --max-timeframes 500 --do-TOF-channel-calib --do-TOF-channel-calib-in-test-mode -b | +o2-calibration-tof-dummy-ccdb-for-calib -b | +o2-calibration-tof-calib-workflow --do-channel-offset --min-entries 50 --do-TOF-channel-calib-in-test-mode -b | +o2-calibration-ccdb-populator-workflow --ccdb-path localhost:8080 -b +``` +* same as above, enabling CCDB + +```shell +o2-calibration-data-generator-workflow --lanes 10 --mean-latency 100000 --max-timeframes 500 --do-TOF-channel-calib --do-TOF-channel-calib-in-test-mode -b | +o2-calibration-tof-dummy-ccdb-for-calib -b | +o2-calibration-tof-calib-workflow --do-channel-offset --use-ccdb --min-entries 50 --do-TOF-channel-calib-in-test-mode -b | +o2-calibration-ccdb-populator-workflow --ccdb-path localhost:8080 -b +``` + +## TimeSlewing: + +For Time Slewing. Will save the Time Slewing information in files when a certain condition is reached. A post-processing +should then take care of extracting the CCDB + +* test mode: + +``` shell +o2-calibration-data-generator-workflow --lanes 10 --mean-latency 100000 --max-timeframes 10 -b | +o2-calibration-tof-collect-calib-workflow --tf-sending-policy --running-in-test-mode -b + +* non-test but simplified (using option "is-max-number-hits-to-fill-tree-absolute"): + +```shell +o2-calibration-data-generator-workflow --lanes 10 --mean-latency 100000 --max-timeframes 10 -b | +o2-calibration-tof-collect-calib-workflow --max-number-hits-to-fill-tree 300 --is-max-number-hits-to-fill-tree-absolute -b +``` \ No newline at end of file diff --git a/Detectors/TOF/calibration/testWorkflow/TOFDCSDataProcessorSpec.h b/Detectors/TOF/calibration/testWorkflow/TOFDCSDataProcessorSpec.h new file mode 100644 index 0000000000000..bf31e0397b40c --- /dev/null +++ b/Detectors/TOF/calibration/testWorkflow/TOFDCSDataProcessorSpec.h @@ -0,0 +1,176 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TOF_DATAPROCESSOR_H +#define O2_TOF_DATAPROCESSOR_H + +/// @file DCSTOFDataProcessorSpec.h +/// @brief TOF Processor for DCS Data Points + +#include +#include +#include +#include "DetectorsDCS/DataPointIdentifier.h" +#include "DetectorsDCS/DataPointValue.h" +#include "DetectorsDCS/DataPointCompositeObject.h" +#include "DetectorsDCS/DeliveryType.h" +#include "DetectorsDCS/AliasExpander.h" +#include "TOFCalibration/TOFDCSProcessor.h" +#include "DetectorsCalibration/Utils.h" +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include "Framework/DeviceSpec.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/Task.h" +#include "Framework/Logger.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace tof +{ + +using DPID = o2::dcs::DataPointIdentifier; +using DPVAL = o2::dcs::DataPointValue; +using DPCOM = o2::dcs::DataPointCompositeObject; +using namespace o2::ccdb; +using CcdbManager = o2::ccdb::BasicCCDBManager; +using clbUtils = o2::calibration::Utils; + +class TOFDCSDataProcessor : public o2::framework::Task +{ + public: + void init(o2::framework::InitContext& ic) final + { + + std::vector vect; + bool useCCDBtoConfigure = ic.options().get("use-ccdb-to-configure"); + if (useCCDBtoConfigure) { + LOG(INFO) << "Configuring via CCDB"; + std::string ccdbpath = ic.options().get("ccdb-path"); + auto& mgr = CcdbManager::instance(); + mgr.setURL(ccdbpath); + CcdbApi api; + api.init(mgr.getURL()); + long ts = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + std::unordered_map* dpid2DataDesc = mgr.getForTimeStamp>("TOF/DCSconfig", ts); + for (auto& i : *dpid2DataDesc) { + vect.push_back(i.first); + } + } else { + LOG(INFO) << "Configuring via hardcoded strings"; + std::vector aliases = {"tof_hv_vp_[00..89]", "tof_hv_vn_[00..89]", "tof_hv_ip_[00..89]", "tof_hv_in_[00..89]"}; + std::vector aliasesInt = {"TOF_FEACSTATUS_[00..71]"}; + std::vector expaliases = o2::dcs::expandAliases(aliases); + std::vector expaliasesInt = o2::dcs::expandAliases(aliasesInt); + for (const auto& i : expaliases) { + vect.emplace_back(i, o2::dcs::RAW_DOUBLE); + } + for (const auto& i : expaliasesInt) { + vect.emplace_back(i, o2::dcs::RAW_INT); + } + } + + LOG(INFO) << "Listing Data Points for TOF:"; + for (auto& i : vect) { + LOG(INFO) << i; + } + + mProcessor = std::make_unique(); + bool useVerboseMode = ic.options().get("use-verbose-mode"); + LOG(INFO) << " ************************* Verbose?" << useVerboseMode; + if (useVerboseMode) { + mProcessor->useVerboseMode(); + } + mProcessor->init(vect); + } + + void run(o2::framework::ProcessingContext& pc) final + { + auto tfid = o2::header::get(pc.inputs().get("input").header)->startTime; + auto dps = pc.inputs().get>("input"); + mProcessor->setTF(tfid); + mProcessor->process(dps); + sendOutput(pc.outputs()); + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + mProcessor->finalize(); + sendOutput(ec.outputs()); + const auto& payload = mProcessor->getTOFDPsInfo(); + auto& info = mProcessor->getccdbDPsInfo(); + auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, &info); + LOG(INFO) << "Sending object " << info.getPath() << "/" << info.getFileName() << " of size " << image->size() + << " bytes, valid for " << info.getStartValidityTimestamp() << " : " << info.getEndValidityTimestamp(); + ec.outputs().snapshot(Output{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBPayload, 0}, *image.get()); + ec.outputs().snapshot(Output{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBInfo, 0}, info); + } + + private: + std::unique_ptr mProcessor; + + //________________________________________________________________ + void sendOutput(DataAllocator& output) + { + // extract CCDB infos and calibration objects, convert it to TMemFile and send them to the output + + if (mProcessor->isLVUpdated()) { + const auto& payload = mProcessor->getLVStatus(); + auto& info = mProcessor->getccdbLVInfo(); + auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, &info); + LOG(INFO) << "Sending object " << info.getPath() << "/" << info.getFileName() << " of size " << image->size() + << " bytes, valid for " << info.getStartValidityTimestamp() << " : " << info.getEndValidityTimestamp(); + output.snapshot(Output{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBPayload, 0}, *image.get()); + output.snapshot(Output{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBInfo, 0}, info); + } + if (mProcessor->isHVUpdated()) { + const auto& payload = mProcessor->getHVStatus(); + auto& info = mProcessor->getccdbHVInfo(); + auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, &info); + LOG(INFO) << "Sending object " << info.getPath() << "/" << info.getFileName() << " of size " << image->size() + << " bytes, valid for " << info.getStartValidityTimestamp() << " : " << info.getEndValidityTimestamp(); + output.snapshot(Output{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBPayload, 0}, *image.get()); + output.snapshot(Output{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBInfo, 0}, info); + } + } + +}; // end class +} // namespace tof + +namespace framework +{ + +DataProcessorSpec getTOFDCSDataProcessorSpec() +{ + + using clbUtils = o2::calibration::Utils; + + std::vector outputs; + outputs.emplace_back(ConcreteDataTypeMatcher{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBPayload}); + outputs.emplace_back(ConcreteDataTypeMatcher{clbUtils::gDataOriginCLB, clbUtils::gDataDescriptionCLBInfo}); + + return DataProcessorSpec{ + "tof-dcs-data-processor", + Inputs{{"input", "DCS", "TOFDATAPOINTS"}}, + outputs, + AlgorithmSpec{adaptFromTask()}, + Options{{"ccdb-path", VariantType::String, "http://localhost:8080", {"Path to CCDB"}}, + {"use-ccdb-to-configure", VariantType::Bool, false, {"Use CCDB to configure"}}, + {"use-verbose-mode", VariantType::Bool, false, {"Use verbose mode"}}}}; +} + +} // namespace framework +} // namespace o2 + +#endif diff --git a/Detectors/TOF/calibration/testWorkflow/tof-dcs-data-workflow.cxx b/Detectors/TOF/calibration/testWorkflow/tof-dcs-data-workflow.cxx new file mode 100644 index 0000000000000..29826c1b66505 --- /dev/null +++ b/Detectors/TOF/calibration/testWorkflow/tof-dcs-data-workflow.cxx @@ -0,0 +1,41 @@ +// Copyright CERN and copyright holders of ALICE O2. This software is +// distributed under the terms of the GNU General Public License v3 (GPL +// Version 3), copied verbatim in the file "COPYING". +// +// See http://alice-o2.web.cern.ch/license for full licensing information. +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "DetectorsDCS/DataPointIdentifier.h" +#include "DetectorsDCS/DataPointValue.h" +#include "Framework/TypeTraits.h" +#include +namespace o2::framework +{ +template <> +struct has_root_dictionary, void> : std::true_type { +}; +} // namespace o2::framework +#include "Framework/DataProcessorSpec.h" +#include "TOFDCSDataProcessorSpec.h" + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + specs.emplace_back(getTOFDCSDataProcessorSpec()); + return specs; +}