From bb74e0d30817bc3710a729293a2fae94fffe4773 Mon Sep 17 00:00:00 2001 From: splitPoleDAQ Date: Wed, 24 May 2023 16:04:32 -0400 Subject: [PATCH] add influxdb --- ClassData.h | 2 +- ClassDigitizer.h | 4 +- DigiSettingsPanel.cpp | 2 + FSUDAQ.cpp | 82 ++++++++++++++++++++-- FSUDAQ.h | 4 ++ FSUDAQ_Qt6.pro | 4 +- README.md | 31 +++++++++ Scope.cpp | 9 ++- influxdb.cpp | 157 ++++++++++++++++++++++++++++++++++++++++++ influxdb.h | 58 ++++++++++++++++ 10 files changed, 341 insertions(+), 12 deletions(-) create mode 100644 README.md create mode 100644 influxdb.cpp create mode 100644 influxdb.h diff --git a/ClassData.h b/ClassData.h index fb924f7..c1b52de 100644 --- a/ClassData.h +++ b/ClassData.h @@ -188,7 +188,7 @@ inline void Data::ClearData(){ } inline void Data::ClearBuffer(){ - printf("==== %s \n", __func__); + printf("==== Data::%s \n", __func__); delete buffer; buffer = nullptr; AllocatedSize = 0; diff --git a/ClassDigitizer.h b/ClassDigitizer.h index 7be3a64..255e27b 100644 --- a/ClassDigitizer.h +++ b/ClassDigitizer.h @@ -103,8 +103,8 @@ class Digitizer{ CAEN_DGTZ_BoardInfo_t GetBoardInfo() const {return BoardInfo;} std::string GetModelName() const {return BoardInfo.ModelName;} int GetSerialNumber() const {return BoardInfo.SerialNumber;} - int GetChannelMask() const {return channelMask;} - bool GetChannelOnOff(unsigned ch) const {return (channelMask & ( 1 << ch) );} + int GetChannelMask() { channelMask = GetSettingFromMemory(DPP::ChannelEnableMask); return channelMask;} + bool GetChannelOnOff(unsigned ch) { channelMask = GetSettingFromMemory(DPP::ChannelEnableMask); return (channelMask & ( 1 << ch) );} float GetCh2ns() const {return ch2ns;} int GetNChannels() const {return NChannel;} int GetHandle() const {return handle;} diff --git a/DigiSettingsPanel.cpp b/DigiSettingsPanel.cpp index 25c0597..526809a 100644 --- a/DigiSettingsPanel.cpp +++ b/DigiSettingsPanel.cpp @@ -1221,6 +1221,8 @@ void DigiSettingsPanel::SetUpChannelMask(){ if( digi[ID]->GetDPPType() == V1730_DPP_PHA_CODE ) UpdatePHASetting(); if( digi[ID]->GetDPPType() == V1730_DPP_PSD_CODE ) UpdatePSDSetting(); + + emit UpdateOtherPanels(); }); } diff --git a/FSUDAQ.cpp b/FSUDAQ.cpp index 8b67d26..5298605 100644 --- a/FSUDAQ.cpp +++ b/FSUDAQ.cpp @@ -12,6 +12,10 @@ #include #include +//TODO make the address in config file +const std::string influxIP = "https://fsunuc.physics.fsu.edu/influx/"; +const std::string dataBaseName = "testing"; + MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent){ setWindowTitle("FSU DAQ"); @@ -166,6 +170,43 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent){ //=========== disable widget WaitForDigitizersOpen(true); + + {//^====================== database + influx = new InfluxDB(influxIP, false); + + if( influx->TestingConnection() ){ + LogMsg(" InfluxDB URL ("+ QString::fromStdString(influxIP) + ") is Valid "); + //==== chck database exist + LogMsg("List of database:"); + std::vector databaseList = influx->GetDatabaseList(); + bool foundDatabase = false; + for( int i = 0; i < (int) databaseList.size(); i++){ + if( databaseList[i] == dataBaseName ) foundDatabase = true; + //LogMsg(QString::number(i) + "|" + QString::fromStdString(databaseList[i])); + } + if( foundDatabase ){ + LogMsg(" Database " + QString::fromStdString(dataBaseName) + " found."); + influx->AddDataPoint("ProgramStart value=1"); + influx->WriteData(dataBaseName); + influx->ClearDataPointsBuffer(); + if( influx->IsWriteOK() ){ + LogMsg("test write database OK."); + }else{ + LogMsg("test write database FAIL."); + } + }else{ + LogMsg(" Database " + QString::fromStdString(dataBaseName) + " NOT found."); + delete influx; + influx = nullptr; + } + }else{ + LogMsg(" InfluxDB URL ("+ QString::fromStdString(influxIP) + ") is NOT Valid "); + delete influx; + influx = nullptr; + } + + } + } MainWindow::~MainWindow(){ @@ -187,6 +228,8 @@ MainWindow::~MainWindow(){ delete scalarThread; } + delete influx; + } //*************************************************************** @@ -497,7 +540,7 @@ void MainWindow::SetupScalar(){ scalarThread = new TimingThread(); connect(scalarThread, &TimingThread::timeUp, this, &MainWindow::UpdateScalar); - scalar->setGeometry(0, 0, 10 + nDigi * 200, 110 + MaxNChannels * 25); + scalar->setGeometry(0, 0, 10 + nDigi * 200, 110 + MaxNChannels * 20); if( lbLastUpdateTime == nullptr ){ lbLastUpdateTime = new QLabel("Last update : NA", scalar); @@ -534,6 +577,7 @@ void MainWindow::SetupScalar(){ rowID = 2; leTrigger[iDigi] = new QLineEdit *[digi[iDigi]->GetNChannels()]; leAccept[iDigi] = new QLineEdit *[digi[iDigi]->GetNChannels()]; + uint32_t chMask = digi[iDigi]->GetChannelMask(); for( int ch = 0; ch < MaxNChannels; ch++){ if( ch == 0 ){ @@ -552,7 +596,6 @@ void MainWindow::SetupScalar(){ } rowID ++; - leTrigger[iDigi][ch] = new QLineEdit(scalar); leTrigger[iDigi][ch]->setReadOnly(true); leTrigger[iDigi][ch]->setAlignment(Qt::AlignRight); @@ -562,6 +605,10 @@ void MainWindow::SetupScalar(){ leAccept[iDigi][ch]->setReadOnly(true); leAccept[iDigi][ch]->setAlignment(Qt::AlignRight); leAccept[iDigi][ch]->setStyleSheet("background-color: #F0F0F0;"); + + leTrigger[iDigi][ch]->setEnabled( (chMask >> ch) & 0x1 ); + leAccept[iDigi][ch]->setEnabled( (chMask >> ch) & 0x1 ); + scalarLayout->addWidget(leAccept[iDigi][ch], rowID, 2*iDigi+2); } } @@ -603,7 +650,7 @@ void MainWindow::OpenScalar(){ void MainWindow::UpdateScalar(){ if( digi == nullptr ) return; if( scalar == nullptr ) return; - if( !scalar->isVisible() ) return; + //if( !scalar->isVisible() ) return; lbLastUpdateTime->setText("Last update: " + QDateTime::currentDateTime().toString("MM.dd hh:mm:ss")); @@ -611,13 +658,26 @@ void MainWindow::UpdateScalar(){ for( unsigned int iDigi = 0; iDigi < nDigi; iDigi++){ digiMTX[iDigi].lock(); for( int i = 0; i < digi[iDigi]->GetNChannels(); i++){ - //printf(" %3d %2d | %7.2f %7.2f \n", digi[iDigi]->GetSerialNumber(), i, digi[iDigi]->GetData()->TriggerRate[i], digi[iDigi]->GetData()->NonPileUpRate[i]); - leTrigger[iDigi][i]->setText(QString::number(digi[iDigi]->GetData()->TriggerRate[i], 'f', 2)); - leAccept[iDigi][i]->setText(QString::number(digi[iDigi]->GetData()->NonPileUpRate[i], 'f', 2)); + if( digi[iDigi]->GetChannelOnOff(i) == true ) { + //printf(" %3d %2d | %7.2f %7.2f \n", digi[iDigi]->GetSerialNumber(), i, digi[iDigi]->GetData()->TriggerRate[i], digi[iDigi]->GetData()->NonPileUpRate[i]); + leTrigger[iDigi][i]->setText(QString::number(digi[iDigi]->GetData()->TriggerRate[i], 'f', 2)); + leAccept[iDigi][i]->setText(QString::number(digi[iDigi]->GetData()->NonPileUpRate[i], 'f', 2)); + + if( influx ){ + influx->AddDataPoint("Rate,Bd="+std::to_string(digi[iDigi]->GetSerialNumber()) + ",Ch=" + QString::number(i).rightJustified(2, '0').toStdString() + " value=" + QString::number(digi[iDigi]->GetData()->TriggerRate[i], 'f', 2).toStdString()); + } + + } } digiMTX[iDigi].unlock(); } + + if( influx ){ + influx->WriteData(dataBaseName); + influx->ClearDataPointsBuffer(); + } + } //*************************************************************** @@ -909,6 +969,16 @@ void MainWindow::UpdateAllPanels(int panelID){ if( panelID == 2 ){ if(scope && scope->isVisible() ) scope->UpdatePanelFromMomeory(); + + if(scalar) { + for( unsigned int iDigi = 0; iDigi < nDigi; iDigi++){ + uint32_t chMask = digi[iDigi]->GetChannelMask(); + for( int i = 0; i < digi[iDigi]->GetNChannels(); i++){ + leTrigger[iDigi][i]->setEnabled( (chMask >> i) & 0x1 ); + leAccept[iDigi][i]->setEnabled( (chMask >> i) & 0x1 ); + } + } + } } } diff --git a/FSUDAQ.h b/FSUDAQ.h index e7ab178..a5c4662 100644 --- a/FSUDAQ.h +++ b/FSUDAQ.h @@ -17,6 +17,7 @@ #include "Scope.h" #include "DigiSettingsPanel.h" #include "CanvasClass.h" +#include "influxdb.h" //^#===================================================== MainWindow class MainWindow : public QMainWindow{ @@ -85,6 +86,9 @@ private: QPushButton * bnCanvas; + //@----- influx + InfluxDB * influx; + //@----- log msg QPlainTextEdit * logInfo; void LogMsg(QString msg); diff --git a/FSUDAQ_Qt6.pro b/FSUDAQ_Qt6.pro index 5522649..d96c117 100644 --- a/FSUDAQ_Qt6.pro +++ b/FSUDAQ_Qt6.pro @@ -12,7 +12,7 @@ QT += core widgets charts #LIBS += -lCAENDigitizer `root-config --cflags --glibs` #QMAKE_CXXFLAGS += -g -LIBS += -lCAENDigitizer +LIBS += -lCAENDigitizer -lcurl # You can make your code fail to compile if you use deprecated APIs. # In order to do so, uncomment the following line. @@ -30,11 +30,13 @@ HEADERS += ClassData.h \ FSUDAQ.h \ macro.h \ RegisterAddress.h \ + influxdb.h\ Scope.h \ CanvasClass.h SOURCES += ClassDigitizer.cpp \ DigiSettingsPanel.cpp \ FSUDAQ.cpp \ main.cpp \ + influxdb.cpp\ Scope.cpp \ CanvasClass.cpp diff --git a/README.md b/README.md new file mode 100644 index 0000000..1b54df7 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# Introduction + +This is a DAQ for 1st gen CAEN digitizer for V1725, V17255S, V1230 with PHA and PSD firmware. + +It has scope (updated every half-sec), allow full control of the digitizer (except LVDS), and allow saving waevform. + +# Required / Development enviroment + +Ubuntu 22.04 + +CAENVMELib_v3.3 + +CAENCOmm_v1.5.3 + +CAENDigitizer_v2.17.1 + +`sudo apt install qt6-base-dev libcurl4-openssl-dev libqt6charts6-dev` + +# Compile + +use `qmake6 -project ` to generate the *.pro + +in the *.pro, add + +` QT += core widgets charts` + +` LIBS += -lCAENDigitizer -lcurl` + +then run ` qmake6 *.pro` it will generate Makefile + +then ` make` \ No newline at end of file diff --git a/Scope.cpp b/Scope.cpp index 13ad080..2c803bd 100644 --- a/Scope.cpp +++ b/Scope.cpp @@ -13,8 +13,6 @@ Scope::Scope(Digitizer ** digi, unsigned int nDigi, ReadDataThread ** readDataTh this->nDigi = nDigi; this->readDataThread = readDataThread; - for( unsigned int i = 0; i < nDigi; i++){ traceOn[i] = digi[i]->IsRecordTrace();} - setWindowTitle("Scope"); setGeometry(0, 0, 1000, 800); setWindowFlags( this->windowFlags() & ~Qt::WindowCloseButtonHint ); @@ -232,6 +230,8 @@ void Scope::StartScope(){ for( unsigned int iDigi = 0; iDigi < nDigi; iDigi ++){ + traceOn[iDigi] = digi[iDigi]->IsRecordTrace(); //remember setting + digi[iDigi]->SetBits(DPP::BoardConfiguration, DPP::Bit_BoardConfig::RecordTrace, 1, -1); digi[iDigi]->GetData()->SetSaveWaveToMemory(true); @@ -300,6 +300,9 @@ void Scope::UpdateScope(){ if( !digi ) return; int ch = cbScopeCh->currentIndex(); + + if( digi[ID]->GetChannelOnOff(ch) == false) return; + int ch2ns = digi[ID]->GetCh2ns(); int factor = digi[ID]->IsDualTrace_PHA() ? 2 : 1; @@ -715,6 +718,8 @@ void Scope::UpdatePanelFromMomeory(){ if( digi[ID]->GetDPPType() == V1730_DPP_PHA_CODE ) UpdatePHAPanel(); if( digi[ID]->GetDPPType() == V1730_DPP_PSD_CODE ) UpdatePSDPanel(); + settingGroup->setEnabled(digi[ID]->GetChannelOnOff(ch)); + } void Scope::UpdatePHAPanel(){ diff --git a/influxdb.cpp b/influxdb.cpp new file mode 100644 index 0000000..afb8747 --- /dev/null +++ b/influxdb.cpp @@ -0,0 +1,157 @@ +#include "influxdb.h" + + +InfluxDB::InfluxDB(std::string url, bool verbose){ + + curl = curl_easy_init(); + if( verbose) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + SetURL(url); + respondCode = 0; + dataPoints = ""; +} + +InfluxDB::~InfluxDB(){ + curl_easy_cleanup(curl); +} + +void InfluxDB::SetURL(std::string url){ + // check the last char of url is "/" + if( url.back() != '/') { + this->databaseIP = url + "/"; + }else{ + this->databaseIP = url; + } +} + +bool InfluxDB::TestingConnection(){ + CheckDatabases(); + if( respond != CURLE_OK ) return false; + return true; +} + +std::string InfluxDB::CheckDatabases(){ + curl_easy_setopt(curl, CURLOPT_POST, 1); + + curl_easy_setopt(curl, CURLOPT_URL, (databaseIP + "query").c_str()); + + std::string postFields="q=Show databases"; + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast(postFields.length())); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postFields.c_str()); + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallBack); + std::string readBuffer; + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); + + Execute(); + + //printf("|%s|\n", readBuffer.c_str()); + + if( respond != CURLE_OK) return ""; + + databaseList.clear(); + + size_t pos = readBuffer.find("values"); + + if( pos > 0 ){ + std::string kaka = readBuffer.substr(pos+8); + + pos = kaka.find("}"); + kaka = kaka.substr(0, pos); + + int len = kaka.length(); + bool startFlag = false; + std::string lala; + + char yaya = '"'; + + for( int i = 0; i < len; i++){ + + if( startFlag == false && kaka[i] == yaya ) { + startFlag = true; + lala = ""; + continue; + } + + if( startFlag && kaka[i] == yaya ){ + startFlag = false; + databaseList.push_back(lala); + continue; + } + if( startFlag ) lala += kaka[i]; + } + } + + return readBuffer; +} + +std::string InfluxDB::Query(std::string databaseName, std::string query){ + + curl_easy_setopt(curl, CURLOPT_POST, 1); + + curl_easy_setopt(curl, CURLOPT_URL, (databaseIP + "query?db=" + databaseName).c_str()); + + std::string postFields = "q=" + query; + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast(postFields.length())); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postFields.c_str()); + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallBack); + std::string readBuffer; + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); + + Execute(); + + //printf("|%s|\n", readBuffer.c_str()); + + return readBuffer; +} + +void InfluxDB::CreateDatabase(std::string databaseName){ + curl_easy_setopt(curl, CURLOPT_URL, (databaseIP + "query").c_str()); + curl_easy_setopt(curl, CURLOPT_POST, 1); + + std::string postFields = "q=CREATE DATABASE " + databaseName; + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast(postFields.length())); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postFields.c_str()); + + Execute(); +} + +void InfluxDB::AddDataPoint(std::string fullString){ + dataPoints += fullString + "\n"; +} + +void InfluxDB::ClearDataPointsBuffer(){ + dataPoints = ""; +} + +void InfluxDB::PrintDataPoints(){ + printf("%s\n", dataPoints.c_str()); +} + +void InfluxDB::WriteData(std::string databaseName){ + if( dataPoints.length() == 0 ) return; + //printf("|%s|\n", (databaseIP + "write?db=" + databaseName).c_str()); + curl_easy_setopt(curl, CURLOPT_URL, (databaseIP + "write?db=" + databaseName).c_str()); + curl_easy_setopt(curl, CURLOPT_POST, 1L); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast(dataPoints.length())); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, dataPoints.c_str()); + Execute(); +} + + +void InfluxDB::Execute(){ + try{ + respond = curl_easy_perform(curl); + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &respondCode); + //printf("==== respond %d (OK = %d)\n", respond, CURLE_OK); + if( respond != CURLE_OK) printf("############# InfluxDB::Execute fail\n"); + } catch (std::exception& e){ // in case of unexpected error + printf("%s\n", e.what()); + respond = CURLE_SEND_ERROR; + } +} + +size_t InfluxDB::WriteCallBack(char *contents, size_t size, size_t nmemb, void *userp){ + ((std::string*)userp)->append((char*)contents, size * nmemb); + return size * nmemb; +} diff --git a/influxdb.h b/influxdb.h new file mode 100644 index 0000000..1f4277f --- /dev/null +++ b/influxdb.h @@ -0,0 +1,58 @@ +#ifndef INFLUXDB_H +#define INFLUXDB_H + +#include +#include +#include +#include +#include + +class InfluxDB{ + private: + + bool isURLValid; + + CURL * curl; + CURLcode respond; + long respondCode; + + std::string databaseIP; + std::string dataPoints; + + std::vector databaseList; + + static size_t WriteCallBack(char *contents, size_t size, size_t nmemb, void *userp); + + void Execute(); + + public: + + InfluxDB(std::string url, bool verbose = false); + ~InfluxDB(); + + void SetURL(std::string url); + bool TestingConnection(); + bool IsURLValid() const {return isURLValid;} + + /// Query + std::string CheckDatabases(); /// this save the list of database into databaseList + std::string Query(std::string databaseName, std::string query); + + /// the CheckDatabases() function must be called before + std::vector GetDatabaseList() {return databaseList;} + + void CreateDatabase(std::string databaseName); + + /// for single or batch write, + /// 1, addDataPoint first, you can add as many as you like + /// 2, writeData. + void AddDataPoint(std::string fullString); + unsigned int GetDataLength() const {return dataPoints.length();} + void ClearDataPointsBuffer(); + void PrintDataPoints(); + void WriteData(std::string databaseName); + bool IsWriteOK() const {return (respond == CURLE_OK) ? true: false;} + +}; + +#endif