diff --git a/Framework/include/QualityControl/CheckConfig.h b/Framework/include/QualityControl/CheckConfig.h index 02f56eb20..840b18022 100644 --- a/Framework/include/QualityControl/CheckConfig.h +++ b/Framework/include/QualityControl/CheckConfig.h @@ -18,6 +18,7 @@ #define QC_CORE_CHECKCONFIG_H #include +#include #include #include @@ -35,6 +36,7 @@ struct CheckConfig : public o2::quality_control::core::UserCodeConfig { bool allowBeautify = false; framework::Inputs inputSpecs{}; framework::OutputSpec qoSpec{ "XXX", "INVALID" }; + std::unordered_map externalTaskDetectorNames; }; } // namespace o2::quality_control::checker diff --git a/Framework/include/QualityControl/CheckRunner.h b/Framework/include/QualityControl/CheckRunner.h index 80c281ab8..9e558c2a6 100644 --- a/Framework/include/QualityControl/CheckRunner.h +++ b/Framework/include/QualityControl/CheckRunner.h @@ -24,6 +24,7 @@ #include #include #include +#include #include // O2 #include @@ -100,8 +101,9 @@ class CheckRunner : public framework::Task * * @param checkRunnerConfig configuration of CheckRunner * @param input Monitor Object input spec. + * @param detectorName detector override for raw objects, or empty to use the input origin. */ - CheckRunner(CheckRunnerConfig, o2::framework::InputSpec input); + CheckRunner(CheckRunnerConfig, o2::framework::InputSpec input, std::string detectorName = {}); /// Destructor ~CheckRunner() override; @@ -215,6 +217,7 @@ class CheckRunner : public framework::Task std::string mDeviceName; std::map mChecks; std::string mDetectorName; + std::unordered_map mExternalTaskDetectorNames; std::shared_ptr mActivity; // shareable with the Checks CheckRunnerConfig mConfig; std::shared_ptr mDatabase; diff --git a/Framework/include/QualityControl/CheckRunnerFactory.h b/Framework/include/QualityControl/CheckRunnerFactory.h index ff3fba185..b8bd6b9a9 100644 --- a/Framework/include/QualityControl/CheckRunnerFactory.h +++ b/Framework/include/QualityControl/CheckRunnerFactory.h @@ -53,7 +53,7 @@ class CheckRunnerFactory * @param input InputSpec with the content to store * @param configurationSource */ - static framework::DataProcessorSpec createSinkDevice(const CheckRunnerConfig& checkRunnerConfig, const o2::framework::InputSpec& input); + static framework::DataProcessorSpec createSinkDevice(const CheckRunnerConfig& checkRunnerConfig, const o2::framework::InputSpec& input, std::string detectorName = {}); static CheckRunnerConfig extractConfig(const core::CommonSpec&); diff --git a/Framework/include/QualityControl/DataSourceSpec.h b/Framework/include/QualityControl/DataSourceSpec.h index fd0629cd0..0ecca3539 100644 --- a/Framework/include/QualityControl/DataSourceSpec.h +++ b/Framework/include/QualityControl/DataSourceSpec.h @@ -39,6 +39,7 @@ struct DataSourceSpec { DataSourceType type; std::string id; std::string name; + std::string detectorName; std::vector inputs; std::vector subInputs; // can be MO or QO names }; diff --git a/Framework/include/QualityControl/ExternalTaskSpec.h b/Framework/include/QualityControl/ExternalTaskSpec.h index 99aa12a62..990003b25 100644 --- a/Framework/include/QualityControl/ExternalTaskSpec.h +++ b/Framework/include/QualityControl/ExternalTaskSpec.h @@ -1,3 +1,4 @@ +#include #include // Copyright 2019-2020 CERN and copyright holders of ALICE O2. @@ -31,6 +32,7 @@ struct ExternalTaskSpec { std::string taskName; std::string query; + std::string detectorName; bool active = true; }; diff --git a/Framework/src/Check.cxx b/Framework/src/Check.cxx index 5620e2760..b18182028 100644 --- a/Framework/src/Check.cxx +++ b/Framework/src/Check.cxx @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -213,6 +214,7 @@ CheckConfig Check::extractConfig(const CommonSpec& commonSpec, const CheckSpec& { framework::Inputs inputs; std::vector objectNames; + std::unordered_map externalTaskDetectorNames; UpdatePolicyType updatePolicy = checkSpec.updatePolicy; bool checkAllObjects = false; for (const auto& dataSource : checkSpec.dataSources) { @@ -221,6 +223,11 @@ CheckConfig Check::extractConfig(const CommonSpec& commonSpec, const CheckSpec& "Unsupported dataSource '" + dataSource.name + "' for a Check '" + checkSpec.checkName + "'"); } inputs.insert(inputs.end(), dataSource.inputs.begin(), dataSource.inputs.end()); + if (dataSource.type == DataSourceType::ExternalTask && !dataSource.detectorName.empty()) { + for (const auto& input : dataSource.inputs) { + externalTaskDetectorNames[framework::DataSpecUtils::label(input)] = dataSource.detectorName; + } + } // Subscribe on predefined MOs. // If "MOs" are not set, the check function will be triggered whenever a new MO appears. @@ -264,6 +271,7 @@ CheckConfig Check::extractConfig(const CommonSpec& commonSpec, const CheckSpec& allowBeautify, std::move(inputs), createUserOutputSpec(DataSourceType::Check, checkSpec.detectorName, checkSpec.checkName), + std::move(externalTaskDetectorNames), }; } diff --git a/Framework/src/CheckRunner.cxx b/Framework/src/CheckRunner.cxx index 35e34f8de..1148a3d63 100644 --- a/Framework/src/CheckRunner.cxx +++ b/Framework/src/CheckRunner.cxx @@ -143,11 +143,12 @@ CheckRunner::CheckRunner(CheckRunnerConfig checkRunnerConfig, const std::vector< mTotalQOSent(0) { for (auto& checkConfig : checkConfigs) { + mExternalTaskDetectorNames.insert(checkConfig.externalTaskDetectorNames.begin(), checkConfig.externalTaskDetectorNames.end()); mChecks.emplace(checkConfig.name, checkConfig); } } -CheckRunner::CheckRunner(CheckRunnerConfig checkRunnerConfig, InputSpec input) +CheckRunner::CheckRunner(CheckRunnerConfig checkRunnerConfig, InputSpec input, std::string detectorName) : mDeviceName(createSinkCheckRunnerName(input)), mConfig(std::move(checkRunnerConfig)), mInputs{ input }, @@ -158,6 +159,9 @@ CheckRunner::CheckRunner(CheckRunnerConfig checkRunnerConfig, InputSpec input) mTotalNumberMOStored(0), mTotalQOSent(0) { + if (!detectorName.empty()) { + mExternalTaskDetectorNames.emplace(DataSpecUtils::label(input), std::move(detectorName)); + } } CheckRunner::~CheckRunner() @@ -262,7 +266,11 @@ void CheckRunner::prepareCacheData(framework::InputRecord& inputRecord) ILOG(Debug, Devel) << "The MO is null, probably a TObject could not be casted into an MO." << ENDM; ILOG(Debug, Devel) << " Creating an ad hoc MO." << ENDM; header::DataOrigin origin = DataSpecUtils::asConcreteOrigin(input); - mo = std::make_shared(tObject, input.binding, "CheckRunner", origin.str); + std::string detectorName = origin.str; + if (auto detector = mExternalTaskDetectorNames.find(DataSpecUtils::label(input)); detector != mExternalTaskDetectorNames.end()) { + detectorName = detector->second; + } + mo = std::make_shared(tObject, input.binding, "CheckRunner", detectorName); mo->setActivity(*mActivity); } diff --git a/Framework/src/CheckRunnerFactory.cxx b/Framework/src/CheckRunnerFactory.cxx index 76813892d..9e7cb0ce2 100644 --- a/Framework/src/CheckRunnerFactory.cxx +++ b/Framework/src/CheckRunnerFactory.cxx @@ -15,6 +15,7 @@ /// #include #include +#include #include #include @@ -49,9 +50,9 @@ DataProcessorSpec CheckRunnerFactory::create(CheckRunnerConfig checkRunnerConfig return newCheckRunner; } -DataProcessorSpec CheckRunnerFactory::createSinkDevice(const CheckRunnerConfig& checkRunnerConfig, const o2::framework::InputSpec& input) +DataProcessorSpec CheckRunnerFactory::createSinkDevice(const CheckRunnerConfig& checkRunnerConfig, const o2::framework::InputSpec& input, std::string detectorName) { - CheckRunner qcCheckRunner{ checkRunnerConfig, input }; + CheckRunner qcCheckRunner{ checkRunnerConfig, input, std::move(detectorName) }; qcCheckRunner.setTaskStoreSet({ DataSpecUtils::label(input) }); DataProcessorSpec newCheckRunner{ qcCheckRunner.getDeviceName(), diff --git a/Framework/src/InfrastructureGenerator.cxx b/Framework/src/InfrastructureGenerator.cxx index 089fb4a63..476b6e7e7 100644 --- a/Framework/src/InfrastructureGenerator.cxx +++ b/Framework/src/InfrastructureGenerator.cxx @@ -626,6 +626,7 @@ void InfrastructureGenerator::generateCheckRunners(framework::WorkflowSpec& work typedef std::vector InputNames; typedef std::vector CheckConfigs; std::map tasksOutputMap; // all active tasks' output, as inputs, keyed by their label + std::map externalTaskDetectorNames; std::map checksMap; // all the Checks defined in the config mapped keyed by their sorted inputNames std::map storeVectorMap; @@ -658,6 +659,9 @@ void InfrastructureGenerator::generateCheckRunners(framework::WorkflowSpec& work Inputs inputs = DataDescriptorQueryBuilder::parse(query.c_str()); for (const auto& taskOutput : inputs) { tasksOutputMap.insert({ DataSpecUtils::label(taskOutput), taskOutput }); + if (!externalTaskSpec.detectorName.empty()) { + externalTaskDetectorNames[DataSpecUtils::label(taskOutput)] = externalTaskSpec.detectorName; + } } } @@ -711,8 +715,11 @@ void InfrastructureGenerator::generateCheckRunners(framework::WorkflowSpec& work ILOG(Debug, Devel) << input << " "; ILOG(Debug, Devel) << ENDM; + auto detectorName = externalTaskDetectorNames.find(inputNames[0]); DataProcessorSpec spec = checkConfigs.empty() - ? CheckRunnerFactory::createSinkDevice(checkRunnerConfig, tasksOutputMap.find(inputNames[0])->second) + ? CheckRunnerFactory::createSinkDevice(checkRunnerConfig, + tasksOutputMap.find(inputNames[0])->second, + detectorName == externalTaskDetectorNames.end() ? std::string{} : detectorName->second) : CheckRunnerFactory::create(checkRunnerConfig, checkConfigs, storeVectorMap[inputNames]); workflow.emplace_back(spec); checkRunnerOutputs.insert(checkRunnerOutputs.end(), spec.outputs.begin(), spec.outputs.end()); diff --git a/Framework/src/InfrastructureSpecReader.cxx b/Framework/src/InfrastructureSpecReader.cxx index 2ad8e6593..7b36cae25 100644 --- a/Framework/src/InfrastructureSpecReader.cxx +++ b/Framework/src/InfrastructureSpecReader.cxx @@ -332,6 +332,10 @@ DataSourceSpec InfrastructureSpecReader::readSpecEntry(const std dss.id = dataSourceTree.get("name"); dss.name = dss.id; auto query = wholeTree.get("qc.externalTasks." + dss.name + ".query"); + dss.detectorName = wholeTree.get("qc.externalTasks." + dss.name + ".detectorName", ""); + if (!dss.detectorName.empty()) { + dss.detectorName = InfrastructureSpecReader::validateDetectorName(dss.detectorName); + } dss.inputs = o2::framework::DataDescriptorQueryBuilder::parse(query.c_str()); break; } @@ -478,6 +482,12 @@ ExternalTaskSpec ets.taskName = externalTaskName; ets.query = externalTaskTree.get("query"); + ets.detectorName = externalTaskTree.get("detectorName", ""); + if (ets.detectorName.empty()) { + ILOG(Warning, Ops) << "External task " << externalTaskName << " has no detectorName configured. The detector will be inferred from the query origin when wrapping raw objects." << ENDM; + } else { + ets.detectorName = InfrastructureSpecReader::validateDetectorName(ets.detectorName); + } ets.active = externalTaskTree.get("active", ets.active); return ets; diff --git a/doc/Configuration.md b/doc/Configuration.md index 783ed3564..d5081ee98 100644 --- a/doc/Configuration.md +++ b/doc/Configuration.md @@ -404,6 +404,7 @@ Below the external task configuration structure is described. Note that more tha "External-1": { "": "ID of the task", "active": "true", "": "Activation flag. If not \"true\", the Task will not be created.", "taskName": "External-1", "": "Name of the task, used e.g. in the QCDB. If empty, the ID is used.", + "detectorName": "TST", "": "Detector name used when wrapping raw objects as MonitorObjects. If omitted, the query origin is used.", "query": "External-1:TST/HISTO/0", "": "Query specifying where the objects to be checked and stored are coming from. Use the task name as binding." } }