diff --git a/DataModel/BeamDataPoint.h b/DataModel/BeamDataPoint.h index 3680456d6..2412ead2c 100644 --- a/DataModel/BeamDataPoint.h +++ b/DataModel/BeamDataPoint.h @@ -18,22 +18,25 @@ struct BeamDataPoint : public SerialisableObject { friend class boost::serialization::access; - BeamDataPoint() : value(0.), unit("") {} - BeamDataPoint(double Value, const std::string& Unit) - : value(Value), unit(Unit) {} + BeamDataPoint() : value(0.), unit(""), time(0) {} + BeamDataPoint(double Value, const std::string& Unit, uint64_t time=0) + : value(Value), unit(Unit), time(0) {} double value; std::string unit; + uint64_t time; template void serialize(Archive & ar, const unsigned int version) { ar & value; ar & unit; + ar & time; } virtual bool Print() override { std::cout << "Value : " << value << '\n'; - std::cout << "Unit : " << unit << '\n'; + std::cout << "Unit : " << unit << '\n'; + std::cout << "Time : " << time << '\n'; return true; } diff --git a/UserTools/BeamFetcherV2/BeamFetcherV2.cpp b/UserTools/BeamFetcherV2/BeamFetcherV2.cpp new file mode 100644 index 000000000..5831fb4ed --- /dev/null +++ b/UserTools/BeamFetcherV2/BeamFetcherV2.cpp @@ -0,0 +1,283 @@ +// standard library includes +#include +#include + +// ToolAnalysis includes +#include "BeamFetcherV2.h" +#include "IFBeamDBInterfaceV2.h" + +namespace { + constexpr uint64_t TWO_HOURS = 7200000ull; // ms + constexpr uint64_t THIRTY_SECONDS = 30000ull; // ms +} + +BeamFetcherV2::BeamFetcherV2():Tool() +{} + +//------------------------------------------------------------------------------ +bool BeamFetcherV2::Initialise(std::string config_filename, DataModel& data) +{ + // Load configuration file variables + if ( !config_filename.empty() ) m_variables.Initialise(config_filename); + + // Assign a transient data pointer + m_data = &data; + + // Get the things + bool got_verbosity = m_variables.Get("verbose", verbosity); + bool got_bundleflag = m_variables.Get("IsBundle", fIsBundle); + bool got_devicesfile = m_variables.Get("DevicesFile", fDevicesFile); + bool got_saveroot = m_variables.Get("SaveROOT", fSaveROOT); + bool got_chunkMSec = m_variables.Get("TimeChunkStepInMilliseconds", fChunkStepMSec); + + + // Check the config parameters and set default values if necessary + if (!got_verbosity) verbosity = 1; + + if (!got_devicesfile) { + logmessage = ("Error (BeamFetcherV2): You must define which devices to poll" + " via a DevicesFile."); + Log(logmessage, v_error, verbosity); + return false; + } else { + // Grab the stuff from the file + std::ifstream devicesFile(fDevicesFile); + if ( devicesFile.good() ) { + std::string line; + while (std::getline(devicesFile, line)) + fDevices.push_back(line); + + if (!fDevices.size()) { + logmessage = ("Error (BeamFetcherV2): No devices specified in your" + " Devices file."); + Log(logmessage, v_error, verbosity); + return false; + } + } else{ + logmessage = ("Error (BeamFetcherV2): Devices file " + "\"" + fDevicesFile + "\"" + " does not exists"); + Log(logmessage, v_error, verbosity); + return false; + } + devicesFile.close(); + } + + if (!got_saveroot) { + logmessage = ("Warning (BeamFetcherV2): SaveROOT was not" + " set in the config file. Using default \"false\""); + Log(logmessage, v_warning, verbosity); + fSaveROOT = false; + } + + + if (!got_chunkMSec) { + logmessage = ("Warning (BeamFetcherV2): TimeChunkStepInMilliseconds was not" + " set in the config file. Using default \"7200000\""); + Log(logmessage, v_warning, verbosity); + fChunkStepMSec = 7200000; + } + + + if (!got_bundleflag || (fIsBundle !=0 && fIsBundle != 1)) { + logmessage = ("Error (BeamFetcherV2): IsBundle flag was not set correctly" + " in the config file."); + Log(logmessage, v_error, verbosity); + return 0; + } + + if (fSaveROOT) this->SetupROOTFile(); + + return true; +} + + +//------------------------------------------------------------------------------ +bool BeamFetcherV2::Execute() +{ + // Do the things + bool goodFetch = this->FetchFromTrigger(); + + if (goodFetch) { + // Emplace fBeamDataToSave to CStore for other tools to use + m_data->CStore.Set("BeamData",fBeamDataToSave); + goodFetch = true; + } + + if (fSaveROOT) this->WriteTrees(); + + // Clear for the next Fetch + fBeamDataToSave.clear(); + + + return goodFetch; +} + +//------------------------------------------------------------------------------ +bool BeamFetcherV2::Finalise() +{ + if (fSaveROOT) this->SaveROOTFile(); + + std::cout << "BeamFetcherV2 tool exitting" << std::endl; + + return true; +} + +//------------------------------------------------------------------------------ +bool BeamFetcherV2::FetchFromTrigger() +{ + // Get a const reference to the beam database interface + const auto& db = IFBeamDBInterfaceV2::Instance(); + + // Need to get the trigger times + std::map>* TimeToTriggerWordMap=nullptr; + bool get_ok = m_data->CStore.Get("TimeToTriggerWordMap",TimeToTriggerWordMap); + + // Now loop over the CTC timestamps + if (get_ok && TimeToTriggerWordMap) { + for (auto iterator : *TimeToTriggerWordMap) { + // We only want to get beam info for beam triggers (word 5) + bool hasBeamTrig = false; + for (auto word : iterator.second) + if (word == 5) hasBeamTrig = true; + if (!hasBeamTrig) continue; + + uint64_t trigTimestamp = iterator.first; + + // Need to drop from ns to ms. This means that some timestamps will + // already be recorded. We can skip these cases + trigTimestamp = trigTimestamp/1E6; + if (fBeamDataToSave.find(trigTimestamp) != fBeamDataToSave.end()) + continue; + + // Check if we already have the info we need + bool fetch = false; + std::map >::iterator low, prev; + low = fBeamData.lower_bound(trigTimestamp); + if (low == fBeamData.end()) { + fetch = true; + logmessage = ("BeamFetcherV2: I'm going to query the DB"); + Log(logmessage, v_message, verbosity); + } + + // We'll pull fChunkStepMSec worth of data at a time to avoid rapid queries + if (fetch) { + if (fIsBundle) { + fBeamData = db.QueryBeamDBBundleSpan(fDevices[0], trigTimestamp, trigTimestamp+fChunkStepMSec); + } else { + std::map > tempMap; + + for (auto device : fDevices) { + auto tempMap = db.QueryBeamDBSingleSpan(device, trigTimestamp, trigTimestamp+fChunkStepMSec); + fBeamData.insert(tempMap.begin(), tempMap.end()); + } + } + } + + // Now we can match the Beam info to CTC timestamps for saving to the CStore + low = fBeamData.lower_bound(trigTimestamp); + if (low == fBeamData.end()) { + logmessage = ("Error (BeamFetcherV2): We fetched the data based on the CTC" + " but somehow didn't turn anything up!?"); + Log(logmessage, v_error, verbosity); + return false; + } else if (low == fBeamData.begin()) { + fBeamDataToSave[trigTimestamp] = low->second; + } else { + // Check the previous DB timestamp to see if it's closer in time + prev = std::prev(low); + if ((trigTimestamp - prev->first) < (low->first - trigTimestamp)) + fBeamDataToSave[trigTimestamp] = prev->second; + else + fBeamDataToSave[trigTimestamp] = low->second; + } + }// end loop over trigger times + } else { + logmessage = ("Error (BeamFetcherV2): Could not load CTC information for" + " timestamps. Did you run TriggerDataDecoder?"); + Log(logmessage, v_error, verbosity); + return false; + } + + return true; +} + +//------------------------------------------------------------------------------ +bool BeamFetcherV2::SaveToFile() +{ + BoostStore fBeamDBStore(false, BOOST_STORE_MULTIEVENT_FORMAT); + fBeamDBStore.Set("BeamData", fBeamData); + fBeamDBStore.Save(fOutFileName); + fBeamDBStore.Delete(); + + fBeamDBStore.Header->Set("BeamDBIndex", fBeamDBIdx); + + // Find the range of times covered by the entire downloaded database + uint64_t overall_start_ms = std::numeric_limits::max(); + uint64_t overall_end_ms = 0ull; + + for (const auto& pair : fBeamDBIdx) { + uint64_t temp_start_ms = pair.second.first; + uint64_t temp_end_ms = pair.second.second; + if (temp_start_ms < overall_start_ms) overall_start_ms = temp_start_ms; + if (temp_end_ms > overall_end_ms) overall_end_ms = temp_end_ms; + } + + fBeamDBStore.Header->Set("StartMillisecondsSinceEpoch", overall_start_ms); + fBeamDBStore.Header->Set("EndMillisecondsSinceEpoch", overall_end_ms); + + fBeamDBStore.Close(); + + logmessage = "Retrieval of beam status data complete"; + Log(logmessage, v_warning, verbosity); + + return true; +} + +//------------------------------------------------------------------------------ +void BeamFetcherV2::SetupROOTFile() +{ + fOutFile = new TFile("beamfetcher_tree.root", "RECREATE"); + fOutTree = new TTree("BeamTree", "BeamTree"); + fOutTree->Branch("Timestamp", &fTimestamp); +} + +//------------------------------------------------------------------------------ +void BeamFetcherV2::WriteTrees() +{ + // Loop over timestamps + int devCounter = 0; + for (const auto iterTS : fBeamDataToSave) { + fTimestamp = iterTS.first; + + // Loop over devices + for (const auto iterDev : iterTS.second) { + std::string device = iterDev.first; + std::replace( device.begin(), device.end(), ':', '_'); + + BeamDataPoint tempPoint = iterDev.second; + + // Dynamically create branches for each new device + if (fDevIdx.find(device) == fDevIdx.end()) { + fDevIdx[device] = devCounter; + fOutTree->Branch(device.c_str(), + &fValues[fDevIdx.at(device)]); + ++devCounter; + } + + fValues[fDevIdx.at(device)] = tempPoint.value; + }// end loop over devices + + fOutTree->Fill(); + }// end loop over timestamps + +} + +//------------------------------------------------------------------------------ +void BeamFetcherV2::SaveROOTFile() +{ + fOutFile->cd(); + fOutTree->Write(); + fOutFile->Close(); +} + diff --git a/UserTools/BeamFetcherV2/BeamFetcherV2.h b/UserTools/BeamFetcherV2/BeamFetcherV2.h new file mode 100644 index 000000000..94056ea72 --- /dev/null +++ b/UserTools/BeamFetcherV2/BeamFetcherV2.h @@ -0,0 +1,75 @@ +// Tool to download beam information from the Intensity Frontier +// beam database and save it to the CStore. Modified from the original BeamFetcher +// +// Steven Gardiner +// Andrew Sutton + +#pragma once + +// standard library includes +#include +#include + +// Boost includes +#include +#include + +// ToolAnalysis includes +#include "Tool.h" + +// ROOT includes +#include "TFile.h" +#include "TTree.h" + + +class BeamFetcherV2: public Tool { + + public: + BeamFetcherV2(); + bool Initialise(std::string configfile, DataModel& data); + bool Execute(); + bool Finalise(); + + protected: + bool FetchFromTrigger(); + bool SaveToFile(); + + void SetupROOTFile(); + void WriteTrees(); + void SaveROOTFile(); + + + // Holder for the retrieved data and the stuff we'll save + std::map > fBeamData; + std::map > fBeamDataToSave; + + // For saving out to a file + std::map > fBeamDBIdx; + + // Holder for the devices we're going to look up + std::vector fDevices; + + // For ROOT file + TFile *fOutFile; + TTree *fOutTree; + uint64_t fTimestamp; + double fValues[100]; + std::map fDevIdx; // map from device string to idx in fValues + + + // Configuration variables + int verbosity; + bool fIsBundle; + bool fSaveROOT; + std::string fDevicesFile; + std::string fOutFileName; + uint64_t fChunkStepMSec; + + // Verbosity things + int v_error = 0; + int v_warning = 1; + int v_message = 2; + int v_debug = 3; + int vv_debug = 4; + std::string logmessage; +}; diff --git a/UserTools/BeamFetcherV2/IFBeamDBInterfaceV2.cpp b/UserTools/BeamFetcherV2/IFBeamDBInterfaceV2.cpp new file mode 100644 index 000000000..99e0a5148 --- /dev/null +++ b/UserTools/BeamFetcherV2/IFBeamDBInterfaceV2.cpp @@ -0,0 +1,377 @@ +// standard library includes +#include +#include +#include +#include +#include +#include +#include + +// ToolAnalysis includes +#include "ANNIEconstants.h" +#include "IFBeamDBInterfaceV2.h" + +IFBeamDBInterfaceV2::IFBeamDBInterfaceV2() +{ + fCurl = curl_easy_init(); + if (!fCurl) throw std::runtime_error("IFBeamDBInterfaceV2 failed to" + " initialize libcurl"); +} + +IFBeamDBInterfaceV2::~IFBeamDBInterfaceV2() +{ + curl_easy_cleanup(fCurl); +} + +//////////////////////////////////////////////////////////////////////////////// +const IFBeamDBInterfaceV2& IFBeamDBInterfaceV2::Instance() { + + // Create the IFBeamDBInterfaceV2 using a static variable. This ensures that + // the singleton instance is only created once. + static std::unique_ptr the_instance( + new IFBeamDBInterfaceV2()); + + // Return a reference to the singleton instance + return *the_instance; +} + +//////////////////////////////////////////////////////////////////////////////// +std::map > + IFBeamDBInterfaceV2::QueryBeamDBSingleSpan(std::string device, uint64_t t0, uint64_t t1) + const +{ + std::stringstream url_stream; + url_stream << "http://ifb-data.fnal.gov:8089/ifbeam/data/data?e=e,1d&v="; + url_stream << device; + url_stream << std::fixed << std::setprecision(3) << (t0 - 1)/1000.; + url_stream << "&t1=" << (t1 + 1)/1000.; + url_stream << "&f=csv"; + + std::string response; + int code = RunQuery(url_stream, response); + + if (code != CURLE_OK) throw std::runtime_error("Error accessing" + " IF beam database. Please check your internet connection."); + + auto beam_data = ParseDBResponseSingleSpan(response); + + return beam_data; +} + +//////////////////////////////////////////////////////////////////////////////// +std::map > + IFBeamDBInterfaceV2::QueryBeamDBBundleSpan(std::string bundle, uint64_t t0, uint64_t t1) + const +{ + std::stringstream url_stream; + url_stream << "http://ifb-data.fnal.gov:8089/ifbeam/data/data?e=e,1d&b="; + url_stream << bundle; + url_stream << "&t0="; + url_stream << std::fixed << std::setprecision(3) << (t0 - 1)/1000.; + url_stream << "&t1=" << (t1 + 1)/1000.; + url_stream << "&f=csv"; + + std::string response; + int code = RunQuery(url_stream, response); + + if (code != CURLE_OK) throw std::runtime_error("Error accessing" + " IF beam database. Please check your internet connection."); + + auto beam_data = ParseDBResponseBundleSpan(response); + + return beam_data; +} + +//////////////////////////////////////////////////////////////////////////////// +std::map > + IFBeamDBInterfaceV2::QueryBeamDBSingle(std::string device, uint64_t time) const +{ + std::stringstream url_stream; + url_stream << "http://ifb-data.fnal.gov:8089/ifbeam/data/data?e=e,1d&v="; + url_stream << device; + url_stream << "&t=" << std::fixed << std::setprecision(3) << time/1000.; + url_stream << "&f=xml"; + + std::string response; + int code = RunQuery(url_stream, response); + + if (code != CURLE_OK) throw std::runtime_error("Error accessing" + " IF beam database. Please check your internet connection."); + + auto beam_data = ParseDBResponseSingle(response); + + return beam_data; +} + +//////////////////////////////////////////////////////////////////////////////// +std::map > + IFBeamDBInterfaceV2::QueryBeamDBBundle(std::string bundle, uint64_t time) const +{ + std::stringstream url_stream; + url_stream << "http://ifb-data.fnal.gov:8089/ifbeam/data/data?e=e,1d&b="; + url_stream << bundle; + url_stream << "&t=" << std::fixed << std::setprecision(3) << time/1000.; + url_stream << "&f=csv"; + + std::string response; + int code = RunQuery(url_stream, response); + + if (code != CURLE_OK) throw std::runtime_error("Error accessing" + " IF beam database. Please check your internet connection."); + + auto beam_data = ParseDBResponseBundle(response); + + return beam_data; +} + +int IFBeamDBInterfaceV2::RunQuery(const std::stringstream &url_stream, std::string &response_string) + const +{ + if (!fCurl) { + throw std::runtime_error("IFBeamDBInterfaceV2::RunQuery() called" + " without inititalizing libcurl"); + return -1; + } + + //std::cout << "IFBeamDBInterfaceV2: sending the following query: " << url_stream.str() << std::endl; + + curl_easy_setopt(fCurl, CURLOPT_URL, url_stream.str().c_str()); + + response_string.clear(); + + curl_easy_setopt(fCurl, CURLOPT_WRITEFUNCTION, + static_cast( + [](char* ptr, size_t size, + size_t num_members, std::string* data) -> size_t + { + data->append(ptr, size * num_members); + return size * num_members; + } + ) + ); + + curl_easy_setopt(fCurl, CURLOPT_WRITEDATA, &response_string); + + int code = curl_easy_perform(fCurl); + + // Check the HTTP response code from the IF beam database server. If + // it's not 200, then throw an exception (something went wrong). + // For more information about the possible HTTP status codes, see + // this Wikipedia article: http://tinyurl.com/8yqvhwf + long http_response_code; + constexpr long HTTP_OK = 200; + curl_easy_getinfo(fCurl, CURLINFO_RESPONSE_CODE, &http_response_code); + if (http_response_code != HTTP_OK) { + throw std::runtime_error("HTTP error (code " + + std::to_string(http_response_code) + ") encountered while querying" + " the IF beam database"); + } + + return code; +} + +// Here's some documentation for some of the parameters stored in the beam +// database. It's taken from the MicroBooNE operations wiki: +// http://tinyurl.com/z3c4mxs +// +// The status page shows the present reading of beamline instrumentation. All +// of this data is being stored to IF beam Database. The "IF Beam DB +// dashboard":http://dbweb4.fnal.gov:8080/ifbeam/app/BNBDash/index provides +// another view of beam data. Some of it is redundant to the status page, but +// it verifies that the data is being stored in the database. At present the +// page shows following devices: +// * TOR860, TOR875 - two toroids in BNB measuring beam intensity. TOR860 is +// at the beginning of the beamline, and TOR875 is at the end. +// * THCURR - horn current +// * HWTOUT - horn water temperature coming out of the horn. +// * BTJT2 - target temperature +// * HP875, VP875 - beam horizontal and vertical positions at the 875 +// location, about 4.5 m upstream of the target center. +// * HPTG1, VPTG1 - beam horizontal and vertical positions immediately +// (about 2.5 m) upstream of the target center. +// * HPTG2, VPTG2 - beam horizontal and vertical positions more immediately +// (about 1.5 m) upstream of the target center. +// * Because there are no optics between H/VP875 and H/VPTG2, the movements +// on these monitors should scale with the difference in distances. +// * BTJT2 - target air cooling temperature. Four RTD measure the return +// temperature of the cooling air. This is the one closest to the target. +// * BTH2T2 - target air cooling temperature. This is the temperature of the +// air going into the horn. + +//////////////////////////////////////////////////////////////////////////////// +std::map > +IFBeamDBInterfaceV2::ParseDBResponseSingleSpan(const std::string& response) const +{ + // Create an empty map to store the parsed data. + std::map > retMap; + + // Use a stringstream to parse the data + std::istringstream response_stream(response); + + // Temporary storage for the comma-separated fields in the database response + // string + std::string line, junk, data_type, time_string, unit, val_string; + uint64_t timestamp; + + // Holder for the earliest TS in a chunk + // We will roll this forward when a TS 60 ms later comes in + // (the max rate of $1D is 15 Hz or 66 ms) + uint64_t earlyTS = 0; + + // Skip the first line (which gives textual column headers) + std::getline(response_stream, line, '\n'); + + while (std::getline(response_stream, line, '\n')) { + std::stringstream ss(line); + + std::getline(ss, junk, ','); + std::getline(ss, data_type, ','); + std::getline(ss, time_string, ','); + std::getline(ss, unit, ','); + std::getline(ss, val_string); + + timestamp = std::stoull(time_string); + if (timestamp - earlyTS > 60) earlyTS = timestamp; + + retMap[earlyTS][data_type] = BeamDataPoint(std::stod(val_string), + unit, + timestamp); + } + + return retMap; +} + +//////////////////////////////////////////////////////////////////////////////// +std::map > +IFBeamDBInterfaceV2::ParseDBResponseBundleSpan(const std::string& response) const +{ + // Create an empty map to store the parsed data. + std::map > retMap; + + // Use a stringstream to parse the data + std::istringstream response_stream(response); + + // Temporary storage for the comma-separated fields in the database response + // string + std::string line, junk, data_type, time_string, unit, val_string; + uint64_t timestamp; + + // Holder for the earliest TS in a chunk + // We will roll this forward when a TS 60 ms later comes in + // (the max rate of $1D is 15 Hz or 66 ms) + uint64_t earlyTS = 0; + + + // Skip the first line (which gives textual column headers) + std::getline(response_stream, line, '\n'); + + while (std::getline(response_stream, line, '\n')) { + std::stringstream ss(line); + + std::getline(ss, time_string, ','); + std::getline(ss, data_type, ','); + std::getline(ss, unit, ','); + std::getline(ss, val_string); + + timestamp = std::stoull(time_string); + if (timestamp - earlyTS > 60) earlyTS = timestamp; + + retMap[earlyTS][data_type] = BeamDataPoint(std::stod(val_string), + unit, + timestamp); + } + + return retMap; +} + +//////////////////////////////////////////////////////////////////////////////// +std::map > +IFBeamDBInterfaceV2::ParseDBResponseSingle(const std::string& response) const +{ + std::map > retMap; + + // Use a stringstream to parse the data + std::istringstream response_stream(response); + + // Temporary storage for the comma-separated fields in the database response + // string + std::string line, junk, data_type, time_string, unit, val_string; + uint64_t timestamp; + + // This is a bit less necessary here, + // but it keeps all the points in the same timestamp map element + // Holder for the earliest TS in a chunk + // We will roll this forward when a TS 60 ms later comes in + // (the max rate of $1D is 15 Hz or 66 ms) + uint64_t earlyTS = 0; + + // Skip the first line (which gives textual column headers) + std::getline(response_stream, line, '\n'); + + // The second line has all the info, but also a load of crap + std::getline(response_stream, line, '\n'); + std::stringstream ss(line); + std::getline(ss, junk, '"'); + std::getline(ss, data_type, '"'); + std::getline(ss, junk, '"'); std::getline(ss, junk, '"'); std::getline(ss, junk, '"'); + std::getline(ss, unit, '"'); + std::getline(ss, junk, '"'); std::getline(ss, junk, '"'); std::getline(ss, junk, '"'); + std::getline(ss, time_string, '"'); + std::getline(ss, junk, '>'); + std::getline(ss, val_string, '\n'); + // we can ignore the remaining lines and finish up + + timestamp = std::stoull(time_string); + if (timestamp - earlyTS > 60) earlyTS = timestamp; + + retMap[earlyTS][data_type] = BeamDataPoint(std::stod(val_string), + unit, + timestamp); + + return retMap; +} + +//////////////////////////////////////////////////////////////////////////////// +std::map > +IFBeamDBInterfaceV2::ParseDBResponseBundle(const std::string& response) const +{ + std::map > retMap; + + // Use a stringstream to parse the data + std::istringstream response_stream(response); + + // Temporary storage for the comma-separated fields in the database response + // string + std::string line, junk, data_type, time_string, unit, val_string; + uint64_t timestamp; + + // This is a bit less necessary here, + // but it keeps all the points in the same timestamp map element + // Holder for the earliest TS in a chunk + // We will roll this forward when a TS 60 ms later comes in + // (the max rate of $1D is 15 Hz or 66 ms) + uint64_t earlyTS = 0; + + // Skip the first line (which gives textual column headers) + std::getline(response_stream, line, '\n'); + + while (std::getline(response_stream, line, '\n')) { + std::stringstream ss(line); + + std::getline(ss, data_type, ','); + std::getline(ss, junk, ','); + std::getline(ss, time_string, ','); + std::getline(ss, unit, ','); + std::getline(ss, val_string); + + timestamp = std::stoull(time_string); + if (timestamp - earlyTS > 60) earlyTS = timestamp; + + retMap[earlyTS][data_type] = BeamDataPoint(std::stod(val_string), + unit, + timestamp); + } + + return retMap; +} + diff --git a/UserTools/BeamFetcherV2/IFBeamDBInterfaceV2.h b/UserTools/BeamFetcherV2/IFBeamDBInterfaceV2.h new file mode 100644 index 000000000..7ba3c6434 --- /dev/null +++ b/UserTools/BeamFetcherV2/IFBeamDBInterfaceV2.h @@ -0,0 +1,90 @@ +#pragma once +// Singleton class used by the BeamFetcherV2 tool to communicate with the +// Fermilab Intensity Frontier beam database +// Modified from the original version in the BeamFetcher tool +// (see http://ifb-data.fnal.gov:8100/ifbeam/data) +// +// Steven Gardiner (sjgardiner@ucdavis.edu) +// Andrew Sutton (asutton@fnal.gov) + +// standard library includes +#include +#include + +// libcurl includes +#include + +// ToolAnalysis includes +#include "BeamDataPoint.h" +#include "BeamStatus.h" +/// @brief Singleton used to interact with the Intensity Frontier beam database +/// at Fermilab +class IFBeamDBInterfaceV2 { + + public: + + /// @brief Clean up libcurl stuff as the IFBeamDBInterfaceV2 object is + /// destroyed + ~IFBeamDBInterfaceV2(); + + /// @brief Deleted copy constructor + IFBeamDBInterfaceV2(const IFBeamDBInterfaceV2&) = delete; + + /// @brief Deleted move constructor + IFBeamDBInterfaceV2(IFBeamDBInterfaceV2&&) = delete; + + /// @brief Deleted copy assignment operator + IFBeamDBInterfaceV2& operator=(const IFBeamDBInterfaceV2&) = delete; + + /// @brief Deleted move assignment operator + IFBeamDBInterfaceV2& operator=(IFBeamDBInterfaceV2&&) = delete; + + /// @brief Get a const reference to the singleton instance of the + /// IFBeamDBInterfaceV2 + static const IFBeamDBInterfaceV2& Instance(); + + /// @brief Get information about the Booster Neutrino Beam (BNB) state from + /// the database for the time interval [t0, t1] + /// @param t0 Starting timestamp (milliseconds since the Unix epoch) + /// @param t1 Starting timestamp (milliseconds since the Unix epoch) + /// @return A nested map containing the parsed data. Keys of the outer map + /// are device names, values are inner maps. Keys of the inner map are + /// timestamps (milliseconds since the Unix epoch), values are + /// BeamDataPoint structs that hold a numerical value and a unit string. + /// @note An easy way to get the current milliseconds since the Unix epoch + /// is to use the terminal utility date like this: @verbatim date +%s%3N + /// @endverbatim + /// I found this handy trick here: http://unix.stackexchange.com/a/123764 + std::map > + QueryBeamDBSingleSpan(std::string device, uint64_t t0, uint64_t t1) const; + + std::map > + QueryBeamDBBundleSpan(std::string bundle, uint64_t t0, uint64_t t1) const; + + std::map > + QueryBeamDBSingle(std::string device, uint64_t time) const; + + std::map > + QueryBeamDBBundle(std::string bundle, uint64_t time) const; + + int RunQuery(const std::stringstream &url_stream, std::string &response_string) const; + + protected: + /// @brief Create the singleton IFBeamDBInterfaceV2 object + IFBeamDBInterfaceV2(); + + std::map > + ParseDBResponseSingleSpan(const std::string& response) const; + + std::map > + ParseDBResponseBundleSpan(const std::string& response) const; + + std::map > + ParseDBResponseSingle(const std::string& response) const; + + std::map > + ParseDBResponseBundle(const std::string& response) const; + + /// @brief Pointer used to interact with libcurl + CURL* fCurl = nullptr; +}; diff --git a/UserTools/BeamFetcherV2/README.md b/UserTools/BeamFetcherV2/README.md new file mode 100644 index 000000000..04df17154 --- /dev/null +++ b/UserTools/BeamFetcherV2/README.md @@ -0,0 +1,48 @@ +# BeamFetcherV2 + +The `BeamFetcherV2` tool obtains information about the status of the BNB from the IF database. It has two distinct modes of operation FetchFromTimes of FetchFromTrigger controlled by the boolean config flag `FetchFromTimes`. In FromTimes mode you will pass in start and stop times and a new file will be created with a BoostStore. In FromTrigger mode you first must run the TriggerDataDecoder then BeamFetcher will grab the trigger timestamp and find the nearest matching time for the beam device. FromTrigger mode will save the info into the CStore for access later on. The `IFBeamDBInterface` class is a helper class that handles the details of the communication with the IF database. + +## Data + +FetchFromTimes: The following objects will be saved in the BeamStatus BoostStore +* "BeamDBIndex" (Header) `map` + * Designates the time interval that is stored in the given BoostStore entry +* "StartMillisecondsSinceEpoch" (Header) `uint64_t` + * Designates the overall start time of database entries stored in the BoostStore +* "EndMillisecondsSinceEpoch" (Header) `uint64_t` + * Designates the overall end time of database entries stored in the BoostStore +* "BeamDB" `map>` + * Actual beam status information, keys are the device names (e.g. E:TOR875) + +FetchFromTrigger: The following objects will be put into the CStore +* "BeamData" `map< uint_64, map >` + * The info from the DB where the key is the associated trigger timestamp to the nearest milliseconds and the internal map is from device name (e.g. E:TOR875) to a BeamDataPoint object containing the readback value, units, and actual timestamp. + +## Configuration + +The main configuration variable for the `BeamFetcher` tool is `FetchFromTimes`, which determines whether to use the tigger timestamps or user input timestamps. The `DevicesFile`, `IsBundle`, and `TimeChunkStepInMilliseconds` variables are required regardless of the fetch mode. You can also set the `SaveROOT` bool in order to save out a TTree with the timestamp and device values as the leaves. + +If `FetchFromTimes == 1` then you will also need the additional config variables. The preferred timestamp format is chosen with the `TimestampMode` variable (LOCALDATE/MSEC/DB). For LOCALDATE mode you use Start/EndDate files with string formatted times (like 2023-04-11 23:03:19.163505). For MSEC mode you use the Start/EndMillisecondsSinceEpoch variables. For DB mode you must first run `LoadRunInfo` and the run timestamps will be pulled from the CStore. + +``` +# BeamFetcher config file +verbose 5 +# +# These three are always needed +# +DevicesFile ./configfiles/BeamFetcher/devices.txt # File containing one device per line or a bundle +IsBundle 0 # bool stating whether DevicesFile contains bundles or individual devices +FetchFromTimes 0 # bool defining how to grab the data (from input times (1) or trigger(0)) +TimeChunkStepInMilliseconds 3600000 # one hour +SaveROOT 0 # bool, do you want to write a ROOT file with the timestamps and devices? +# +# These parameters are only needed if FetchFromTimes == 1 +# +OutputFile ./1604_beamdb +TimestampMode LOCALDATE +DaylightSavings 0 # Do we need to account for DST? +StartDate ./configfiles/BeamFetcher/my_start_date.txt #String form of start date stored in a file +EndDate ./configfiles/BeamFetcher/my_end_date.txt #String form of end date stored in a file +#StartMillisecondsSinceEpoch 1491132659000 # 6:30:49 AM 2 April 2017 (FNAL time) #msec format of start time +#EndMillisecondsSinceEpoch 1491164001000 # 3:13:21 PM 2 April 2017 (FNAL time) #msec format of end time +``` diff --git a/UserTools/Factory/Factory.cpp b/UserTools/Factory/Factory.cpp index 706d3e4f1..59dc6005f 100644 --- a/UserTools/Factory/Factory.cpp +++ b/UserTools/Factory/Factory.cpp @@ -158,5 +158,6 @@ if (tool=="ReweightEventsGenie") ret=new ReweightEventsGenie; if (tool=="FilterLAPPDEvents") ret=new FilterLAPPDEvents; if (tool=="FilterEvents") ret=new FilterEvents; if (tool=="Stage1DataBuilder") ret=new Stage1DataBuilder; +if (tool=="BeamFetcherV2") ret=new BeamFetcherV2; return ret; } diff --git a/UserTools/Unity.h b/UserTools/Unity.h index 7fab6b155..e77bfa810 100644 --- a/UserTools/Unity.h +++ b/UserTools/Unity.h @@ -166,3 +166,4 @@ #include "FilterLAPPDEvents.h" #include "FilterEvents.h" #include "Stage1DataBuilder.h" +#include "BeamFetcherV2.h"