diff --git a/Detectors/DCS/CMakeLists.txt b/Detectors/DCS/CMakeLists.txt index 1d037f297f6da..5c03ffb6ff1a1 100644 --- a/Detectors/DCS/CMakeLists.txt +++ b/Detectors/DCS/CMakeLists.txt @@ -8,35 +8,64 @@ # granted to it by virtue of its status as an Intergovernmental Organization or # submit itself to any jurisdiction. -o2_add_library(DetectorsDCS - TARGETVARNAME targetName - SOURCES src/Clock.cxx - src/DataPointCompositeObject.cxx - src/DataPointIdentifier.cxx - src/DataPointValue.cxx - src/DeliveryType.cxx - src/GenericFunctions.cxx - src/StringUtils.cxx - src/DCSProcessor.cxx - PUBLIC_LINK_LIBRARIES O2::Headers - O2::CommonUtils - O2::CCDB - O2::DetectorsCalibration - ms_gsl::ms_gsl) +o2_add_library( + DetectorsDCS + TARGETVARNAME targetName + SOURCES src/AliasExpander.cxx + src/DCSProcessor.cxx + src/DataPointCompositeObject.cxx + src/DataPointCreator.cxx + src/DataPointGenerator.cxx + src/DataPointIdentifier.cxx + src/DataPointValue.cxx + src/DeliveryType.cxx + src/GenericFunctions.cxx + src/StringUtils.cxx + src/Clock.cxx + PUBLIC_LINK_LIBRARIES O2::Headers O2::CommonUtils O2::CCDB + O2::DetectorsCalibration ms_gsl::ms_gsl) -o2_target_root_dictionary(DetectorsDCS - HEADERS include/DetectorsDCS/DataPointCompositeObject.h - include/DetectorsDCS/DataPointIdentifier.h - include/DetectorsDCS/DataPointValue.h - include/DetectorsDCS/DCSProcessor.h) +o2_target_root_dictionary( + DetectorsDCS + HEADERS include/DetectorsDCS/DataPointCompositeObject.h + include/DetectorsDCS/DataPointIdentifier.h + include/DetectorsDCS/DataPointValue.h + include/DetectorsDCS/DCSProcessor.h) -o2_add_executable(dcs-data-workflow - COMPONENT_NAME dcs - SOURCES testWorkflow/dcs-data-workflow.cxx - PUBLIC_LINK_LIBRARIES O2::Framework - O2::DetectorsDCS) +o2_add_executable( + data-workflow + COMPONENT_NAME dcs + SOURCES testWorkflow/dcs-data-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) +o2_add_executable( + random-data-workflow + COMPONENT_NAME dcs + SOURCES testWorkflow/dcs-random-data-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) +endif() + +if(BUILD_TESTING) + o2_add_test( + data-point-types + SOURCES test/testDataPointTypes.cxx + COMPONENT_NAME dcs + LABELS "dcs" + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsDCS) + o2_add_test( + alias-expander + SOURCES test/testAliasExpander.cxx + COMPONENT_NAME dcs + LABELS "dcs" + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsDCS) + o2_add_test( + data-point-generator + SOURCES test/testDataPointGenerator.cxx + COMPONENT_NAME dcs + LABELS "dcs" + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsDCS) endif() diff --git a/Detectors/DCS/README.md b/Detectors/DCS/README.md new file mode 100644 index 0000000000000..13cf1eebe53b2 --- /dev/null +++ b/Detectors/DCS/README.md @@ -0,0 +1,20 @@ + + +# Export of DCS to CCDB + +To be written + +# Generating DCS aliases + +For test purposes, DCS aliases can be generated making use of the helper +function `generateRandomDataPoints`. For example : + +```c++ +#include "DetectorsDCS/DataPointGenerator.h" +std::vector patterns = { "DET/HV/Crate[0.9]/Channel[00.42]/vMon" }; +auto dps = o2::dcs::generateRandomDataPoints(patterns,0.0,1200.0); +``` + +would generate 420 data points. diff --git a/Detectors/DCS/include/DetectorsDCS/AliasExpander.h b/Detectors/DCS/include/DetectorsDCS/AliasExpander.h new file mode 100644 index 0000000000000..ac2ea2486e92b --- /dev/null +++ b/Detectors/DCS/include/DetectorsDCS/AliasExpander.h @@ -0,0 +1,69 @@ +// 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_DCS_ALIAS_EXPANDER_H +#define O2_DCS_ALIAS_EXPANDER_H + +#include +#include + +namespace o2::dcs +{ +/** + * expandAlias converts a single pattern into a list of strings. + * + * @param pattern a pattern is made of a number of "XX[YY]" blocks (at least one) + * + * where : + * - XX is any text + * - YY describes either a integral range or a textual list + * + * An integral range is [a..b] where the formatting of the biggest of the + * two integers a and b dictates, by default, the formatting of the output + * alias. For instance [0..3] is expanded to the set 0,1,2,3 while [00..03] + * is expanded to 00,01,02,03. If you want more control on the formatting, + * you can use a python/fmt format {} e.g. [0..15{:d}] would yields 0,1, + * 2,...,14,15 simply (no 0 filling). + * + * A textual list is simply a list of values separated by commas, + * e.g. "vMon,iMon" + * + * @returns a vector of strings containing all the possible expansions of + * the pattern. That vector is not guaranteed to be sorted. + * + * For example, pattern=DET[A,B]/Channel[000,002]/[iMon,vMon] yields : + * + * - DETA/Channel000/iMon + * - DETA/Channel001/iMon + * - DETA/Channel002/iMon + * - DETA/Channel000/vMon + * - DETA/Channel001/vMon + * - DETA/Channel002/vMon + * - DETB/Channel000/iMon + * - DETB/Channel001/iMon + * - DETB/Channel002/iMon + * - DETB/Channel000/vMon + * - DETB/Channel001/vMon + * - DETB/Channel002/vMon + +*/ +std::vector expandAlias(const std::string& pattern); + +/** expandAliases converts a list of patterns into a list of strings. + * + * each input pattern is treated by expandAlias() + * + * @returns a _sorted_ vector of strings containing all the possible + * expansions of the pattern. + */ +std::vector expandAliases(const std::vector& patternedAliases); +} // namespace o2::dcs + +#endif diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h b/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h index fea0bab7dfca0..c2b5f848bb858 100644 --- a/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h +++ b/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h @@ -254,8 +254,29 @@ struct alignas(128) DataPointCompositeObject final { (char*)&dpcom.data.payload_pt1, 56); } } + + /** + * The destructor for DataPointCompositeObject so it is not deleted + * and thus DataPointCompositeObject is trivially copyable + */ + ~DataPointCompositeObject() noexcept = default; ClassDefNV(DataPointCompositeObject, 1); }; + +/** + * Return the value contained in the DataPointCompositeObject, if possible. + * + * @tparam T the expected type of the value + * + * @param dpcom the DataPointCompositeObject the value is extracted from + * + * @returns the value of the data point + * + * @throws if the DeliveryType of the data point is not compatible with T + */ +template +T getValue(const DataPointCompositeObject& dpcom); + } // namespace dcs } // namespace o2 diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointCreator.h b/Detectors/DCS/include/DetectorsDCS/DataPointCreator.h new file mode 100644 index 0000000000000..4c63e96108d15 --- /dev/null +++ b/Detectors/DCS/include/DetectorsDCS/DataPointCreator.h @@ -0,0 +1,47 @@ +// 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_DCS_DATAPOINT_CREATOR_H +#define O2_DCS_DATAPOINT_CREATOR_H + +#include "DataPointCompositeObject.h" + +namespace o2::dcs +{ +/** + * createDataPointCompositeObject is a convenience function to + * simplify the creation of a DataPointCompositeObject. + * + * @param alias the DataPoint alias name (max 56 characters) + * @param val the value of the datapoint + * @param flags value for ADAPOS flags. + * @param milliseconds value for milliseconds. + * @param seconds value for seconds. + * + * @returns a DataPointCompositeObject + * + * The actual DeliveryType of the returned + * DataPointCompositeObject is deduced from the type of val. + * + * Note that only a few relevant specialization are actually provided + * + * - T=int32_t : DeliveryType = RAW_INT + * - T=uint32_t : DeliveryType = RAW_UINT + * - T=double : DeliveryType = RAW_DOUBLE + * - T=bool : DeliveryType = RAW_BOOL + * - T=char : DeliveryType = RAW_CHAR + * - T=std::string : DeliveryType = RAW_STRING + * + */ +template +o2::dcs::DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, T val, uint32_t seconds, uint16_t msec, uint16_t flags = 0); +} // namespace o2::dcs + +#endif diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointGenerator.h b/Detectors/DCS/include/DetectorsDCS/DataPointGenerator.h new file mode 100644 index 0000000000000..5b3bcc180dcb2 --- /dev/null +++ b/Detectors/DCS/include/DetectorsDCS/DataPointGenerator.h @@ -0,0 +1,43 @@ +// 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_DCS_DATAPOINT_GENERATOR_H +#define O2_DCS_DATAPOINT_GENERATOR_H + +#include "DetectorsDCS/DeliveryType.h" +#include "DetectorsDCS/DataPointCompositeObject.h" +#include + +namespace o2::dcs +{ +/** +* Generate random data points, uniformly distributed between two values. +* +* @tparam T the type of value of the data points to be generated. Only +* a few types are supported : double, uint32_t, int32_t, char, bool +* +* @param aliases the list of aliases to be generated. Those can use +* patterns that will be expanded, @see AliasExpander +* @param minValue the minimum value of the values to be generated +* @param maxValue the maximum value of the values to be generated +* @param refDate the date to be associated with all data points +* in `%Y-%b-%d %H:%M:%S` format. If refDate="" the current date is used. +* +* @returns a vector of DataPointCompositeObject objects +*/ +template +std::vector generateRandomDataPoints(const std::vector& aliases, + T min, + T max, + std::string refDate = ""); + +} // namespace o2::dcs + +#endif diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointValue.h b/Detectors/DCS/include/DetectorsDCS/DataPointValue.h index c32c4e04128b8..6bf7f490d3fac 100644 --- a/Detectors/DCS/include/DetectorsDCS/DataPointValue.h +++ b/Detectors/DCS/include/DetectorsDCS/DataPointValue.h @@ -459,7 +459,7 @@ struct alignas(64) DataPointValue final { */ inline std::unique_ptr get_timestamp() const noexcept { -#ifdef __linux__ +#if defined(__linux__) || defined(__APPLE__) // time_t should be uint64_t (compatible) on 64-bit Linux platforms: char buffer[17]; std::time_t ts((uint64_t)sec); diff --git a/Detectors/DCS/src/AliasExpander.cxx b/Detectors/DCS/src/AliasExpander.cxx new file mode 100644 index 0000000000000..e2f7448ad5696 --- /dev/null +++ b/Detectors/DCS/src/AliasExpander.cxx @@ -0,0 +1,145 @@ +// 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/AliasExpander.h" +#include +#include + +namespace +{ + +std::vector splitString(const std::string& src, char delim) +{ + std::stringstream ss(src); + std::string token; + std::vector tokens; + + while (std::getline(ss, token, delim)) { + if (!token.empty()) { + tokens.push_back(std::move(token)); + } + } + + return tokens; +} + +std::vector extractList(const std::string& slist) +{ + auto dots = slist.find(","); + if (dots == std::string::npos) { + return {}; + } + return splitString(slist, ','); +} + +std::vector extractRange(std::string range) +{ + auto dots = range.find(".."); + if (dots == std::string::npos) { + return extractList(range); + } + + auto braceStart = range.find("{"); + auto braceEnd = range.find("}"); + + if ( + (braceStart != std::string::npos && + braceEnd == std::string::npos) || + (braceStart == std::string::npos && + braceEnd != std::string::npos)) { + // incomplete custom pattern + return {}; + } + + std::string intFormat; + std::string sa, sb; + + if (braceStart != std::string::npos && + braceEnd != std::string::npos) { + intFormat = range.substr(braceStart, braceEnd - braceStart + 1); + range.erase(braceStart, braceEnd); + dots = range.find(".."); + sa = range.substr(0, dots); + sb = range.substr(dots + 2); + } else { + sa = range.substr(0, dots); + sb = range.substr(dots + 2); + auto size = std::max(sa.size(), sb.size()); + intFormat = "{:" + fmt::format("0{}d", size) + "}"; + } + + auto a = std::stoi(sa); + auto b = std::stoi(sb); + std::vector result; + + for (auto i = a; i <= b; i++) { + auto substituted = fmt::format(intFormat, i); + result.push_back(substituted); + } + return result; +} +} // namespace + +namespace o2::dcs +{ +std::vector expandAlias(const std::string& pattern) +{ + auto leftBracket = pattern.find("["); + auto rightBracket = pattern.find("]"); + + // no bracket at all -> return pattern simply + if (leftBracket == std::string::npos && rightBracket == std::string::npos) { + return {pattern}; + } + + // no matching bracket -> wrong pattern -> return nothing + if ((leftBracket == std::string::npos && + rightBracket != std::string::npos) || + (leftBracket != std::string::npos && + rightBracket == std::string::npos)) { + return {}; + } + auto rangeStr = pattern.substr(leftBracket + 1, rightBracket - leftBracket - 1); + + auto range = extractRange(rangeStr); + + // incorrect range -> return nothing + if (range.empty()) { + return {}; + } + + auto newPattern = pattern.substr(0, leftBracket) + + "{:s}" + + pattern.substr(rightBracket + 1); + + std::vector result; + + for (auto r : range) { + auto substituted = fmt::format(newPattern, r); + result.emplace_back(substituted); + } + + return o2::dcs::expandAliases(result); +} + +std::vector expandAliases(const std::vector& patternedAliases) +{ + std::vector result; + + for (auto a : patternedAliases) { + auto e = expandAlias(a); + result.insert(result.end(), e.begin(), e.end()); + } + // sort to get a predictable result + std::sort(result.begin(), result.end()); + + return result; +} +} // namespace o2::dcs diff --git a/Detectors/DCS/src/DataPointCompositeObject.cxx b/Detectors/DCS/src/DataPointCompositeObject.cxx index 93aaa674bb2ef..2ec290215f0ad 100644 --- a/Detectors/DCS/src/DataPointCompositeObject.cxx +++ b/Detectors/DCS/src/DataPointCompositeObject.cxx @@ -13,3 +13,71 @@ using namespace o2::dcs; ClassImp(DataPointCompositeObject); + +namespace o2::dcs +{ +template +T getValueImpl(const DataPointCompositeObject& dpcom) +{ + union Converter { + uint64_t raw_data; + T t_value; + }; + if (dpcom.id.get_type() != dt) { + throw std::runtime_error("DPCOM is of unexpected type " + o2::dcs::show(dt)); + } + Converter converter; + converter.raw_data = dpcom.data.payload_pt1; + return converter.t_value; +} + +// only specialize the getValue function for the types we support : +// +// - double +// - uint32_t +// - int32_t +// - char +// - bool +// +// - string + +template <> +double getValue(const DataPointCompositeObject& dpcom) +{ + return getValueImpl(dpcom); +} + +template <> +uint32_t getValue(const DataPointCompositeObject& dpcom) +{ + return getValueImpl(dpcom); +} + +template <> +int32_t getValue(const DataPointCompositeObject& dpcom) +{ + return getValueImpl(dpcom); +} + +template <> +char getValue(const DataPointCompositeObject& dpcom) +{ + return getValueImpl(dpcom); +} + +template <> +bool getValue(const DataPointCompositeObject& dpcom) +{ + return getValueImpl(dpcom); +} + +template <> +std::string getValue(const DataPointCompositeObject& dpcom) +{ + if (dpcom.id.get_type() != o2::dcs::DeliveryType::RAW_STRING) { + throw std::runtime_error("DPCOM is of unexpected type " + o2::dcs::show(dpcom.id.get_type())); + } + return std::string((char*)&dpcom.data.payload_pt1); +} + +} // namespace o2::dcs diff --git a/Detectors/DCS/src/DataPointCreator.cxx b/Detectors/DCS/src/DataPointCreator.cxx new file mode 100644 index 0000000000000..6b11d76a7675e --- /dev/null +++ b/Detectors/DCS/src/DataPointCreator.cxx @@ -0,0 +1,66 @@ +// 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/DataPointCreator.h" + +namespace +{ +o2::dcs::DataPointCompositeObject createDPCOM(const std::string& alias, const uint64_t* val, uint32_t seconds, uint16_t msec, uint16_t flags, o2::dcs::DeliveryType dt) +{ + auto dpid = o2::dcs::DataPointIdentifier(alias, dt); + auto dpval = o2::dcs::DataPointValue( + flags, + msec, + seconds, + val, + dt); + return o2::dcs::DataPointCompositeObject(dpid, dpval); +} +} // namespace + +namespace o2::dcs +{ +template <> +DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, double val, uint32_t seconds, uint16_t msec, uint16_t flags) +{ + return createDPCOM(alias, reinterpret_cast(&val), seconds, msec, flags, DeliveryType::RAW_DOUBLE); +} + +template <> +DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, int32_t val, uint32_t seconds, uint16_t msec, uint16_t flags) +{ + return createDPCOM(alias, reinterpret_cast(&val), seconds, msec, flags, DeliveryType::RAW_INT); +} + +template <> +DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, uint32_t val, uint32_t seconds, uint16_t msec, uint16_t flags) +{ + return createDPCOM(alias, reinterpret_cast(&val), seconds, msec, flags, DeliveryType::RAW_UINT); +} + +template <> +DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, char val, uint32_t seconds, uint16_t msec, uint16_t flags) +{ + return createDPCOM(alias, reinterpret_cast(&val), seconds, msec, flags, DeliveryType::RAW_CHAR); +} + +template <> +DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, bool val, uint32_t seconds, uint16_t msec, uint16_t flags) +{ + return createDPCOM(alias, reinterpret_cast(&val), seconds, msec, flags, DeliveryType::RAW_BOOL); +} + +template <> +DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, std::string val, uint32_t seconds, uint16_t msec, uint16_t flags) +{ + return createDPCOM(alias, reinterpret_cast(val.c_str()), seconds, msec, flags, DeliveryType::RAW_STRING); +} + +} // namespace o2::dcs diff --git a/Detectors/DCS/src/DataPointGenerator.cxx b/Detectors/DCS/src/DataPointGenerator.cxx new file mode 100644 index 0000000000000..062d996452798 --- /dev/null +++ b/Detectors/DCS/src/DataPointGenerator.cxx @@ -0,0 +1,124 @@ +// 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/AliasExpander.h" +#include "DetectorsDCS/DataPointGenerator.h" +#include "DetectorsDCS/DataPointCreator.h" +#include "DetectorsDCS/DataPointCompositeObject.h" +#include "DetectorsDCS/StringUtils.h" +#include +#include +#include +#include +#include + +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); + } else { + std::tm t{}; + std::istringstream ss(refDate); + ss >> std::get_time(&t, "%Y-%b-%d %H:%M:%S"); + seconds = mktime(&t); + } + uint16_t msec = 5; + return std::make_pair(seconds, msec); +} + +} // namespace + +namespace o2::dcs +{ + +//std::enable_if_t::value, bool> = true> + +template +std::vector + generateRandomDataPoints(const std::vector& aliases, + T minValue, T maxValue, std::string refDate) +{ + std::vector dpcoms; + static_assert(std::is_arithmetic::value, "T must be an arithmetic type"); + typedef typename std::conditional::value, + std::uniform_int_distribution, + std::uniform_real_distribution>::type distType; + + std::random_device rd; + std::mt19937 mt(rd()); + distType dist{minValue, maxValue}; + auto [seconds, msec] = getDate(refDate); + for (auto alias : expandAliases(aliases)) { + auto value = dist(mt); + dpcoms.emplace_back(o2::dcs::createDataPointCompositeObject(alias, value, seconds, msec)); + } + return dpcoms; +} + +// only specialize the functions for the types we support : +// +// - double +// - uint32_t +// - int32_t +// - char +// - bool +// +// - std::string + +template std::vector generateRandomDataPoints(const std::vector& aliases, double minValue, double maxValue, std::string); + +template std::vector generateRandomDataPoints(const std::vector& aliases, uint32_t minValue, uint32_t maxValue, std::string); + +template std::vector generateRandomDataPoints(const std::vector& aliases, int32_t minValue, int32_t maxValue, std::string); + +template std::vector generateRandomDataPoints(const std::vector& aliases, char minValue, char maxValue, std::string); + +/** Need a specific specialization for bool as got into trouble compiling uniform_int_distribution + * on some platform (e.g. CC7). + */ +template <> +std::vector generateRandomDataPoints(const std::vector& aliases, bool minValue, bool maxValue, std::string refDate) +{ + std::vector dpcoms; + std::random_device rd; + std::mt19937 mt(rd()); + std::uniform_int_distribution dist{0, 1}; + auto [seconds, msec] = getDate(refDate); + for (auto alias : expandAliases(aliases)) { + bool value = dist(mt); + dpcoms.emplace_back(o2::dcs::createDataPointCompositeObject(alias, value, seconds, msec)); + } + return dpcoms; +} + +/** + * Generate data points of type string, where each string is random, with + * a length between the length of the two input strings (minLength,maxLength) + */ +template <> +std::vector generateRandomDataPoints(const std::vector& aliases, std::string minLength, std::string maxLength, std::string refDate) +{ + std::vector dpcoms; + std::random_device rd; + std::mt19937 mt(rd()); + std::uniform_int_distribution dist{minLength.size(), maxLength.size()}; + auto [seconds, msec] = getDate(refDate); + for (auto alias : expandAliases(aliases)) { + auto value = o2::dcs::random_string2(dist(mt)); + dpcoms.emplace_back(o2::dcs::createDataPointCompositeObject(alias, value, seconds, msec)); + } + return dpcoms; +} +} // namespace o2::dcs diff --git a/Detectors/DCS/src/DetectorsDCSLinkDef.h b/Detectors/DCS/src/DetectorsDCSLinkDef.h index 5f53e7757a349..da9a282797cba 100644 --- a/Detectors/DCS/src/DetectorsDCSLinkDef.h +++ b/Detectors/DCS/src/DetectorsDCSLinkDef.h @@ -19,5 +19,7 @@ #pragma link C++ struct o2::dcs::DataPointValue + ; #pragma link C++ class o2::dcs::DCSProcessor + ; #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&); #endif diff --git a/Detectors/DCS/test/testAliasExpander.cxx b/Detectors/DCS/test/testAliasExpander.cxx new file mode 100644 index 0000000000000..549a7a69cda65 --- /dev/null +++ b/Detectors/DCS/test/testAliasExpander.cxx @@ -0,0 +1,127 @@ +// 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. + +#define BOOST_TEST_MODULE Test DCS AliasExpander +#define BOOST_TEST_MAIN + +#define BOOST_TEST_DYN_LINK + +#include +#include +#include +#include "DetectorsDCS/AliasExpander.h" + +BOOST_AUTO_TEST_CASE(ExpandAliasesIsNoopWhenNoPatternGiven) +{ + std::vector aliases = o2::dcs::expandAliases({"ab"}); + + std::vector expected = {"ab"}; + + BOOST_TEST(aliases == expected, boost::test_tools::per_element()); +} + +BOOST_AUTO_TEST_CASE(ExpandAliasesReturnsEmptyVectorWhenPatternIsIncorrect) +{ + std::vector aliases = o2::dcs::expandAliases({"ab[c"}); + + std::vector expected = {}; + + BOOST_TEST(aliases == expected, boost::test_tools::per_element()); + + aliases = o2::dcs::expandAliases({"ab]c"}); + + BOOST_TEST(aliases == expected, boost::test_tools::per_element()); + + aliases = o2::dcs::expandAliases({"ab[1.2]c"}); + + BOOST_TEST(aliases == expected, boost::test_tools::per_element()); +} + +BOOST_AUTO_TEST_CASE(ExpandAliasesWithIntegerRange) +{ + std::vector aliases = o2::dcs::expandAliases({"a[1..2]bcde[99..101]toto"}); + + std::vector expected = { + "a1bcde099toto", + "a1bcde100toto", + "a1bcde101toto", + "a2bcde099toto", + "a2bcde100toto", + "a2bcde101toto"}; + + BOOST_TEST(aliases == expected, boost::test_tools::per_element()); +} + +BOOST_AUTO_TEST_CASE(ExpandAliasesWithIntegerRangeWithCustomFormat) +{ + std::vector aliases = o2::dcs::expandAliases({"a[1..3{:03d}]"}); + + std::vector expected = { + "a001", + "a002", + "a003"}; + + BOOST_TEST(aliases == expected, boost::test_tools::per_element()); +} + +BOOST_AUTO_TEST_CASE(ExpandAliasesWithIntegerRangeWithCustomFormatBis) +{ + std::vector aliases = o2::dcs::expandAliases({"a[1..3{:d}]"}); + + std::vector expected = { + "a1", + "a2", + "a3"}; + + BOOST_TEST(aliases == expected, boost::test_tools::per_element()); +} + +BOOST_AUTO_TEST_CASE(ExpandAliasesWithStringList) +{ + std::vector aliases = o2::dcs::expandAliases({"a[1..2]bcde[99..101][toto,titi,tata]"}); + + std::vector expected = { + "a1bcde099tata", + "a1bcde099titi", + "a1bcde099toto", + "a1bcde100tata", + "a1bcde100titi", + "a1bcde100toto", + "a1bcde101tata", + "a1bcde101titi", + "a1bcde101toto", + "a2bcde099tata", + "a2bcde099titi", + "a2bcde099toto", + "a2bcde100tata", + "a2bcde100titi", + "a2bcde100toto", + "a2bcde101tata", + "a2bcde101titi", + "a2bcde101toto", + }; + + BOOST_TEST(aliases == expected, boost::test_tools::per_element()); +} + +BOOST_AUTO_TEST_CASE(ExpandMch) +{ + std::vector aliases = o2::dcs::expandAliases( + {"MchHvLvLeft/Chamber[00..03]Left/Quad1Sect[0..2].actual.[vMon,iMon]", + "MchHvLvLeft/Chamber[00..03]Left/Quad2Sect[0..2].actual.[vMon,iMon]", + "MchHvLvLeft/Chamber[04..09]Left/Slat[00..08].actual.[vMon,iMon]", + "MchHvLvLeft/Chamber[06..09]Left/Slat[09..12].actual.[vMon,iMon]", + "MchHvLvRight/Chamber[00..03]Right/Quad0Sect[0..2].actual.[vMon,iMon]", + "MchHvLvRight/Chamber[00..03]Right/Quad3Sect[0..2].actual.[vMon,iMon]", + "MchHvLvRight/Chamber[04..09]Right/Slat[00..08].actual.[vMon,iMon]", + "MchHvLvRight/Chamber[06..09]Right/Slat[09..12].actual.[vMon,iMon]"}); + + BOOST_TEST(aliases.size(), 376); +} diff --git a/Detectors/DCS/test/testDataPointGenerator.cxx b/Detectors/DCS/test/testDataPointGenerator.cxx new file mode 100644 index 0000000000000..367e52dc14558 --- /dev/null +++ b/Detectors/DCS/test/testDataPointGenerator.cxx @@ -0,0 +1,81 @@ +// 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. + +#define BOOST_TEST_MODULE Test DCS DataPointGenerator +#define BOOST_TEST_MAIN + +#define BOOST_TEST_DYN_LINK + +#include +#include +#include +#include "DetectorsDCS/DataPointGenerator.h" +#include + +BOOST_AUTO_TEST_CASE(GenerateDouble) +{ + double fmin = 1620.0; + double fmax = 1710.5; + auto fbi = o2::dcs::generateRandomDataPoints({"TST/SECTOR[00..06]/CRATE[0..3]/voltage"}, fmin, fmax, "2022-November-18 12:34:56"); + + BOOST_CHECK_EQUAL(fbi.size(), 28); + + for (auto dp : fbi) { + BOOST_CHECK_EQUAL(dp.id.get_type(), o2::dcs::DeliveryType::RAW_DOUBLE); + double value = o2::dcs::getValue(dp); + BOOST_CHECK(value >= fmin && value <= fmax); + } +} + +BOOST_AUTO_TEST_CASE(GenerateInt) +{ + uint32_t imin = 0; + uint32_t imax = 3; + auto fbi = o2::dcs::generateRandomDataPoints({"TST/SECTOR[00..06]/CRATE[0..3]/current"}, imin, imax, "2022-November-18 12:34:56"); + + BOOST_CHECK_EQUAL(fbi.size(), 28); + + for (auto dp : fbi) { + BOOST_CHECK_EQUAL(dp.id.get_type(), o2::dcs::DeliveryType::RAW_UINT); + double value = o2::dcs::getValue(dp); + BOOST_CHECK(value >= imin && value <= imax); + BOOST_CHECK_THROW(o2::dcs::getValue(dp), std::runtime_error); + BOOST_CHECK_THROW(o2::dcs::getValue(dp), std::runtime_error); + } +} + +BOOST_AUTO_TEST_CASE(GenerateBool) +{ + auto fbi = o2::dcs::generateRandomDataPoints({"TST/SECTOR[00..06]/status"}, 0, 1, "2022-November-18 12:34:56"); + + BOOST_CHECK_EQUAL(fbi.size(), 7); + + for (auto dp : fbi) { + BOOST_CHECK_EQUAL(dp.id.get_type(), o2::dcs::DeliveryType::RAW_BOOL); + BOOST_CHECK_NO_THROW(o2::dcs::getValue(dp)); + BOOST_CHECK_THROW(o2::dcs::getValue(dp), std::runtime_error); + } +} + +BOOST_AUTO_TEST_CASE(GenerateString) +{ + auto fbi = o2::dcs::generateRandomDataPoints({"TST/SECTOR[00..06]/name"}, "123", "1234567", "2022-November-18 12:34:56"); + + BOOST_CHECK_EQUAL(fbi.size(), 7); + + for (auto dp : fbi) { + BOOST_CHECK_EQUAL(dp.id.get_type(), o2::dcs::DeliveryType::RAW_STRING); + BOOST_CHECK_NO_THROW(o2::dcs::getValue(dp)); + BOOST_CHECK_THROW(o2::dcs::getValue(dp), std::runtime_error); + auto value = o2::dcs::getValue(dp); + BOOST_CHECK(value.size() >= 3); + BOOST_CHECK(value.size() <= 7); + } +} diff --git a/Detectors/DCS/test/testDataPointTypes.cxx b/Detectors/DCS/test/testDataPointTypes.cxx new file mode 100644 index 0000000000000..91c1db1def7c2 --- /dev/null +++ b/Detectors/DCS/test/testDataPointTypes.cxx @@ -0,0 +1,39 @@ +// 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 +#define BOOST_TEST_MODULE Test DetectorsDCS DataPoints +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include +#include "DetectorsDCS/DataPointCompositeObject.h" +#include "Framework/TypeTraits.h" +#include +#include +#include +#include + +typedef boost::mpl::list testTypes; + +BOOST_AUTO_TEST_CASE_TEMPLATE(DataPointCompositeObjectTypeTraits, T, testTypes) +{ + BOOST_CHECK_EQUAL(std::is_trivially_copyable::value, true); + BOOST_CHECK_EQUAL(std::is_polymorphic::value, false); + BOOST_CHECK_EQUAL(std::is_pointer::value, false); + BOOST_CHECK_EQUAL(o2::framework::is_forced_non_messageable::value, false); +} + +BOOST_AUTO_TEST_CASE(DataPointsAreMessageable) +{ + BOOST_CHECK_EQUAL(o2::framework::is_messageable::value, true); + BOOST_CHECK_EQUAL(o2::framework::is_messageable::value, true); + BOOST_CHECK_EQUAL(o2::framework::is_messageable::value, true); +} diff --git a/Detectors/DCS/testWorkflow/DCSRandomDataGeneratorSpec.h b/Detectors/DCS/testWorkflow/DCSRandomDataGeneratorSpec.h new file mode 100644 index 0000000000000..9874df180c2dc --- /dev/null +++ b/Detectors/DCS/testWorkflow/DCSRandomDataGeneratorSpec.h @@ -0,0 +1,170 @@ +// 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_DCS_RANDOM_DATA_GENERATOR_SPEC_H +#define O2_DCS_RANDOM_DATA_GENERATOR_SPEC_H + +#include "DetectorsDCS/DataPointCompositeObject.h" +#include "DetectorsDCS/DataPointGenerator.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/ControlService.h" +#include "Framework/DeviceSpec.h" +#include "Framework/Logger.h" +#include "Framework/Task.h" +#include +#include +#include + +using namespace o2::framework; + +namespace +{ +/* + * A compact representation a group of alias to be generated + */ +template +struct DataPointHint { + std::string aliasPattern; // alias pattern e.g. DET/HV/Crate[0..2]/Channel[000..012]/vMon + T minValue; // minimum value to generate + T maxValue; // maximum value to generate +}; + +using HintType = std::variant, + DataPointHint, + DataPointHint, + DataPointHint, + DataPointHint, + DataPointHint>; + +/** generate random integers uniformly distributed within a range. + * + * @param size the number of integers to generate + * @param min the minimum value to be generated + * @param max the maximum value to be generated + * + * @returns a vector of integers + */ +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); }); + return data; +} + +/** generate DCS data points. + * + * @param hints vector of HintType describing what to generate + * @param fraction fraction of the generated aliases that are returned (1.0 by default) + * + * @returns a vector of DataPointCompositeObjects + */ +std::vector generate(const std::vector hints, + float fraction = 1.0) +{ + std::vector dataPoints; + + auto GenerateVisitor = [](const auto& t) { + return o2::dcs::generateRandomDataPoints({t.aliasPattern}, t.minValue, t.maxValue); + }; + + for (const auto& hint : hints) { + auto dpcoms = std::visit(GenerateVisitor, hint); + for (auto dp : dpcoms) { + dataPoints.push_back(dp); + } + } + if (fraction < 1.0) { + auto indices = generateIntegers(fraction * dataPoints.size(), 0, dataPoints.size() - 1); + auto tmp = dataPoints; + dataPoints.clear(); + for (auto i : indices) { + dataPoints.push_back(tmp[i]); + } + } + return dataPoints; +} + +/** + * DCSRandomDataGenerator is an example device that generates random + * DCS Data Points. + * + * The actual description of what is generated is hard-coded in + * the init() method. + */ +class DCSRandomDataGenerator : public o2::framework::Task +{ + using DPID = o2::dcs::DataPointIdentifier; + using DPVAL = o2::dcs::DataPointValue; + using DPCOM = o2::dcs::DataPointCompositeObject; + + public: + void init(o2::framework::InitContext& ic) final + { + mMaxTF = ic.options().get("max-timeframes"); + mDeltaFraction = ic.options().get("delta-fraction"); + 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'}); + 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"}); + } + + void run(o2::framework::ProcessingContext& pc) final + { + auto input = pc.inputs().begin(); + uint64_t tfid = o2::header::get((*input).header)->startTime; + if (tfid >= mMaxTF) { + LOG(INFO) << "Data generator reached TF " << tfid << ", stopping"; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(o2::framework::QuitRequest::Me); + } + + bool generateFBI = (mTFs % mMaxCyclesNoFullMap == 0); + // fraction is one if we generate FBI (Full Buffer Image) + float fraction = (generateFBI ? 1.0 : mDeltaFraction); + + auto dpcoms = generate(mDataPointHints, fraction); + + // 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); + mTFs++; + } + + private: + uint64_t mMaxTF; + uint64_t mTFs = 0; + uint64_t mMaxCyclesNoFullMap; + float mDeltaFraction; + std::vector mDataPointHints; +}; + +} // namespace + +DataProcessorSpec getDCSRandomDataGeneratorSpec() +{ + return DataProcessorSpec{ + "dcs-random-data-generator", + Inputs{}, + Outputs{{{"outputDCS"}, "DCS", "DATAPOINTS"}, {{"outputDCSdelta"}, "DCS", "DATAPOINTSdelta"}}, + AlgorithmSpec{adaptFromTask()}, + Options{ + {"max-timeframes", VariantType::Int64, 99999999999ll, {"max TimeFrames to generate"}}, + {"delta-fraction", VariantType::Float, 0.05f, {"fraction of data points to put in the delta"}}, + {"max-cycles-no-full-map", VariantType::Int64, 6000ll, {"max num of cycles between the sending of 2 full maps"}}}}; +} + +#endif diff --git a/Detectors/DCS/testWorkflow/README.md b/Detectors/DCS/testWorkflow/README.md new file mode 100644 index 0000000000000..2c4b879ab56eb --- /dev/null +++ b/Detectors/DCS/testWorkflow/README.md @@ -0,0 +1,6 @@ +Local example workflow with local CCDB (running on port 6464) : + +```shell +o2-dcs-random-data-workflow --max-timeframes=10 | +o2-calibration-ccdb-populator-workflow --ccdb-path http://localhost:6464 +``` diff --git a/Detectors/DCS/testWorkflow/dcs-random-data-workflow.cxx b/Detectors/DCS/testWorkflow/dcs-random-data-workflow.cxx new file mode 100644 index 0000000000000..3f1539fb63e49 --- /dev/null +++ b/Detectors/DCS/testWorkflow/dcs-random-data-workflow.cxx @@ -0,0 +1,43 @@ +// 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 "DCSRandomDataGeneratorSpec.h" +#include "DCSDataProcessorSpec.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()); + specs.emplace_back(getDCSDataProcessorSpec()); + return specs; +} diff --git a/Detectors/README.md b/Detectors/README.md index 3be3ed677ae60..89114fd394a66 100644 --- a/Detectors/README.md +++ b/Detectors/README.md @@ -19,6 +19,7 @@ This module contains the following submodules: * \subpage refDetectorsCPV * \subpage refDetectorsCTF * \subpage refDetectorsEMCAL +* \subpage refDetectorsDCS * \subpage refDetectorsFIT * \subpage refDetectorsHMPID * \subpage refDetectorsITSMFT