diff --git a/ConditionMonitoring/CustomStringUtilities.cpp b/ConditionMonitoring/CustomStringUtilities.cpp new file mode 100644 index 0000000..63d90ff --- /dev/null +++ b/ConditionMonitoring/CustomStringUtilities.cpp @@ -0,0 +1,21 @@ +#include "CustomStringUtilities.h" +#include + +const char* CustomStringUtilities::typeOfWhitespacesReturn = " \t\n\r\f\v"; +const char* CustomStringUtilities::typeOfWhitespaces = " \t\r\f\v"; + +void CustomStringUtilities::removeAllWhitespaces(std::string& str, bool trimReturn) { + const char* whitespaces = trimReturn ? typeOfWhitespacesReturn : typeOfWhitespaces; + size_t pos, offset = 0; + while ((pos = str.find_first_of(whitespaces, offset)) != std::string::npos) { + str.erase(pos, 1); + offset = pos; + } +} + +void CustomStringUtilities::trim(std::string& str) { + str.erase(str.find_last_not_of(typeOfWhitespaces) + 1); + str.erase(0, str.find_first_not_of(typeOfWhitespaces)); +} + + diff --git a/ConditionMonitoring/CustomStringUtilities.h b/ConditionMonitoring/CustomStringUtilities.h new file mode 100644 index 0000000..7af2c7c --- /dev/null +++ b/ConditionMonitoring/CustomStringUtilities.h @@ -0,0 +1,14 @@ +#pragma once +#include + +class CustomStringUtilities +{ +private: + static const char* typeOfWhitespaces; + static const char* typeOfWhitespacesReturn; +public: + //remove whitespaces from the passed string + static void removeAllWhitespaces(std::string& str, bool trimReturn = true); + static void trim(std::string&); +}; + diff --git a/ConditionMonitoring/DataAcquisition.cpp b/ConditionMonitoring/DataAcquisition.cpp new file mode 100644 index 0000000..06198dc --- /dev/null +++ b/ConditionMonitoring/DataAcquisition.cpp @@ -0,0 +1,163 @@ +#include "DataAcquisition.h" +#include "easylogging++.h" +#include +#include +#include + +std::atomic_bool modbusThreadRunning = false; +std::atomic_bool modbusThreadCancelled = false; + +//Modbus worker thread, lock and notification variable +std::condition_variable cvar_modbus_queue; +std::mutex mux_modbus_queue; + +DataAcquisition::DataAcquisition() : dataModel(DataModel::Instance()) +{ +} + +DataAcquisition::~DataAcquisition() +{ + LOG(INFO) << "Wait until modbus worker queue finishes..."; + modbusThreadCancelled = true; + cvar_modbus_queue.notify_one(); + if(modbusThread.size() != 0 && modbusThread[0].joinable()){ + modbusThread[0].join(); + modbusThread.clear(); + } +} + + +//Modbus thread +//Execute the enqueued modbus register requests +void modbusWorkerThread(DataAcquisition* this_p) { + LOG(INFO) << "Modbus Worker Thread started"; + auto& queue = this_p->getModbusQueue(); + auto& publishers = this_p->getPublishers(); + bool first = true; + try { + //vector of connections which failed to open + std::vector failedConnections; + + while (1) { + + if (modbusThreadCancelled) + return; + + + //If this is the first execution or the thread has + //been notified after being in sleep mode + if (queue.size() == 0 || first){ + first = false; + failedConnections.clear(); + //No work to do, wait for something to be enqueued + std::unique_lock threadLock(mux_modbus_queue); + + //Wait till notification (happens when some registers are enqueued + cvar_modbus_queue.wait(threadLock); + + if (modbusThreadCancelled) + return; + + //open all of the registered connections + for(unsigned int i = 0; i < publishers.size(); i++){ + if(!publishers[i]->open()){ + //write id to list if connection failed + failedConnections.push_back(publishers[i]->getID()); + } + } + + } + + if (modbusThreadCancelled) + return; + + //Get next parameter from the queue + ParameterSpecification modbusParameter = queue.pop_front(); + + //Look if the connection is in the list of the failed connections, if so , skip this parameter + if(std::find(failedConnections.begin(), failedConnections.end(), modbusParameter.connection->getID()) != failedConnections.end()){ + continue; + } + + //Skip parameter if not a reading parameter or connection is not open + if(!modbusParameter.isReadable()) + continue; + + switch (modbusParameter.length) { + case 1: + modbusParameter.connection->readBit(modbusParameter); + break; + case 8: + modbusParameter.connection->readByte(modbusParameter); + break; + case 16: + modbusParameter.connection->readRegister(modbusParameter); + break; + case 32: + modbusParameter.connection->readDRegister(modbusParameter); + break; + case 64: + modbusParameter.connection->readQRegister(modbusParameter); + break; + default: + modbusParameter.connection->readBits(modbusParameter); + } + + if(modbusParameter.error){ + //Rading of Modbus Parameter was not successful + LOG(WARNING) << std::dec + << "[Modbus " << modbusParameter.connection->getID() << ": " << modbusParameter.connection->getConnectionType() << "] " + << "Failed reading parameter "<< (int)modbusParameter.description + << " at 0x" + << std::hex << std::setfill('0') << std::setw(4) + << modbusParameter.address; + } + else{ + + //LOG(INFO) << std::dec + // << "[Modbus " << modbusParameter.connection->getConnectionType() << " "<< + // modbusParameter.connection->getID() << "]" << " Readed param " << (int)modbusParameter.description + // << " value: " << value; + + DataModel::Instance()->saveModbusParameter(std::move(modbusParameter)); + + } + + } + + } + catch (std::exception& e) { + LOG(FATAL) << "Error in modbus access, shutting down modbus thread: " << e.what(); + } + modbusThreadRunning = false; +} + + + + +void DataAcquisition::startModbusThread() { + if (modbusThreadRunning == false) + { + modbusThreadRunning = true; + modbusThread.push_back(std::thread(modbusWorkerThread, this)); + } +} + + +//Registers publisher and moves ownership to DataAcquisition class +void DataAcquisition::registerPublisher(std::unique_ptr publisher) { + publishers.push_back(std::move(publisher)); +} + +void DataAcquisition::enqueuePublisherRegister(){ + for (size_t i = 0; i < publishers.size(); i++) { + //Collects the alert modbus registers and enqueue them + publishers[i]->enqueueReadingRegisters(modbusAccessQueue, Category::Alert); + } + for (size_t i = 0; i < publishers.size(); i++) { + //Collects the condition modbus registers and enqueue them + publishers[i]->enqueueReadingRegisters(modbusAccessQueue, Category::Condition); + } + cvar_modbus_queue.notify_one(); +} + diff --git a/ConditionMonitoring/DataAcquisition.h b/ConditionMonitoring/DataAcquisition.h new file mode 100644 index 0000000..c1dd027 --- /dev/null +++ b/ConditionMonitoring/DataAcquisition.h @@ -0,0 +1,38 @@ +#pragma once +#include "DataModel.h" +#include "PublisherInterface.h" +#include +#include +#include +#include "ts_queue.h" +#include "modbus_interface_lib.h" + +class DataAcquisition +{ +private: + std::unique_ptr& dataModel; + + //Modbus Connections for POC and Bender, can be serial or IP + std::vector> publishers; + + //Modbus thread + std::vector modbusThread; + + ts_queue modbusAccessQueue; + + +public: + DataAcquisition(); + ~DataAcquisition(); + + + void startModbusThread(); + ts_queue& getModbusQueue() { return modbusAccessQueue; } + + //void registerPublisher(Publisher* publisher); + void registerPublisher(std::unique_ptr publisher); + + std::vector>& getPublishers() { return publishers; } + + void enqueuePublisherRegister(); +}; diff --git a/ConditionMonitoring/DataModel.cpp b/ConditionMonitoring/DataModel.cpp new file mode 100644 index 0000000..60af67a --- /dev/null +++ b/ConditionMonitoring/DataModel.cpp @@ -0,0 +1,364 @@ +#include "DataModel.h" +#include + +#include +#include +#include "SystemConfig.h" +#include +#include + +#include "PublisherPowercenter.h" +#include "PublisherBenderRcm.h" + +std::chrono::milliseconds timeoutMS = std::chrono::milliseconds(10'000); + +long DataModel::permanentParamHistory = 0; +int DataModel::narrowBlock = 0; + +//Initialization of static member +std::unique_ptr DataModel::instance = nullptr; + +//Read file locker +std::timed_mutex accessFilesMTX; + + +constexpr unsigned long long DataModel::timepoint_to_sec_long(const std::chrono::system_clock::time_point& t) { + return duration_to_sec_long(t.time_since_epoch()); +} + +const ts_map &DataModel::getPublisherData() const +{ + return temporaryStorage; +} + +CappedStorage* DataModel::getPermanentData(ModbusRegister reg){ + auto it = permanentStorage.find(reg); + if(it != permanentStorage.end()){ + return &(it->second); + } + else + return nullptr; +} + +constexpr unsigned long long DataModel::duration_to_sec_long(const std::chrono::system_clock::duration& t) { + return std::chrono::duration_cast(t).count(); +} + +inline void DataModel::Create() { + instance = std::make_unique(); + + permanentParamHistory = SystemConfig::getIntConfigParameter("permanent_param_history"); + narrowBlock = SystemConfig::getIntConfigParameter("narrow_block"); +} + +DataModel::~DataModel() +{ + LOG(INFO) << "Save locally stored permanent data to permanent storage..."; + //Save permanent data to storage + long long time = std::chrono::system_clock::now().time_since_epoch().count(); + std::fstream file; + std::stringstream ss; + ss << dataDir << "data_" << time; + std::string filename = ss.str(); + file.open(filename, std::ios::out); + + if (file.is_open()) { + for(auto &storage : permanentStorage){ + if(storage.second.size() == 0) + continue; + file << (int)storage.first << ":" << std::endl; + file << storage.second << std::endl; + } + file.close(); + } + else + LOG(ERROR) << "Could not access file to store permanent data..."; + +} + + + +std::unique_ptr& DataModel::Instance() { + if (!instance) + Create(); + return instance; +} + +void DataModel::Destroy() { + if (instance) + instance.reset(); + +} + + +void DataModel::makePermanent(ModbusRegister reg, bool biased) +{ + permanentStorage.emplace(reg, CappedStorage(biased)); +} + + + +void DataModel::saveModbusParameter(ParameterSpecification param) +{ + if(param.cat == Category::Alert){ + short bitmask = 0; + std::memcpy(&bitmask, param.readedBytes.data(), param.length); + //Found an alert here + if(bitmask != 0) + LOG(WARNING) << "Received an alert from Modbus " << param.connection->getConnectionType() << ", ID " << param.connection->getID(); + alerts.push(param); + return; + } + + //Temporary storage of last value + temporaryStorage.emplaceOrOverwrite(param.description, SavedData(param.cat, param.readedBytes)); + + //If parameter is listet to be stored permanently + auto res = permanentStorage.find(param.description); + if(res != permanentStorage.end()){ + checkForFlushData(); + res->second.store(param.readedBytes); + } +} + +void DataModel::checkForFlushData() { + + while(1){ + auto permStorageIter = permanentStorage.begin(); + auto storageIter = permanentStorage.find(permStorageIter->first); + auto& X = storageIter->second.getX(); + auto& y = storageIter->second.getY(); + //Rescale Matrix if data exceeds time limit + storageIter->second.lock(); + if(X.size() > 1 && (X(0,1) <= (X(X.rows()-1, 1) - DataModel::permanentParamHistory))){ + if(X.size() <= narrowBlock) + return; + LOG(INFO) << "Shrink permanent storage of parameter " << std::dec << (int)permStorageIter->first; + std::stringstream ss; + ss << (int)storageIter->first << "(X);"; + Eigen::Matrix temp(X.rows()-narrowBlock, X.cols()); + temp = X.block(narrowBlock, 0, X.rows() - narrowBlock, X.cols()); + //backup capped data to flush to file + for(int i = 0; i < narrowBlock-1; i++){ + ss << std::fixed << std::setprecision(0) << X(i, X.cols()-1) << ";"; + } + + X.resize(X.rows() - narrowBlock, X.cols()); + X = std::move(temp); + + ss << std::fixed << std::setprecision(0) << std::endl << (int)storageIter->first << "(y);"; + + temp.resize(y.rows()-narrowBlock, 1); + temp = y.block(narrowBlock, 0, y.rows() - narrowBlock, 1); + //backup capped data to flush to file + for(int i = 0; i < narrowBlock-1; i++){ + ss << std::fixed << std::setprecision(5) << y(i) << ";"; + } + ss << std::endl; + y.resize(y.rows() - narrowBlock, 1); + y = std::move(temp); + + flush(ss); + } + storageIter->second.unlock(); + if((++permStorageIter) == permanentStorage.end()) + break; + } +} + + +bool DataModel::flush(std::stringstream& ss) +{ + //Data file lock condition + if (!accessFilesMTX.try_lock_for(timeoutMS)) { + LOG(ERROR) << "Timeout after waiting " << (long long)timeoutMS.count() << " for reading data files"; + return false; + } + + LOG(INFO) << "Flush data to local file"; + std::fstream file; + auto now = std::chrono::system_clock::now(); + std::stringstream filename; + filename << dataDir << "data_" << now.time_since_epoch().count(); + file.open(filename.str(), std::ios_base::out); + if (file.is_open()) { + file << ss.str(); + file.close(); + } + else + LOG(ERROR) << "Could not open file for writing"; + accessFilesMTX.unlock(); + return true; +} + +//reads in the log file to the supplied stringstream +//define fromTime to get only logs older than fromTime (seconds after epoch) +uintmax_t DataModel::readLogFile(std::stringstream& ss, long long fromTime) { + el::Loggers::flushAll(); + + uintmax_t sizeRead = 0; + + std::filesystem::path p{ logDir }; + std::ifstream file; + std::string lineBuffer; + std::filesystem::directory_iterator iterator(p); + for (auto& currentFile : std::filesystem::directory_iterator(p)) + if (currentFile.is_regular_file()) { + if (logFileName.compare(currentFile.path().filename().string()) == 0) { + file.open(currentFile.path()); + if (file.is_open()) { + sizeRead += currentFile.file_size(); + bool foundTimePoint = false; + while (std::getline(file, lineBuffer)) { + if (lineBuffer.size() == 0) + continue; + if (fromTime != 0 && foundTimePoint == false) { + std::stringstream temp; + temp << lineBuffer; + std::tm tm{}; + temp >> std::get_time(&tm, dateFormatLogger.c_str()); + auto timePoint = std::chrono::system_clock::from_time_t(std::mktime(&tm)); + if (timePoint.time_since_epoch().count() >= fromTime) { + foundTimePoint = true; + } + } + else + ss << lineBuffer << std::endl; + } + file.close(); + LOG(INFO) << "Readed log file"; + } + else + LOG(WARNING) << "Couldn't open LOG file for writing"; + break; + } + } + //If size read is 0, no tile at specified index found or file was empty + return sizeRead; +} + + +uintmax_t DataModel::readAllDataFiles(std::stringstream& ss) { + uintmax_t sizeRead = 0; + + if (!accessFilesMTX.try_lock_for(timeoutMS)) { + LOG(ERROR) << "Timeout after waiting " << (long long)timeoutMS.count() << " for reading data files - blocked by another thread"; + return 0; + } + std::filesystem::path p{ dataDir }; + std::ifstream file; + std::string lineBuffer; + std::filesystem::directory_iterator iterator(p); + for (auto& currentFile : std::filesystem::directory_iterator(p)){ + if (currentFile.is_regular_file() && std::regex_match(currentFile.path().filename().string(), regexPatternFile)) { + //look for a valid file with the specified index + file.open(currentFile.path()); + if (file.is_open()) { + sizeRead += currentFile.file_size(); + while (std::getline(file, lineBuffer)) { + ss << lineBuffer << std::endl; + } + file.close(); + std::filesystem::remove(currentFile.path()); + } + } + } + accessFilesMTX.unlock(); + //If size read is 0, no tile at specified index found or file was empty + return sizeRead; +} + +std::stringstream& DataModel::readPermanentData(std::stringstream& buffer, bool retain){ + for(auto &e: permanentStorage){ + buffer << (int)e.first << ":" << std::endl; + buffer << e.second; + } + if(!retain) + permanentStorage.clear(); + return buffer; +} + +ts_queue& DataModel::getAlerts() +{ + return alerts; +} + +std::stringstream& DataModel::readTemporaryData(std::stringstream& buffer){ + for(auto &e: temporaryStorage){ + buffer << (int)e.first << ":"; + buffer << e.second; + } + return buffer; +} + +//olderThan: files which are older than this specified time are deleted +unsigned long DataModel::removeStoredData(seconds olderThan) { + using namespace std::filesystem; + auto timeNow = duration_cast(system_clock::now().time_since_epoch()); + u_int filesDeleted = 0; + for (auto& file : directory_iterator(path("data/"))) { + if (file.is_regular_file()) { + if (std::regex_match(file.path().stem().string(), regexPatternFile)) { + std::string str = file.path().stem().string(); + str = str.substr(str.find_first_of("0123456789", 0)); + //time of file in seconds after 01/01/1970 + long long timeOfFile = 0; + //if (auto [p, ec] = std::from_chars(str.data(), str.data() + str.length(), timeOfFile); ec == std::errc()) + timeOfFile = std::stoll(str, 0); + if ((timeOfFile + olderThan.count()) < timeNow.count()) + if(remove(file)) + filesDeleted++; + } + } + } + LOG(INFO) << "Deleted data files (" << filesDeleted << ") that were older than " << (long long)olderThan.count() <<"seconds on local storage"; + return filesDeleted; +} + +//remove every file that are stored locally +unsigned long DataModel::removeStoredData() { + using namespace std::filesystem; + create_directory("data/"); + unsigned long filesDeleted = 0; + try{ + + for (auto& file : directory_iterator(path("data/"))) { + if (file.is_regular_file()) { + if (std::regex_match(file.path().stem().string(), regexPatternFile)) { + std::string str = file.path().stem().string(); + str = str.substr(str.find_first_of("0123456789", 0)); + filesDeleted++; + } + } + } + } + catch(std::exception& e){ + LOG(ERROR) << "Can't access data directory"; + } + LOG(INFO) << "Deleted all data files (" << filesDeleted << ") on local storage"; + return filesDeleted; +} + + + +std::ostream& operator<<(std::ostream& os, const PublisherType type) { + switch (type) { + case PublisherType::RCMS_BENDER: + os << "Bender RCMS"; + break; + case PublisherType::POWERCENTER: + os << "Siemens Powercenter"; + break; + default: + os << ""; + break; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, const SavedData& savedData){ + float value = 0; + std::memcpy(&value, savedData.data.data(), 4); + os << value << std::endl; + return os; +} diff --git a/ConditionMonitoring/DataModel.h b/ConditionMonitoring/DataModel.h new file mode 100644 index 0000000..a2795fe --- /dev/null +++ b/ConditionMonitoring/DataModel.h @@ -0,0 +1,129 @@ +#pragma once +#include +#include +#include "PublisherData.h" +#include +#include +#include +#include +#include +#include "ModbusDataPOC.h" +#include "ModbusDataBender.h" +#include "ts_map.h" +#include "cappedstorage.h" + +const Eigen::MatrixXd dummyMat(0,0); +const Eigen::VectorXd dummyVec(0); + + +struct SavedData{ + Category cat; + std::vector data; + + SavedData(const Category c, const std::vector d){ + cat = c; + data = d; + } + friend std::ostream& operator<<(std::ostream& os, const SavedData& savedData); + +}; + + + +class PublisherInterface; + +constexpr auto READ_BUFFER_SIZE = 4096; + +typedef std::vector> float_matrix; +//Type for one dimensional permanent double values +typedef ts_map permanent_data; + +using namespace std::chrono; + +class DataModel +{ +public: + //Sigleton Methods + static std::unique_ptr& Instance(); + static void Destroy(); + static void Create(); + + ~DataModel(); + + //save Data inside application + void savePublishedData(PublisherData&&); + + void makePermanent(ModbusRegister reg, bool biased); + + void saveModbusParameter(ParameterSpecification param); + + //getter + std::vector getSavedPublishedDataIndexes(const u_int id, const seconds newerThan); + + + //---local file storage methods--- + //Stores in-programm data to local file system + bool flush(std::stringstream &ss); + + uintmax_t readLogFile(std::stringstream& ss, long long fromTime); + + //Reads in from all files their content to ss, or a certain file, specified by its index + //TODO: define readSince to read al data after that time point + //size_t readSingleFile(std::stringstream& ss, const size_t index); + uintmax_t readFromAllFiles(std::stringstream& ss, std::chrono::system_clock::time_point readSince = std::chrono::system_clock::from_time_t(0)); + uintmax_t readAllDataFiles(std::stringstream& ss); + + unsigned long removeStoredData(seconds olderThan); + unsigned long removeStoredData(); + + //saves collected data after a certain amount of time points + void checkForFlushData(); + + unsigned long long getStartTime(const unsigned int id); + unsigned long long getStartTime(); + const char* getDataDir() { return dataDir; } + const std::regex& getRegexPatternFile() { return regexPatternFile; } + + constexpr unsigned long long duration_to_sec_long(const std::chrono::system_clock::duration& t); + constexpr unsigned long long timepoint_to_sec_long(const std::chrono::system_clock::time_point& t); + + const ts_map &getPublisherData() const; + CappedStorage* getPermanentData(ModbusRegister reg); + + std::stringstream &readTemporaryData(std::stringstream &buffer); + std::stringstream &readPermanentData(std::stringstream &buffer, bool retain); + ts_queue &getAlerts(); + +private: + //Register to enqueue data wich are stored permanently + permanent_data permanentStorage; + //Temporary storage of the last readed parameter + ts_map temporaryStorage; + //Temporary saved alerts + ts_queue alerts; + + static long permanentParamHistory; + static int narrowBlock; + + const std::regex regexPatternFile = std::regex("data_\\w+"); + const std::string logFileName = "data_analysis.log"; + const std::string dateFormatLogger = "%Y-%m-%d %H:%M:%S"; + u_int nrOfDataPoints = 0; + + //static instance which can be accessed by calling DataModel::Instance() + static std::unique_ptr instance; + + //local directory path to the data storage + const char* dataDir = "data/"; + const char* logDir = "log/"; + + + //Data from publishers, one elemt in outer vector for one id + //Each id consists of a vector of PublisherData (use map to make the id become a unique key) + + + + //ostream operators (read and write dataModel data from streams) + friend std::ostream& operator<<(std::ostream& os, DataModel& dataModel); + friend std::istream& operator>>(std::istream& is, DataModel& dataModel); +}; diff --git a/ConditionMonitoring/Evaluator.cpp b/ConditionMonitoring/Evaluator.cpp new file mode 100644 index 0000000..d2101d8 --- /dev/null +++ b/ConditionMonitoring/Evaluator.cpp @@ -0,0 +1,37 @@ +#include "Evaluator.h" +#include +#include "SystemConfig.h" + + +Evaluator::Evaluator() +{ + //initialize Linear Regression Analysis on Residual Current Register + CappedStorage* storageP = DataModel::Instance()->getPermanentData(ModbusRegister::BENDER_Residual_current); + if(storageP != nullptr){ + MLAlgorithms.push_back(std::make_unique(storageP)); + } + else + LOG(ERROR) << "Tried to invoke ML-Algorithm on non permanent or non existing parameter storage"; + +} + +inline std::ostream& operator<<(std::ostream& os, std::vector& vec) { + std::for_each(vec.begin(), vec.end(), [&os](float a) {os << a << " "; }); + return os; +} + + +//update all registered models +std::vector Evaluator::evaluate() +{ + std::vector alerts; + for(auto &a: MLAlgorithms){ + MLAlert alert = a->updateModel(); + if(alert.type != CustomAlertTypes::NO_ALERT) + alerts.push_back(alert); + } + for(auto &a: alerts) + LOG(WARNING) << a.message; + return alerts; +} + diff --git a/ConditionMonitoring/Evaluator.h b/ConditionMonitoring/Evaluator.h new file mode 100644 index 0000000..fb9e9f3 --- /dev/null +++ b/ConditionMonitoring/Evaluator.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include "MLAnalyzer.h" +#include "MLLinReg.h" +#include "MLAlert.h" +#include +#include + +class Evaluator +{ +private: + + //Enqueued Algorithms to perform data analysis + std::vector> MLAlgorithms; + + +public: + Evaluator(); + + //Gets called if new data is available + std::vector evaluate(); + +}; + diff --git a/ConditionMonitoring/MLAlert.h b/ConditionMonitoring/MLAlert.h new file mode 100644 index 0000000..b8a570b --- /dev/null +++ b/ConditionMonitoring/MLAlert.h @@ -0,0 +1,17 @@ +#ifndef MLALERT_H +#define MLALERT_H +#include + +enum CustomAlertTypes{ + NO_ALERT, + CRIT_RCM_TENDENCY, +}; + +struct MLAlert{ + CustomAlertTypes type; + std::string message; + + MLAlert(CustomAlertTypes type, std::string message): type(type), message(message){} +}; + +#endif // MLALERT_H diff --git a/ConditionMonitoring/MLAnalyzer.cpp b/ConditionMonitoring/MLAnalyzer.cpp new file mode 100644 index 0000000..18df6b2 --- /dev/null +++ b/ConditionMonitoring/MLAnalyzer.cpp @@ -0,0 +1,42 @@ +#include "MLAnalyzer.h" +#include +#include "DataModel.h" +#include "SystemConfig.h" + +MLAnalyzer::MLAnalyzer(CappedStorage *storage) : + n(storage->getN()), m(storage->getM()), data(storage), updateRate(SystemConfig::getIntConfigParameter("update_model_rate")) +{ +} + + + +void MLAnalyzer::addData(Eigen::VectorXd Px, double Py) +{ + Eigen::Matrix temp; + temp << Py; + addData(Px, temp); +} + + + +void MLAnalyzer::addData(Eigen::MatrixXd Px, Eigen::VectorXd Py) +{ + //if(Px.cols() != n || (Px.rows() != Py.rows())){ + // std::stringstream ss; + // ss << "Invalid Matrix dimensions: "; + // throw ss.str(); + //} + // + //X.conservativeResize(X.rows()+Px.rows(), Eigen::NoChange_t()); + //y.conservativeResize(y.rows()+Py.rows(), Eigen::NoChange_t()); + // + //X.block(m, 0, Px.rows(), 1) = Eigen::MatrixXd::Ones(Px.rows(), 1); + //X.block(m, 1, Px.rows(), Px.cols()) = Px; + // + //m += X.rows(); + // + //if(++currentBatch >= trainAfterNPoints){ + // updateModel(); + // currentBatch = 0; + //} +} diff --git a/ConditionMonitoring/MLAnalyzer.h b/ConditionMonitoring/MLAnalyzer.h new file mode 100644 index 0000000..b75eb6c --- /dev/null +++ b/ConditionMonitoring/MLAnalyzer.h @@ -0,0 +1,37 @@ +#ifndef MLANALYZER_H +#define MLANALYZER_H +#include +#include +#include +#include "DataModel.h" +#include "MLAlert.h" + +//Interface for Machine Learning algorithms +class MLAnalyzer +{ +protected: + std::chrono::milliseconds updateTime; + + const long long n; + long long m; + unsigned short currentBatch = 0; + const unsigned short trainAfterNPoints = 10; + + //Data references + CappedStorage* data; + + const int updateRate; + int updateCounter = 0; + +public: + + MLAnalyzer(CappedStorage *storage); + + void addData(Eigen::VectorXd Px, double Py); + void addData(Eigen::MatrixXd Px, Eigen::VectorXd Py); + + virtual MLAlert updateModel() = 0; +}; + + +#endif // MLANALYZER_H diff --git a/ConditionMonitoring/MLLinReg.cpp b/ConditionMonitoring/MLLinReg.cpp new file mode 100644 index 0000000..8609f1b --- /dev/null +++ b/ConditionMonitoring/MLLinReg.cpp @@ -0,0 +1,74 @@ +#include "MLLinReg.h" +#include "SystemConfig.h" +#include +#include +#include + +MLLinReg::MLLinReg(CappedStorage *storage) : MLAnalyzer(storage) +{ + criticalResidualCurrent = SystemConfig::getFloatConfigParameter("crit_residual_current"); + criticalTimeRangeSec = SystemConfig::getIntConfigParameter("crit_residual_timerange") * SECONDS_IN_WEEK; + + Theta.resize(2, 1); +} + + + +MLAlert MLLinReg::updateModel() +{ + if(t0 == 0 && data->getX().rows() != 0) {LOG(INFO) << std::setprecision(12) << std::fixed << "t_0 = " << (data->getX())(0, 1); t0 = (data->getX())(0, 1);} + if(++updateCounter < updateRate) + return MLAlert(CustomAlertTypes::NO_ALERT, ""); + updateCounter = 0; + LOG(INFO) << "Update Linear Regression Model"; + normalEquation(); + bool error = checkCritical(); + + if(error){ + //Return an alert object if tendency is critical + std::stringstream ans; + ans << "Received critical tendency in the next " << std::round(criticalTimeRangeSec/SECONDS_IN_WEEK) << " seconds"; + + //LOG(INFO) << "Theta values: " << Theta(0) << " " << Theta(1); + //if(first){ + // std::ofstream file; + // std::stringstream ss; + // file.open("debug_lin_reg.txt", std::ios::out); + // if(file.is_open()){ + // file << std::setprecision(12) << std::fixed << "Theta: " << Theta(0) << " " << Theta(1) << std::endl; + // file << std::setprecision(12) << std::fixed << "X: " << data->getX()(data->getX().rows()-1, 1) << std::endl; + // file << std::setprecision(12) << std::fixed << "y: " << data->getY()(data->getY().rows()-1) << std::endl; + // file << std::setprecision(12) << std::fixed << "t0: " << t0 << std::endl; + // file << std::setprecision(12) << std::fixed << std::endl; + // file.close(); + // } + // first = false; + //} + return MLAlert(CustomAlertTypes::CRIT_RCM_TENDENCY, ans.str()); + } + else + return MLAlert(CustomAlertTypes::NO_ALERT, ""); +} + + +bool MLLinReg::checkCritical(){ + //Check, if the critical value is reached within the specified time range + if(data->getM() == 0) + return false; + //Offset, to avoid checking with little data and receive faulty results + if(data->getM() < 100) + return false; + return (Theta(0) + Theta(1) * ((data->getX())((data->getX()).rows()-1, 1) + criticalTimeRangeSec)) + >= criticalResidualCurrent; +} + + +//Numerical approach to solve univariate Linear Regression Model +void MLLinReg::normalEquation() { + Theta = Eigen::MatrixXd::Zero(2, 1); + Theta = (data->getX().transpose() * data->getX()).inverse() * data->getX().transpose() * data->getY(); + if(data->getX().rows() > 500){ + LOG(INFO) << Theta(0) << " " << Theta(1); + LOG(INFO) << data->getX().rows(); + } +} diff --git a/ConditionMonitoring/MLLinReg.h b/ConditionMonitoring/MLLinReg.h new file mode 100644 index 0000000..1f48366 --- /dev/null +++ b/ConditionMonitoring/MLLinReg.h @@ -0,0 +1,23 @@ +#ifndef MLLINREG_H +#define MLLINREG_H +#include "MLAnalyzer.h" + +constexpr int SECONDS_IN_WEEK = 7*24*60*60; + +class MLLinReg: public MLAnalyzer +{ +private: + Eigen::MatrixXd Theta; + void normalEquation(); + bool first = true; + + long t0 = 0; + float criticalResidualCurrent; + int criticalTimeRangeSec; +public: + MLLinReg(CappedStorage*); + MLAlert updateModel() override; + bool checkCritical(); +}; + +#endif // MLLINREG_H diff --git a/ConditionMonitoring/ModbusDataBender.cpp b/ConditionMonitoring/ModbusDataBender.cpp new file mode 100644 index 0000000..7d2ebe2 --- /dev/null +++ b/ConditionMonitoring/ModbusDataBender.cpp @@ -0,0 +1,10 @@ +#include "ModbusDataBender.h" + +ModbusDataBender::ModbusDataBender(const unsigned int id) : ModbusDataInterface(id) +{ + //Store Modbus reading registers + //Identification + //Dummy parameter to simulate continous reading + modbusParamFP64.emplace_back(ModbusRegister::BENDER_Residual_current, 100, Access::R, Category::Condition); + +} diff --git a/ConditionMonitoring/ModbusDataBender.h b/ConditionMonitoring/ModbusDataBender.h new file mode 100644 index 0000000..fb1de8d --- /dev/null +++ b/ConditionMonitoring/ModbusDataBender.h @@ -0,0 +1,11 @@ +#pragma once +#include "ModbusDataInterface.h" + + +class ModbusDataBender : public ModbusDataInterface +{ +public: + ModbusDataBender(const unsigned int id); + +}; + diff --git a/ConditionMonitoring/ModbusDataInterface.cpp b/ConditionMonitoring/ModbusDataInterface.cpp new file mode 100644 index 0000000..44dfc8a --- /dev/null +++ b/ConditionMonitoring/ModbusDataInterface.cpp @@ -0,0 +1,20 @@ +#include "ModbusDataInterface.h" +#include + +/* +Collects all ModbusRegister which fulfill the specified category + cat: specified category to look for + queue: reference to a thread safe queue, which contains the readable registers +return: the queue with the enqueued values +*/ +ts_queue& ModbusDataInterface::modbusRegisterCat(const Category cat, ts_queue& queue, std::unique_ptr& connection) +{ + std::for_each(modbusParamFP32.begin(), modbusParamFP32.end(), [&queue, cat, &connection](const auto& p) { if (cat == p.getCategory()) queue.push(p.getSpecification(connection)); }); + std::for_each(modbusParamFP64.begin(), modbusParamFP64.end(), [&queue, cat, &connection](const auto& p) { if (cat == p.getCategory()) queue.push(p.getSpecification(connection)); }); + std::for_each(modbusParamU16.begin(), modbusParamU16.end(), [&queue, cat, &connection](const auto& p) { if (cat == p.getCategory()) queue.push(p.getSpecification(connection)); }); + std::for_each(modbusParamU32.begin(), modbusParamU32.end(), [&queue, cat, &connection](const auto& p) { if (cat == p.getCategory()) queue.push(p.getSpecification(connection)); }); + std::for_each(modbusParamS16.begin(), modbusParamS16.end(), [&queue, cat, &connection](const auto& p) { if (cat == p.getCategory()) queue.push(p.getSpecification(connection)); }); + std::for_each(modbusParamString.begin(), modbusParamString.end(), [&queue, cat, &connection](const auto& p) { if (cat == p.getCategory()) queue.push(p.getSpecification(connection)); }); + + return queue; +} diff --git a/ConditionMonitoring/ModbusDataInterface.h b/ConditionMonitoring/ModbusDataInterface.h new file mode 100644 index 0000000..66456cb --- /dev/null +++ b/ConditionMonitoring/ModbusDataInterface.h @@ -0,0 +1,34 @@ +#pragma once +#include +#include +#include +#include +#include +#include "ts_queue.h" +#include "ParameterDouble.h" +#include "ParameterFloat.h" +#include "ParameterUInt16.h" +#include "ParameterUInt32.h" +#include "ParameterCharP.h" +#include "ParameterS16.h" + +class ModbusInterface; + +class ModbusDataInterface +{ +private: + const unsigned int id; +protected: + std::vector modbusParamFP32; + std::vector modbusParamFP64; + std::vector modbusParamU16; + std::vector modbusParamU32; + std::vector modbusParamS16; + std::vector modbusParamString; +public: + ModbusDataInterface(const unsigned int id) : id(id) { } + + ts_queue& modbusRegisterCat(const Category cat, ts_queue& queue, std::unique_ptr &connection); + + void readAll(); +}; diff --git a/ConditionMonitoring/ModbusDataPOC.cpp b/ConditionMonitoring/ModbusDataPOC.cpp new file mode 100644 index 0000000..b8888cc --- /dev/null +++ b/ConditionMonitoring/ModbusDataPOC.cpp @@ -0,0 +1,247 @@ +#include "ModbusDataPOC.h" + + +std::string ModbusDataPOC::getStatusMessage(unsigned short statusID) +{ + switch (statusID) + { + case 1: + return "Breaker off (without realeasing)"; + case 2: + return "Breaker normal (on)"; + case 3: + return "Breaker released (off)"; + case 4: + return "Breaker released (Lever on and blocked)"; + default: + return "Status unknown"; + } +} + + + +ModbusDataPOC::ModbusDataPOC(const unsigned int id) : ModbusDataInterface(id){ + +//Store Modbus reading registers + //Identification + modbusParamU16.emplace_back(ModbusRegister::POC_Ident_Manufacturer, 0x0002, Access::R); + modbusParamString.emplace_back(ModbusRegister::POC_Ident_Ordernumber, 0x0003, 20, Access::R); + modbusParamString.emplace_back(ModbusRegister::POC_Ident_Seriesnumber, 0x000D, 16, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Ident_Hardware_Version, 0x0015, Access::R); + modbusParamString.emplace_back(ModbusRegister::POC_Ident_Software_Version, 0x0016, 4, Access::R); + modbusParamString.emplace_back(ModbusRegister::POC_Ident_Plant_identification_code, 0x001D, 32, Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_Ident_Installation_site, 0x002D, 22, Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_Ident_Installation_date, 0x0038, 16, Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_Ident_Firmware_Applicationcontroller, 0x0045, 10, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Ident_Market, 0x005E, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Ident_Main_device_rated_current, 0x005F, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Ident_Tripping_curve_characteristic, 0x0060, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Ident_Installation_place_fuse, 0x0062, Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_Ident_MLFB_fuse, 0x0063, 20, Access::RW); + modbusParamU16.emplace_back(ModbusRegister::POC_Ident_Hardware_Electronics, 0x006D, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Ident_rated_current_melting_part, 0x006E, Access::RW); + + //IP + modbusParamString.emplace_back(ModbusRegister::POC_IP_Ethernet_MAC, 0x0200, 6, Access::R); + modbusParamU32.emplace_back(ModbusRegister::POC_IP_Status_DHCP, 0x0203, Access::RW); + modbusParamU32.emplace_back(ModbusRegister::POC_IP_SNTP_server_ip, 0x0205, Access::RW); + modbusParamU32.emplace_back(ModbusRegister::POC_IP_SNTP_client_mode, 0x0207, Access::RW); + modbusParamU32.emplace_back(ModbusRegister::POC_IP_Status_firewall, 0x0209, Access::RW); + modbusParamU32.emplace_back(ModbusRegister::POC_IP_port_number, 0x020D, Access::RW); + modbusParamU32.emplace_back(ModbusRegister::POC_IP_Static_IP, 0x020F, Access::RW); + modbusParamU32.emplace_back(ModbusRegister::POC_IP_Subnetmask, 0x0211, Access::RW); + modbusParamU32.emplace_back(ModbusRegister::POC_IP_Gateway, 0x029D, Access::RW); + modbusParamU32.emplace_back(ModbusRegister::POC_IP_Current_IP, 0x029F, Access::R); + modbusParamU32.emplace_back(ModbusRegister::POC_IP_Current_Subnet, 0x02A1, Access::R); + modbusParamU32.emplace_back(ModbusRegister::POC_IP_Current_Gateway, 0x02A3, Access::R); + + //Bluetooth + modbusParamU16.emplace_back(ModbusRegister::POC_BT_Status, 0x0301, Access::R); + modbusParamS16.emplace_back(ModbusRegister::POC_BT_send_power, 0x0302, Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_BT_device_address, 0x0303, 6, Access::R); + modbusParamU32.emplace_back(ModbusRegister::POC_BT_passkey, 0x0306, Access::RW); + + //Radio + modbusParamU32.emplace_back(ModbusRegister::POC_Radio_Date_time_utc, 0x02A3, Access::RW); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_1, 0x0402, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_2, 0x0403, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_3, 0x0404, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_4, 0x0405, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_5, 0x0406, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_6, 0x0407, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_7, 0x0408, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_8, 0x0409, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_9, 0x040A, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_10, 0x040B, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_11, 0x040C, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_12, 0x040D, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_13, 0x040E, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_14, 0x040F, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_15, 0x0410, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_16, 0x0411, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_17, 0x0412, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_18, 0x0413, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_19, 0x0414, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_20, 0x0415, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_21, 0x0416, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_22, 0x0417, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_23, 0x0418, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Paring_status_24, 0x0419, Access::R); + modbusParamS16.emplace_back(ModbusRegister::POC_Radio_transmit_power, 0x041A, Access::RW); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_1, 0x041B, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_2, 0x041C, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_3, 0x041D, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_4, 0x041E, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_5, 0x041F, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_6, 0x0420, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_7, 0x0421, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_8, 0x0422, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_9, 0x0423, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_10, 0x0424, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_11, 0x0425, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_12, 0x0426, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_13, 0x0427, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_14, 0x0428, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_15, 0x0429, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_16, 0x042A, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_17, 0x042B, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_18, 0x042C, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_19, 0x042D, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_20, 0x042E, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_21, 0x042F, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_22, 0x0430, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_23, 0x0431, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Device_status_24, 0x0432, Access::R); + modbusParamU16.emplace_back(ModbusRegister::POC_Radio_Time_sync_to_POC, 0x0433, Access::R); + + //Radio Communication + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_1, 0x0609, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_2, 0x0616, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_3, 0x0623, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_4, 0x0630, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_5, 0x063D, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_6, 0x064A, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_7, 0x0657, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_8, 0x0664, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_9, 0x0671, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_10, 0x067E, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_11, 0x068B, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_12, 0x0698, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_13, 0x06A5, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_14, 0x06B2, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_15, 0x06BF, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_16, 0x06CC, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_17, 0x06D9, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_18, 0x06E6, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_19, 0x06F3, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_20, 0x0700, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_21, 0x070D, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_22, 0x071A, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_23, 0x0727, 8 , Access::RW); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Mac_address_device_24, 0x0734, 8 , Access::RW); + + //Measurement and Condition + modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_Temp, 0x0C00 ,Access::R, Category::Condition); + modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_Temp_avg, 0x0C02 ,Access::R, Category::Condition); + modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_I_Phase, 0x0C04 ,Access::R, Category::Condition); + modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_I_Phase_avg, 0x0C06 ,Access::R, Category::Condition); + modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_I_Phase_max, 0x0C08 ,Access::R, Category::Condition); + modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_U_L_N, 0x0C0A ,Access::R, Category::Condition); + modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_Freq, 0x0C0C ,Access::R, Category::Condition); + modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_P, 0x0C0E ,Access::R, Category::Condition); + modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_S, 0x0C10 ,Access::R, Category::Condition); + modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_Q, 0x0C12 ,Access::R, Category::Condition); + modbusParamFP32.emplace_back(ModbusRegister::POC_Cond_Cos_phi, 0x0C14 ,Access::R, Category::Condition); + modbusParamFP64.emplace_back(ModbusRegister::POC_Cond_P_in, 0x0C16 ,Access::R, Category::Condition); + modbusParamFP64.emplace_back(ModbusRegister::POC_Cond_P_ab, 0x0C1A ,Access::R, Category::Condition); + modbusParamFP64.emplace_back(ModbusRegister::POC_Cond_S_in, 0x0C1E ,Access::R, Category::Condition); + modbusParamFP64.emplace_back(ModbusRegister::POC_Cond_S_ab, 0x0C22 ,Access::R, Category::Condition); + modbusParamU16.emplace_back(ModbusRegister::POC_Cond_Status, 0x0C26 ,Access::R, Category::Condition); + + //Measurement settings + modbusParamU32.emplace_back(ModbusRegister::POC_Measurement_Time_period_temperature, 0x0E02 ,Access::RW ); + modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_Activate_temp_alert, 0x0E04 ,Access::RW ); + modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_Limit_temperature_alert, 0x0E05 ,Access::RW ); + modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_Hysteresis_temperature_alert, 0x0E07 ,Access::RW ); + modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_I_Averaging_interval_s, 0x0E0D ,Access::RW ); + modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_I_alert1_over_on_off, 0x0E0E ,Access::RW ); + modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_I_alert1_over_limit_percentage, 0x0E0F ,Access::RW ); + modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_I_alert1_over_hysteresis, 0x0E11 ,Access::RW ); + modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_I_alert2_over_on_off, 0x0E17 ,Access::RW ); + modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_I_alert2_over_limit_percentage, 0x0E18 ,Access::RW ); + modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_I_alert2_over_hysteresis, 0x0E1A ,Access::RW ); + modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_I_alert1_under_on_off, 0x0E20 ,Access::RW ); + modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_I_alert1_under_limit_percentage, 0x0E21 ,Access::RW ); + modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_I_alert1_under_hysteresis, 0x0E23 ,Access::RW ); + modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_I_alert2_under_on_off, 0x0E29 ,Access::RW ); + modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_I_alert2_under_limit_percentage, 0x0E2A ,Access::RW ); + modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_I_alert2_under_hysteresis, 0x0E2C ,Access::RW ); + modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_U_alert1_over_on_off, 0x0E32 ,Access::RW ); + modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_U_alert1_over_limit_percentage, 0x0E33 ,Access::RW ); + modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_U_alert1_over_hysteresis, 0x0E35 ,Access::RW ); + modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_U_alert2_over_on_off, 0x0E3B ,Access::RW ); + modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_U_alert2_over_limit_percentage, 0x0E3C ,Access::RW ); + modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_U_alert2_over_hysteresis, 0x0E3E ,Access::RW ); + modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_U_alert1_under_on_off, 0x0E44 ,Access::RW ); + modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_U_alert1_under_limit_percentage, 0x0E45 ,Access::RW ); + modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_U_alert1_under_hysteresis, 0x0E47 ,Access::RW ); + modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_U_alert2_under_on_off, 0x0E4D ,Access::RW ); + modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_U_alert2_under_limit_percentage, 0x0E4E ,Access::RW ); + modbusParamFP32.emplace_back(ModbusRegister::POC_Measurement_U_alert2_under_hysteresis, 0x0E50 ,Access::RW ); + modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_Energy_flow_direction, 0x0E5A ,Access::RW ); + modbusParamU16.emplace_back(ModbusRegister::POC_Measurement_Alert_on_off_AFDD_threshold_shortfall, 0x0E5C ,Access::RW ); + + + //Diagnosis + modbusParamU32.emplace_back(ModbusRegister::POC_Diag_Alert, 0x0A00 ,Access::R , Category::Alert); + modbusParamFP64.emplace_back(ModbusRegister::POC_Diag_H_run_with_I, 0x0A02 ,Access::R , Category::Diagnosis); + modbusParamU16.emplace_back(ModbusRegister::POC_Diag_Alert_on_off_run_total_with_I, 0x0A06 ,Access::RW , Category::Diagnosis); + modbusParamU16.emplace_back(ModbusRegister::POC_Diag_Min_load_current, 0x0A07 ,Access::RW , Category::Diagnosis); + modbusParamFP64.emplace_back(ModbusRegister::POC_Diag_Limit_run_hours_with_I_alert, 0x0A08 ,Access::RW , Category::Diagnosis); + modbusParamFP64.emplace_back(ModbusRegister::POC_Diag_H_run_total, 0x0A12 ,Access::R , Category::Diagnosis); + modbusParamU16.emplace_back(ModbusRegister::POC_Diag_Alert_on_off_run_total, 0x0A16 ,Access::RW , Category::Diagnosis); + modbusParamFP64.emplace_back(ModbusRegister::POC_Diag_Limit_run_hours_total_alert, 0x0A17 ,Access::RW , Category::Diagnosis); + modbusParamFP32.emplace_back(ModbusRegister::POC_Diag_Nr_of_mechanical_switchings, 0x0A21 ,Access::R , Category::Diagnosis); + modbusParamU16.emplace_back(ModbusRegister::POC_Diag_Alert_on_off_nr_mechanical_switchings, 0x0A23 ,Access::RW , Category::Diagnosis); + modbusParamFP32.emplace_back(ModbusRegister::POC_Diag_Limit_mechanical_switchings, 0x0A24 ,Access::RW , Category::Diagnosis); + modbusParamFP32.emplace_back(ModbusRegister::POC_Diag_Nr_of_triggered_switches, 0x0A2A ,Access::R , Category::Diagnosis); + modbusParamU16.emplace_back(ModbusRegister::POC_Diag_Alert_on_off_nr_triggered_switchings, 0x0A2C ,Access::RW , Category::Diagnosis); + modbusParamFP32.emplace_back(ModbusRegister::POC_Diag_Limit_triggered_switchings, 0x0A2D ,Access::RW , Category::Diagnosis); + modbusParamS16.emplace_back(ModbusRegister::POC_Diag_RSSI_BLE, 0x0A3D ,Access::R , Category::Diagnosis); + modbusParamS16.emplace_back(ModbusRegister::POC_Diag_RSSI_radio, 0x0A3E ,Access::R , Category::Diagnosis); + modbusParamFP32.emplace_back(ModbusRegister::POC_Diag_Nr_of_short_circuit_triggers, 0x0A40 ,Access::R , Category::Diagnosis); + modbusParamU16.emplace_back(ModbusRegister::POC_Diag_Alert_on_off_nr_short_circuit_triggers, 0x0A42 ,Access::RW , Category::Diagnosis); + modbusParamFP32.emplace_back(ModbusRegister::POC_Diag_Nr_of_short_circuit_triggers, 0x0A43 ,Access::RW , Category::Diagnosis); + modbusParamString.emplace_back(ModbusRegister::POC_Diag_Time_and_sync_status, 0x0A45, 8 ,Access::R , Category::Diagnosis); + + + //Radio Communication + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_1, 0x0600, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_2, 0x060D, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_3, 0x061A, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_4, 0x0627, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_5, 0x0634, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_6, 0x0641, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_7, 0x064E, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_8, 0x065B, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_9, 0x0668, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_10, 0x0675, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_11, 0x0682, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_12, 0x068F, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_13, 0x069C, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_14, 0x06A9, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_15, 0x06B6, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_16, 0x06C3, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_17, 0x06D0, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_18, 0x06DD, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_19, 0x06EA, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_20, 0x06F7, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_21, 0x0704, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_22, 0x0711, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_23, 0x071E, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Installation_Code_24, 0x072B, 18, Access::W); + modbusParamString.emplace_back(ModbusRegister::POC_RadioCom_Paring_Device, 0x0738, 18, Access::W); + +} + + diff --git a/ConditionMonitoring/ModbusDataPOC.h b/ConditionMonitoring/ModbusDataPOC.h new file mode 100644 index 0000000..02e9477 --- /dev/null +++ b/ConditionMonitoring/ModbusDataPOC.h @@ -0,0 +1,20 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include "ModbusDataInterface.h" + + + +class ModbusDataPOC : public ModbusDataInterface +{ +private: + +public: + ModbusDataPOC(const unsigned int id); + + static std::string getStatusMessage(unsigned short statusID); +}; diff --git a/ConditionMonitoring/ModbusInterface.cpp b/ConditionMonitoring/ModbusInterface.cpp new file mode 100644 index 0000000..1a96635 --- /dev/null +++ b/ConditionMonitoring/ModbusInterface.cpp @@ -0,0 +1,213 @@ +#include "ModbusInterface.h" +#include "ModbusInterface.h" +#include +#include + +void ModbusInterface::assignSlave() { + my_modbus->addSlave(slaveAddressID); + my_slave = my_modbus->slavePtr(slaveAddressID); +} + +ModbusInterface::~ModbusInterface() { + if(my_modbus != nullptr){ + my_modbus->close(); + delete my_modbus; + } +} + +bool ModbusInterface::openConnection() { + modbus_init(); + //if(!(my_modbus->isOpen())) + open = my_modbus->open(); + if(!open){ + LOG(ERROR) << "Couldn't open Modbus " << getConnectionType() << + " connection at " << device; + LOG(ERROR) << my_modbus->lastError(); + } + return open; +} + +void ModbusInterface::disconnect() { + if(my_modbus != nullptr) + my_modbus->close(); +} + +bool ModbusInterface::readBit(ParameterSpecification ¶m) { + if(!param.connection->getIsOpen()){ + param.error = true; + return false; + } + bool buf; + if (my_slave->readCoil(param.address, buf) != 1) { + param.error = true; + return 0; + } + else{ + param.readedBytes.push_back(buf); + param.error = false; + return buf; + } +} + + +bool ModbusInterface::readBits(ParameterSpecification ¶m) { + if(!param.connection->getIsOpen()){ + param.error = true; + return false; + } + bool bufBool[param.length*8]; + int ans = my_slave->readCoils(param.address, bufBool, param.length*8); + if (ans != param.length*8) { + param.error = true; + return 0; + } + else{ + //Big Endian decryption + for(unsigned int i = 0; i < param.length; i++){ + uint8_t byte = 0; + for(unsigned int j = 0; j < 8; j++){ + byte += bufBool[i*8 +j] << j; + } + param.readedBytes.push_back(byte); + } + param.error = false; + return true; + } +} + +uint8_t ModbusInterface::readByte(ParameterSpecification ¶m) { + if(!param.connection->getIsOpen()){ + param.error = true; + return false; + } + uint16_t buf; + if (my_slave->readRegister(param.address, buf) != 1) { + param.error = true; + return 0; + } + else{ + param.readedBytes.push_back(buf & 0x00FF); + param.error = false; + return static_cast(buf & 0x00FF); + } +} + +uint16_t ModbusInterface::readRegister(ParameterSpecification ¶m) { + if(!param.connection->getIsOpen()){ + param.error = true; + return false; + } + uint16_t buf; + if (my_slave->readRegister(param.address, buf) != 1) { + param.error = true; + return 0; + } + else{ + param.readedBytes.push_back(buf); + param.error = false; + return buf; + } +} + +uint32_t ModbusInterface::readDRegister(ParameterSpecification ¶m) { + if(!param.connection->getIsOpen()){ + param.error = true; + return false; + } + uint16_t buf[2]; + + if (my_slave->readRegisters(param.address, buf, 2) != 2) { + param.error = true; + return 0; + } + else{ + param.readedBytes.push_back(*buf); + param.readedBytes.push_back(*(buf+1)); + param.error = false; + return _MODBUS_GET_INT32_FROM_INT16(buf, 0); + } +} + +uint64_t ModbusInterface::readQRegister(ParameterSpecification ¶m) { + if(!param.connection->getIsOpen()){ + param.error = true; + return false; + } + + uint16_t buf[4]; + int ans = my_slave->readRegisters(param.address, buf, 4); + if (ans != 4) { + param.error = true; + return 0; + } + else{ + param.readedBytes.push_back(*buf); + param.readedBytes.push_back(*(buf+1)); + param.readedBytes.push_back(*(buf+2)); + param.readedBytes.push_back(*(buf+3)); + param.error = false; + return _MODBUS_GET_INT64_FROM_INT16(buf, 0); + } +} + +bool ModbusInterface::writeBit(const uint16_t address, bool bit) +{ + if(my_slave->writeCoil(address, bit) != 1){ + return false; + } + else + return true; +} + +bool ModbusInterface::writeByte(const uint16_t address, uint8_t byte) +{ + bool buf[8]; + for(int i = 0; i < 8; i++){ + buf[i] = byte & 0x01; + byte = byte > 1; + } + + if(my_slave->writeCoils(address, buf, 8) != 1){ + return false; + } + else + return true; +} + +bool ModbusInterface::writeRegister(const uint16_t address, uint16_t word) +{ + if(my_slave->writeRegister(address, word) != 1){ + return false; + } + else + return true; +} + +bool ModbusInterface::writeDRegister(const uint16_t address, uint32_t dword) +{ + uint16_t data[2]; + data[0] = (dword & 0x0000FFFF); + data[0] = (dword & 0xFFFF0000)>>16; + + if(my_slave->writeRegisters(address, data, 2) != 1){ + return false; + } + else + return true; +} + +bool ModbusInterface::writeQRegister(const uint16_t address, uint64_t qword) +{ + uint16_t data[4]; + + data[0] = (qword & 0x0000'0000'0000'FFFF); + data[1] = (qword & 0x0000'0000'FFFF'0000) >> 16; + data[2] = (qword & 0x0000'FFFF'0000'0000) >> 32; + data[3] = (qword & 0xFFFF'0000'0000'0000) >> 48; + + if(my_slave->writeRegisters(address, data, 4) != 1){ + return false; + } + else + return true; +} diff --git a/ConditionMonitoring/ModbusInterface.h b/ConditionMonitoring/ModbusInterface.h new file mode 100644 index 0000000..23bf11d --- /dev/null +++ b/ConditionMonitoring/ModbusInterface.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include "ParameterInterface.h" + +class ModbusInterface { +private: + unsigned int slaveAddressID; + bool open; +protected: + Modbus::Master* my_modbus = nullptr; + Modbus::Slave* my_slave = nullptr; + + //IP (TCP) or serial port (RTU) + std::string device; + + void assignSlave(); + +public: + ModbusInterface(u_int _id, std::string _device) : slaveAddressID(_id), device(_device) { }; + virtual ~ModbusInterface(); + virtual void modbus_init() = 0; + virtual std::string getConnectionType() = 0; + + bool openConnection(); + void disconnect(); + bool getIsOpen() const { return open; } + + unsigned int getID(){ return slaveAddressID; } + + //Read and write functions + bool readBit(ParameterSpecification ¶m); + bool readBits(ParameterSpecification ¶m); + uint8_t readByte(ParameterSpecification ¶m); + uint16_t readRegister(ParameterSpecification ¶m); + uint32_t readDRegister(ParameterSpecification ¶m); + uint64_t readQRegister(ParameterSpecification ¶m); + + bool writeBit(const uint16_t address, bool bit); + bool writeByte(const uint16_t address, uint8_t byte); + bool writeRegister(const uint16_t address, uint16_t word); + bool writeDRegister(const uint16_t address, uint32_t dword); + bool writeQRegister(const uint16_t address, uint64_t qword); +}; + + +#define _MODBUS_GET_INT64_FROM_INT16(tab_int16, index) \ + (((int64_t)tab_int16[(index) ] << 48) + \ + ((int64_t)tab_int16[(index) + 1] << 32) + \ + ((int64_t)tab_int16[(index) + 2] << 16) + \ + (int64_t)tab_int16[(index) + 3]) +#define _MODBUS_GET_INT32_FROM_INT16(tab_int16, index) ((tab_int16[(index)] << 16) + tab_int16[(index) + 1]) +#define _MODBUS_GET_INT16_FROM_INT8(tab_int8, index) ((tab_int8[(index)] << 8) + tab_int8[(index) + 1]) diff --git a/ConditionMonitoring/ModbusRegister.h b/ConditionMonitoring/ModbusRegister.h new file mode 100644 index 0000000..6730975 --- /dev/null +++ b/ConditionMonitoring/ModbusRegister.h @@ -0,0 +1,283 @@ +#pragma once + + +enum class Category { + //Category for collective reading accesses + NONE, + Condition, + Diagnosis, + Alert +}; + + +enum class ModbusRegister { + //---------------------Powercenter---------------------------- + //Information for identification + POC_Ident_Manufacturer, + POC_Ident_Ordernumber, + POC_Ident_Seriesnumber, + POC_Ident_Hardware_Version, + POC_Ident_Software_Version, + POC_Ident_Plant_identification_code, + POC_Ident_Installation_site, + POC_Ident_Installation_date, + POC_Ident_Firmware_Applicationcontroller, + POC_Ident_Market, + POC_Ident_Main_device_rated_current, + POC_Ident_Tripping_curve_characteristic, + POC_Ident_Installation_place_fuse, + POC_Ident_MLFB_fuse, + POC_Ident_Hardware_Electronics, + POC_Ident_rated_current_melting_part, + //IP Information + POC_IP_Ethernet_MAC, + POC_IP_Status_DHCP, + POC_IP_SNTP_server_ip, + POC_IP_SNTP_client_mode, + POC_IP_Status_firewall, + POC_IP_port_number, + POC_IP_Static_IP, + POC_IP_Subnetmask, + POC_IP_Gateway, + POC_IP_Current_IP, + POC_IP_Current_Subnet, + POC_IP_Current_Gateway, + //Bluetooth Information + POC_BT_Status, + POC_BT_send_power, + POC_BT_device_address, + POC_BT_passkey, + //Radio Information + POC_Radio_Date_time_utc, + POC_Radio_Paring_status_1, + POC_Radio_Paring_status_2, + POC_Radio_Paring_status_3, + POC_Radio_Paring_status_4, + POC_Radio_Paring_status_5, + POC_Radio_Paring_status_6, + POC_Radio_Paring_status_7, + POC_Radio_Paring_status_8, + POC_Radio_Paring_status_9, + POC_Radio_Paring_status_10, + POC_Radio_Paring_status_11, + POC_Radio_Paring_status_12, + POC_Radio_Paring_status_13, + POC_Radio_Paring_status_14, + POC_Radio_Paring_status_15, + POC_Radio_Paring_status_16, + POC_Radio_Paring_status_17, + POC_Radio_Paring_status_18, + POC_Radio_Paring_status_19, + POC_Radio_Paring_status_20, + POC_Radio_Paring_status_21, + POC_Radio_Paring_status_22, + POC_Radio_Paring_status_23, + POC_Radio_Paring_status_24, + POC_Radio_transmit_power, + POC_Radio_Device_status_1, + POC_Radio_Device_status_2, + POC_Radio_Device_status_3, + POC_Radio_Device_status_4, + POC_Radio_Device_status_5, + POC_Radio_Device_status_6, + POC_Radio_Device_status_7, + POC_Radio_Device_status_8, + POC_Radio_Device_status_9, + POC_Radio_Device_status_10, + POC_Radio_Device_status_11, + POC_Radio_Device_status_12, + POC_Radio_Device_status_13, + POC_Radio_Device_status_14, + POC_Radio_Device_status_15, + POC_Radio_Device_status_16, + POC_Radio_Device_status_17, + POC_Radio_Device_status_18, + POC_Radio_Device_status_19, + POC_Radio_Device_status_20, + POC_Radio_Device_status_21, + POC_Radio_Device_status_22, + POC_Radio_Device_status_23, + POC_Radio_Device_status_24, + POC_Radio_Time_sync_to_POC, + //Radio Communication Information + POC_RadioCom_Mac_address_device_1, + POC_RadioCom_Mac_address_device_2, + POC_RadioCom_Mac_address_device_3, + POC_RadioCom_Mac_address_device_4, + POC_RadioCom_Mac_address_device_5, + POC_RadioCom_Mac_address_device_6, + POC_RadioCom_Mac_address_device_7, + POC_RadioCom_Mac_address_device_8, + POC_RadioCom_Mac_address_device_9, + POC_RadioCom_Mac_address_device_10, + POC_RadioCom_Mac_address_device_11, + POC_RadioCom_Mac_address_device_12, + POC_RadioCom_Mac_address_device_13, + POC_RadioCom_Mac_address_device_14, + POC_RadioCom_Mac_address_device_15, + POC_RadioCom_Mac_address_device_16, + POC_RadioCom_Mac_address_device_17, + POC_RadioCom_Mac_address_device_18, + POC_RadioCom_Mac_address_device_19, + POC_RadioCom_Mac_address_device_20, + POC_RadioCom_Mac_address_device_21, + POC_RadioCom_Mac_address_device_22, + POC_RadioCom_Mac_address_device_23, + POC_RadioCom_Mac_address_device_24, + + //Measurement Settings + POC_Measurement_Time_period_temperature, + POC_Measurement_Activate_temp_alert, + POC_Measurement_Limit_temperature_alert, + POC_Measurement_Hysteresis_temperature_alert, + POC_Measurement_I_Averaging_interval_s, + //Current Alerts + POC_Measurement_I_alert1_over_on_off, + POC_Measurement_I_alert1_over_limit_percentage, + POC_Measurement_I_alert1_over_hysteresis, + POC_Measurement_I_alert2_over_on_off, + POC_Measurement_I_alert2_over_limit_percentage, + POC_Measurement_I_alert2_over_hysteresis, + POC_Measurement_I_alert1_under_on_off, + POC_Measurement_I_alert1_under_limit_percentage, + POC_Measurement_I_alert1_under_hysteresis, + POC_Measurement_I_alert2_under_on_off, + POC_Measurement_I_alert2_under_limit_percentage, + POC_Measurement_I_alert2_under_hysteresis, + //Voltage Alerts + POC_Measurement_U_alert1_over_on_off, + POC_Measurement_U_alert1_over_limit_percentage, + POC_Measurement_U_alert1_over_hysteresis, + POC_Measurement_U_alert2_over_on_off, + POC_Measurement_U_alert2_over_limit_percentage, + POC_Measurement_U_alert2_over_hysteresis, + POC_Measurement_U_alert1_under_on_off, + POC_Measurement_U_alert1_under_limit_percentage, + POC_Measurement_U_alert1_under_hysteresis, + POC_Measurement_U_alert2_under_on_off, + POC_Measurement_U_alert2_under_limit_percentage, + POC_Measurement_U_alert2_under_hysteresis, + + POC_Measurement_Energy_flow_direction, + POC_Measurement_Alert_on_off_AFDD_threshold_shortfall, + + //Switch gears condition + POC_Cond_Temp, //°C + POC_Cond_Temp_avg, //°C + POC_Cond_I_Phase, //A + POC_Cond_I_Phase_avg, //A + POC_Cond_I_Phase_max, //A + POC_Cond_U_L_N, //V + POC_Cond_Freq, //Hz + POC_Cond_P, //W + POC_Cond_S, //W + POC_Cond_Q, //Var + POC_Cond_Cos_phi, + POC_Cond_P_in, //Wh + POC_Cond_P_ab, //Wh + POC_Cond_S_in, //Varh + POC_Cond_S_ab, //Varh + POC_Cond_Status, + + //Powercenter Diagnosis + POC_Diag_Alert, + //Hours run with current flow + POC_Diag_H_run_with_I, //s + POC_Diag_Alert_on_off_run_total_with_I, + POC_Diag_Min_load_current, + POC_Diag_Limit_run_hours_with_I_alert, + POC_Diag_H_run_total, //s + POC_Diag_Alert_on_off_run_total, + POC_Diag_Limit_run_hours_total_alert, + POC_Diag_Nr_of_mechanical_switchings, + POC_Diag_Alert_on_off_nr_mechanical_switchings, + POC_Diag_Limit_mechanical_switchings, + POC_Diag_Nr_of_triggered_switches, + POC_Diag_Alert_on_off_nr_triggered_switchings, + POC_Diag_Limit_triggered_switchings, + //Received Signal Strength Indicator in dbm + POC_Diag_RSSI_BLE, + POC_Diag_RSSI_radio, + POC_Diag_Nr_of_short_circuit_triggers, + POC_Diag_Alert_on_off_nr_short_circuit_triggers, + POC_Diag_Limit_shoer_circuit_triggers, + POC_Diag_Time_and_sync_status, + + //Radio communication settings + POC_RadioCom_Installation_Code_1, + POC_RadioCom_Installation_Code_2, + POC_RadioCom_Installation_Code_3, + POC_RadioCom_Installation_Code_4, + POC_RadioCom_Installation_Code_5, + POC_RadioCom_Installation_Code_6, + POC_RadioCom_Installation_Code_7, + POC_RadioCom_Installation_Code_8, + POC_RadioCom_Installation_Code_9, + POC_RadioCom_Installation_Code_10, + POC_RadioCom_Installation_Code_11, + POC_RadioCom_Installation_Code_12, + POC_RadioCom_Installation_Code_13, + POC_RadioCom_Installation_Code_14, + POC_RadioCom_Installation_Code_15, + POC_RadioCom_Installation_Code_16, + POC_RadioCom_Installation_Code_17, + POC_RadioCom_Installation_Code_18, + POC_RadioCom_Installation_Code_19, + POC_RadioCom_Installation_Code_20, + POC_RadioCom_Installation_Code_21, + POC_RadioCom_Installation_Code_22, + POC_RadioCom_Installation_Code_23, + POC_RadioCom_Installation_Code_24, + + POC_RadioCom_Paring_Device, + + POC_RadioCom_Paring_device_1, + POC_RadioCom_Paring_device_2, + POC_RadioCom_Paring_device_3, + POC_RadioCom_Paring_device_4, + POC_RadioCom_Paring_device_5, + POC_RadioCom_Paring_device_6, + POC_RadioCom_Paring_device_7, + POC_RadioCom_Paring_device_8, + POC_RadioCom_Paring_device_9, + POC_RadioCom_Paring_device_10, + POC_RadioCom_Paring_device_11, + POC_RadioCom_Paring_device_12, + POC_RadioCom_Paring_device_13, + POC_RadioCom_Paring_device_14, + POC_RadioCom_Paring_device_15, + POC_RadioCom_Paring_device_16, + POC_RadioCom_Paring_device_17, + POC_RadioCom_Paring_device_18, + POC_RadioCom_Paring_device_19, + POC_RadioCom_Paring_device_20, + POC_RadioCom_Paring_device_21, + POC_RadioCom_Paring_device_22, + POC_RadioCom_Paring_device_23, + POC_RadioCom_Paring_device_24, + + + //---------------------Bender---------------------------- + //Test variable to be read from virtual RCM device + BENDER_Residual_current, +}; + + +namespace CMD { + enum class Identification { + Trigger_flash_light, + }; + + enum class IP { + Apply_ethernet_configuration_changes, + }; + + enum class Bluetooth { + Switch_on_off_BT_Reset_passkey, + }; + + enum class MeasurementSettings { + Reset_energy_counter, + Reset_extrem_values, + }; +} diff --git a/ConditionMonitoring/ModbusRtu.cpp b/ConditionMonitoring/ModbusRtu.cpp new file mode 100644 index 0000000..a484d9b --- /dev/null +++ b/ConditionMonitoring/ModbusRtu.cpp @@ -0,0 +1,31 @@ +#include "ModbusRtu.h" +#include + +inline std::string ModbusRTU::getSettingsString() const +{ + return std::to_string(baud) + pairity + std::to_string(stopBit); +} + +ModbusRTU::~ModbusRTU() +{ + //if(my_slave != nullptr){ + // delete my_slave; + //} + //if(my_modbus != nullptr){ + // my_modbus->close(); + // delete my_modbus; + //} +} + +ModbusRTU::ModbusRTU(const u_int id, const std::string _device, const unsigned int _baud, const char _pairity, const unsigned int _stopBit) + : ModbusInterface(id, _device), baud(_baud), pairity(_pairity), stopBit(_stopBit) +{ + +} + +void ModbusRTU::modbus_init() { + if(my_modbus == nullptr){ + my_modbus = new Modbus::Master(Modbus::Rtu, device, getSettingsString()); + assignSlave(); + } +} diff --git a/ConditionMonitoring/ModbusRtu.h b/ConditionMonitoring/ModbusRtu.h new file mode 100644 index 0000000..98a8010 --- /dev/null +++ b/ConditionMonitoring/ModbusRtu.h @@ -0,0 +1,26 @@ +#pragma once +#include +#include "ModbusInterface.h" + +class ModbusRTU : + public ModbusInterface +{ +private: + unsigned int baud; + char pairity; + unsigned int stopBit; + + + + //Create settings string + std::string getSettingsString() const; + +public: + ModbusRTU(const u_int id, const std::string _device, const u_int _baud, const char _pairity, const u_int _stopBit); + virtual ~ModbusRTU(); + virtual void modbus_init() override; + + std::string getConnectionType()override { return "Rtu"; } + +}; + diff --git a/ConditionMonitoring/ModbusTcp.cpp b/ConditionMonitoring/ModbusTcp.cpp new file mode 100644 index 0000000..aa99186 --- /dev/null +++ b/ConditionMonitoring/ModbusTcp.cpp @@ -0,0 +1,25 @@ +#include "ModbusTcp.h" + +ModbusTCP::ModbusTCP(const u_int id, const std::string _ip, const unsigned int _port) + : ModbusInterface(id, _ip), port(std::to_string(_port)) +{ + +} + +ModbusTCP::~ModbusTCP() +{ + //if(my_slave != nullptr){ + // delete my_slave; + //} + //if(my_modbus != nullptr){ + // my_modbus->close(); + // delete my_modbus; + //} +} + +void ModbusTCP::modbus_init() { + if (my_modbus == nullptr){ + my_modbus = new Modbus::Master(Modbus::Tcp, device, port); + assignSlave(); + } +} diff --git a/ConditionMonitoring/ModbusTcp.h b/ConditionMonitoring/ModbusTcp.h new file mode 100644 index 0000000..1139042 --- /dev/null +++ b/ConditionMonitoring/ModbusTcp.h @@ -0,0 +1,20 @@ +#pragma once +#include +#include "ModbusInterface.h" + +class ModbusTCP : + public ModbusInterface +{ +private: + + std::string port = "502"; + +public: + ModbusTCP(const u_int id, const std::string _ip, const u_int _port); + virtual ~ModbusTCP(); + virtual void modbus_init() override; + + std::string getConnectionType()override { return "Tcp"; } + +}; + diff --git a/ConditionMonitoring/NetServer.cpp b/ConditionMonitoring/NetServer.cpp new file mode 100644 index 0000000..d1b11d9 --- /dev/null +++ b/ConditionMonitoring/NetServer.cpp @@ -0,0 +1,128 @@ +#include "NetServer.h" +#include +#include "DataModel.h" + +void NetServer::Stop() +{ + net::ServerInterface::Stop(); + LOG(INFO) << "[SERVER] Stopped Server"; + serverRunning = false; +} + +bool NetServer::Start() +{ + if(serverRunning){ + LOG(WARNING) << "[SERVER] tried to start ip tcp server, but it is already running"; + return true; + } + + bool res = net::ServerInterface::Start(); + if(res){ + LOG(INFO) << "[SERVER] Started Server"; + serverRunning = true; + return res; + } + LOG(ERROR) << "[SERVER] Couldn't start Server"; + serverRunning = false; + return res; +} + +bool NetServer::OnClientConnect(std::shared_ptr> client) +{ + //Decide whether to accept or deny connection (always accept in this case) + net::Message msg; + msg.header.id = net::MessageTypes::ServerAccept; + client->Send(msg); + return true; +} + + +// Called when a client appears to have disconnected +void NetServer::OnClientDisconnect(std::shared_ptr> client) +{ + LOG(INFO) << "Removing client [" << client->GetID() << "]"; +} + + +// Called when a message arrives +void NetServer::OnMessage(std::shared_ptr> client, net::Message& msgRcv) +{ + net::Message msgSend; + std::stringstream body; + + switch (msgRcv.header.id) + { + case net::MessageTypes::ServerData: + LOG(INFO) << "[Client " << client->GetID() << "]: Request Server Data"; + msgSend.header.id = net::MessageTypes::ServerData; + getBodyData(body); + msgSend << body.str(); + client->Send(msgSend); + break; + case net::MessageTypes::ServerCondition: + LOG(INFO) << "[Client " << client->GetID() << "]: Request temporary Condition Data"; + msgSend.header.id = net::MessageTypes::ServerCondition; + getBodyCondition(body); + msgSend << body.str(); + client->Send(msgSend); + break; + case net::MessageTypes::ServerAlert: + LOG(INFO) << "[Client " << client->GetID() << "]: Request Alert Data"; + msgSend.header.id = net::MessageTypes::ServerAlert; + client->Send(msgSend); + break; + case net::MessageTypes::ServerLog: + LOG(INFO) << "[Client " << client->GetID() << "]: Request Log Files"; + msgSend.header.id = net::MessageTypes::ServerLog; + getBodyLog(body, msgRcv); + msgSend << body.str(); + client->Send(msgSend); + break; + case net::MessageTypes::ServerCloseConnection: + LOG(INFO) << "[Client " << client->GetID() << "]: Request disconnection"; + client->Disconnect(); + break; + + default: + LOG(ERROR) << "[Client " << client->GetID() << "]: Invaild reuqest code"; + break; + } +} + +void NetServer::notifyAllConnections(net::Message &message) +{ + LOG(INFO) << "Sent broadcast to "; + unsigned int counter = 0; + //Send message to all listed active connections + for(auto &connection: m_deqConnections){ + connection->Send(message); + counter++; + } + LOG(INFO) << "Sent broadcast to " << counter << " devices"; +} + + +std::stringstream& NetServer::getBodyData(std::stringstream& buffer) +{ + DataModel::Instance()->readAllDataFiles(buffer); + DataModel::Instance()->readPermanentData(buffer, false); + return buffer; +} + +std::stringstream& NetServer::getBodyLog(std::stringstream& buffer, net::Message msgRcv) +{ + unsigned long long fromTime; + if (msgRcv.header.size == 0) + fromTime = 0; + else + msgRcv >> fromTime; + DataModel::Instance()->readLogFile(buffer, fromTime); + return buffer; +} + +std::stringstream& NetServer::getBodyCondition(std::stringstream& buffer) +{ + DataModel::Instance()->readTemporaryData(buffer); + return buffer; +} + diff --git a/ConditionMonitoring/NetServer.h b/ConditionMonitoring/NetServer.h new file mode 100644 index 0000000..db0eff9 --- /dev/null +++ b/ConditionMonitoring/NetServer.h @@ -0,0 +1,54 @@ +#pragma once +#include + +#include +#include "net_server_client.h" + +namespace net { + enum class MessageTypes : uint32_t + { + ServerAccept, + ServerDeny, + ServerData, + ServerLog, + ServerCondition, + ServerAlert, + ServerCloseConnection, + }; +} + +class NetServer : public net::ServerInterface +{ +public: + NetServer(uint16_t nPort) : net::ServerInterface(nPort) {} + NetServer() = delete; + NetServer(NetServer&) = delete; + + void Stop() override; + bool Start() override; + + bool isRunning(){return serverRunning;} + + void notifyAllConnections(net::Message& message); + + bool serverRunning = false; +protected: + virtual bool OnClientConnect(std::shared_ptr> client) override; + + // Called when a client appears to have disconnected + virtual void OnClientDisconnect(std::shared_ptr> client) override; + + // Called when a message arrives + virtual void OnMessage(std::shared_ptr> client, net::Message& msg) override; + +private: + //Functions to collect the data for response message body + + //Collects Data message body + std::stringstream& getBodyData(std::stringstream& buffer); + //Collects Lof files message body + std::stringstream& getBodyLog(std::stringstream& buffer, net::Message msgRcv); + //Collects Electrical Condition values message body + std::stringstream& getBodyCondition(std::stringstream& buffer); + +}; diff --git a/ConditionMonitoring/ParameterCharP.cpp b/ConditionMonitoring/ParameterCharP.cpp new file mode 100644 index 0000000..ffd4a88 --- /dev/null +++ b/ConditionMonitoring/ParameterCharP.cpp @@ -0,0 +1 @@ +#include "ParameterCharP.h" diff --git a/ConditionMonitoring/ParameterCharP.h b/ConditionMonitoring/ParameterCharP.h new file mode 100644 index 0000000..264f6e9 --- /dev/null +++ b/ConditionMonitoring/ParameterCharP.h @@ -0,0 +1,15 @@ +#pragma once +#include "ParameterInterface.h" + +class ParameterCharP : public ParameterInterface +{ +private: + char* param = nullptr; + const uint8_t length; +public: + ParameterCharP(const ModbusRegister description, const uint16_t address, const uint8_t _length, const Access access) : ParameterInterface(description, address, access), length(_length) {}; + ParameterCharP(const ModbusRegister description, const unsigned short address, const uint8_t _length, const Access access, const Category cat) : ParameterInterface(description, address, access, cat), length(_length) {}; + virtual ~ParameterCharP(){} + + uint8_t getSize() const override { return length; } +}; diff --git a/ConditionMonitoring/ParameterDouble.cpp b/ConditionMonitoring/ParameterDouble.cpp new file mode 100644 index 0000000..5069c8f --- /dev/null +++ b/ConditionMonitoring/ParameterDouble.cpp @@ -0,0 +1,2 @@ +#include "ParameterDouble.h" + diff --git a/ConditionMonitoring/ParameterDouble.h b/ConditionMonitoring/ParameterDouble.h new file mode 100644 index 0000000..2206156 --- /dev/null +++ b/ConditionMonitoring/ParameterDouble.h @@ -0,0 +1,16 @@ +#pragma once +#include "ParameterInterface.h" +typedef double FP64; + +class ParameterDouble : public ParameterInterface +{ +private: + FP64 param = 0.0; +public: + ParameterDouble(const ModbusRegister description, uint16_t address, Access access) : ParameterInterface(description, address, access) {}; + ParameterDouble(const ModbusRegister description, const unsigned short address, const Access access, const Category cat) : ParameterInterface(description, address, access, cat) {}; + virtual ~ParameterDouble(){} + + uint8_t getSize() const override { return 64; } + +}; diff --git a/ConditionMonitoring/ParameterFloat.cpp b/ConditionMonitoring/ParameterFloat.cpp new file mode 100644 index 0000000..58e7b57 --- /dev/null +++ b/ConditionMonitoring/ParameterFloat.cpp @@ -0,0 +1,2 @@ +#include "ParameterFloat.h" + diff --git a/ConditionMonitoring/ParameterFloat.h b/ConditionMonitoring/ParameterFloat.h new file mode 100644 index 0000000..a4d3875 --- /dev/null +++ b/ConditionMonitoring/ParameterFloat.h @@ -0,0 +1,17 @@ +#pragma once +#include "ParameterInterface.h" + +typedef float FP32; + +class ParameterFloat : public ParameterInterface +{ +private: +public: + ParameterFloat(const ModbusRegister description, uint16_t address, Access access) : ParameterInterface(description, address, access) {}; + ParameterFloat(const ModbusRegister description, const unsigned short address, const Access access, const Category cat) : ParameterInterface(description, address, access, cat) {}; + virtual ~ParameterFloat(){} + + uint8_t getSize() const override { return 32; } + +}; + diff --git a/ConditionMonitoring/ParameterInterface.h b/ConditionMonitoring/ParameterInterface.h new file mode 100644 index 0000000..32f3eb5 --- /dev/null +++ b/ConditionMonitoring/ParameterInterface.h @@ -0,0 +1,58 @@ +#pragma once +#include "ModbusRegister.h" +#include +#include +#include +class ModbusInterface; + +enum class Access { + //Access Permission of a given MODBUS Parameter + R, + W, + RW, + CMD +}; + +struct ParameterSpecification { + ModbusRegister description; + Category cat; + uint16_t address; + uint8_t length; + Access access; + std::vector readedBytes; + bool error = true; + std::unique_ptr& connection; + + ParameterSpecification(ModbusRegister d, Category c, uint16_t ad, uint8_t l, std::unique_ptr& i, Access ac) : + description(d), cat(c), address(ad), length(l), access(ac), connection(i) {} + + bool isReadable(){ return access == Access::R || access == Access::RW;} + bool isWritable(){ return access == Access::W || access == Access::RW || access == Access::CMD;} +}; + + +template +class ParameterInterface +{ +protected: + S currentParam{}; + + const ModbusRegister valueDescription; + const uint16_t modbusAddress; + const Access access; + const Category cat = Category::NONE; + +public: + ParameterInterface(const ModbusRegister description, const unsigned short address, const Access access) : valueDescription(description), modbusAddress(address), access(access) {}; + ParameterInterface(const ModbusRegister description, const unsigned short address, const Access access, const Category _cat) : valueDescription(description), modbusAddress(address), access(access), cat(_cat) {}; + virtual ~ParameterInterface(){} + + bool isReadable() { return (access == Access::R || access == Access::RW); } + S getParam()const { return currentParam; }; + uint16_t getModbusAddress() { return modbusAddress; } + ModbusRegister getValueDescription() { return valueDescription; }; + Category getCategory()const {return cat;} + virtual uint8_t getSize() const = 0; + + ParameterSpecification getSpecification(std::unique_ptr& connection) const { return ParameterSpecification(valueDescription, cat, modbusAddress, getSize(), connection, access); } +}; diff --git a/ConditionMonitoring/ParameterS16.cpp b/ConditionMonitoring/ParameterS16.cpp new file mode 100644 index 0000000..deb316a --- /dev/null +++ b/ConditionMonitoring/ParameterS16.cpp @@ -0,0 +1,2 @@ +#include "ParameterS16.h" + diff --git a/ConditionMonitoring/ParameterS16.h b/ConditionMonitoring/ParameterS16.h new file mode 100644 index 0000000..4da1f95 --- /dev/null +++ b/ConditionMonitoring/ParameterS16.h @@ -0,0 +1,15 @@ +#pragma once +#include "ParameterInterface.h" + +class ParameterS16 : public ParameterInterface +{ +private: + int16_t param = 0; +public: + ParameterS16(const ModbusRegister description, uint16_t address, Access access) : ParameterInterface(description, address, access) {}; + ParameterS16(const ModbusRegister description, const unsigned short address, const Access access, const Category cat) : ParameterInterface(description, address, access, cat) {}; + virtual ~ParameterS16(){} + + uint8_t getSize() const override { return 16; } + +}; diff --git a/ConditionMonitoring/ParameterUInt16.cpp b/ConditionMonitoring/ParameterUInt16.cpp new file mode 100644 index 0000000..c24c791 --- /dev/null +++ b/ConditionMonitoring/ParameterUInt16.cpp @@ -0,0 +1,2 @@ +#include "ParameterUInt16.h" + diff --git a/ConditionMonitoring/ParameterUInt16.h b/ConditionMonitoring/ParameterUInt16.h new file mode 100644 index 0000000..386dad5 --- /dev/null +++ b/ConditionMonitoring/ParameterUInt16.h @@ -0,0 +1,15 @@ +#pragma once +#include "ParameterInterface.h" + +class ParameterUInt16 : public ParameterInterface +{ +private: + uint16_t param = 0; +public: + ParameterUInt16(const ModbusRegister description, uint16_t address, Access access) : ParameterInterface(description, address, access) {}; + ParameterUInt16(const ModbusRegister description, const unsigned short address, const Access access, const Category cat) : ParameterInterface(description, address, access, cat) {}; + virtual ~ParameterUInt16(){} + + uint8_t getSize() const override { return 16; } + +}; diff --git a/ConditionMonitoring/ParameterUInt32.cpp b/ConditionMonitoring/ParameterUInt32.cpp new file mode 100644 index 0000000..3cc6c2a --- /dev/null +++ b/ConditionMonitoring/ParameterUInt32.cpp @@ -0,0 +1,2 @@ +#include "ParameterUInt32.h" + diff --git a/ConditionMonitoring/ParameterUInt32.h b/ConditionMonitoring/ParameterUInt32.h new file mode 100644 index 0000000..19d193d --- /dev/null +++ b/ConditionMonitoring/ParameterUInt32.h @@ -0,0 +1,14 @@ +#pragma once +#include "ParameterInterface.h" +class ParameterUInt32 : public ParameterInterface +{ +private: + uint32_t param = 0; +public: + ParameterUInt32(const ModbusRegister description, uint16_t address, Access access) : ParameterInterface(description, address, access) {}; + ParameterUInt32(const ModbusRegister description, const unsigned short address, const Access access, const Category cat) : ParameterInterface(description, address, access, cat) {}; + virtual ~ParameterUInt32(){} + + uint8_t getSize() const override { return 32; } + +}; diff --git a/ConditionMonitoring/PublisherBenderRcm.cpp b/ConditionMonitoring/PublisherBenderRcm.cpp new file mode 100644 index 0000000..f3db97c --- /dev/null +++ b/ConditionMonitoring/PublisherBenderRcm.cpp @@ -0,0 +1,20 @@ +#include "PublisherBenderRcm.h" +#include "SystemConfig.h" + +PublisherBenderRcm::PublisherBenderRcm() : PublisherInterface(PublisherType::RCMS_BENDER) +{ + connection = std::make_unique(SystemConfig::getIntConfigParameter("modbus_rtu_slave_address"), + SystemConfig::getStringConfigParameter("modbus_rtu_device"), + SystemConfig::getIntConfigParameter("modbus_rtu_baud"), + SystemConfig::getStringConfigParameter("modbus_rtu_pairity").at(0), + SystemConfig::getIntConfigParameter("modbus_rtu_stop_bits")); + + modbusData = std::make_unique(this->id); +} + +std::string PublisherBenderRcm::getName() const +{ + std::stringstream name; + name << "RCM - Bender (ID: " << id << ")"; + return name.str(); +} diff --git a/ConditionMonitoring/PublisherBenderRcm.h b/ConditionMonitoring/PublisherBenderRcm.h new file mode 100644 index 0000000..d922249 --- /dev/null +++ b/ConditionMonitoring/PublisherBenderRcm.h @@ -0,0 +1,18 @@ +#pragma once +#include +#include "PublisherType.h" +#include "ModbusDataBender.h" +#include "PublisherInterface.h" +#include "modbus_interface_lib.h" + +class PublisherBenderRcm : + public PublisherInterface +{ +private: + +public: + PublisherBenderRcm(); + + std::string getName()const override; +}; + diff --git a/ConditionMonitoring/PublisherData.cpp b/ConditionMonitoring/PublisherData.cpp new file mode 100644 index 0000000..8c8e226 --- /dev/null +++ b/ConditionMonitoring/PublisherData.cpp @@ -0,0 +1,30 @@ +#include "PublisherData.h" + +PublisherData::PublisherData(const int _publisherID, PublisherType pubType, float _value) : + publisherID(_publisherID), publisherType(pubType), values(std::vector()) +{ + values.push_back(_value); +} + +PublisherData::PublisherData(const int _publisherID, PublisherType pubType, std::vector _values) : + publisherID(_publisherID), publisherType(pubType), values(_values) +{ + +} + +PublisherData::PublisherData() : + publisherID(0), publisherType(PublisherType::NA), values({ 0.0 }) +{ + +} + + + + +std::ostream& operator<<(std::ostream& os, const PublisherData& data){ + for (u_int i = 0; i < data.values.size()-1; i++) { + os << data.values[i] << ","; + } + os << data.values[data.values.size()-1] << ";"; + return os; +} diff --git a/ConditionMonitoring/PublisherData.h b/ConditionMonitoring/PublisherData.h new file mode 100644 index 0000000..9cee658 --- /dev/null +++ b/ConditionMonitoring/PublisherData.h @@ -0,0 +1,42 @@ +#pragma once +#include +#include +#include +#include "PublisherType.h" + +using namespace std::chrono; +typedef unsigned int u_int; + +class PublisherData +{ +private: + u_int publisherID; + const PublisherType publisherType; + system_clock::time_point time_generated; + std::vector values; + +public: + //constructors + PublisherData(const int _publisherID, PublisherType pubType, float value); + PublisherData(const int _publisherID, PublisherType pubType, std::vector _values); + PublisherData(); + + //void updateTime() { this->time_generated = system_clock::now(); } + inline void addData(const float data) { values.push_back(data); } + + u_int getID() { return publisherID; } + std::vector& getValues() { return values; }; + + //getter/setter + PublisherType getType()const { return publisherType; } + system_clock::time_point getTimeGenerated()const { return time_generated; } + void setTimeGenerated(const std::chrono::system_clock::time_point t) { time_generated = t; } + + + bool hasData()const { return values.size() == 0 ? false : true; } + + //operator + friend std::ostream& operator<<(std::ostream&, const PublisherData&); + friend std::istream& operator>>(std::istream&, PublisherData&); +}; + diff --git a/ConditionMonitoring/PublisherInterface.cpp b/ConditionMonitoring/PublisherInterface.cpp new file mode 100644 index 0000000..faa63a2 --- /dev/null +++ b/ConditionMonitoring/PublisherInterface.cpp @@ -0,0 +1,19 @@ +#include "PublisherInterface.h" + +#include + +u_int PublisherInterface::id_static = 0; + +PublisherInterface::PublisherInterface(const PublisherType type) + : id(id_static++), type(type) +{} + + +bool PublisherInterface::operator==(const PublisherInterface& p2) const{ + return this->id == p2.id; +} + +ts_queue& PublisherInterface::enqueueReadingRegisters(ts_queue< ParameterSpecification>& queue, Category cat){ + modbusData->modbusRegisterCat(cat, queue, connection); + return queue; +} diff --git a/ConditionMonitoring/PublisherInterface.h b/ConditionMonitoring/PublisherInterface.h new file mode 100644 index 0000000..d2e515a --- /dev/null +++ b/ConditionMonitoring/PublisherInterface.h @@ -0,0 +1,35 @@ +#pragma once +#include +#include "PublisherData.h" +#include "ModbusDataInterface.h" +#include "modbus_interface_lib.h" +#include "ts_queue.h" +#include "SystemConfig.h" + + +class PublisherInterface +{ +private: + static u_int id_static; + +protected: + const u_int id; + const PublisherType type; + std::unique_ptr connection; + std::unique_ptr modbusData; + +public: + PublisherInterface(const PublisherType type); + virtual ~PublisherInterface(){} + + ts_queue& enqueueReadingRegisters(ts_queue& queue, Category cat); + + virtual std::string getName()const = 0; + + bool open(){ return connection->openConnection(); } + bool isOpen() const { return connection->getIsOpen(); } + + u_int getID()const { return id; } + //operator overloading + inline bool operator==(const PublisherInterface& p2)const; +}; diff --git a/ConditionMonitoring/PublisherPowercenter.cpp b/ConditionMonitoring/PublisherPowercenter.cpp new file mode 100644 index 0000000..ce119f2 --- /dev/null +++ b/ConditionMonitoring/PublisherPowercenter.cpp @@ -0,0 +1,26 @@ +#include "PublisherPowercenter.h" +#include + + +PublisherPowercenter::PublisherPowercenter() : PublisherInterface(PublisherType::POWERCENTER) +{ + connection = std::make_unique(SystemConfig::getIntConfigParameter("modbus_tcp_slave_address"), + SystemConfig::getStringConfigParameter("modbus_poc_ip"), + SystemConfig::getIntConfigParameter("modbus_poc_port")); + + modbusData = std::make_unique(this->id); +} + +PublisherPowercenter::~PublisherPowercenter() { + //modbus->disconnect(); +} + + + + +std::string PublisherPowercenter::getName() const +{ + std::stringstream name; + name << "Powercenter - Siemens (ID: " << id << ")"; + return name.str(); +} diff --git a/ConditionMonitoring/PublisherPowercenter.h b/ConditionMonitoring/PublisherPowercenter.h new file mode 100644 index 0000000..dc5c8ff --- /dev/null +++ b/ConditionMonitoring/PublisherPowercenter.h @@ -0,0 +1,16 @@ +#pragma once +#include "PublisherInterface.h" +#include "ModbusDataPOC.h" +#include + +class PublisherPowercenter : + public PublisherInterface +{ +private: + +public: + PublisherPowercenter(); + virtual ~PublisherPowercenter(); + + std::string getName()const override; +}; diff --git a/ConditionMonitoring/PublisherType.h b/ConditionMonitoring/PublisherType.h new file mode 100644 index 0000000..0d99093 --- /dev/null +++ b/ConditionMonitoring/PublisherType.h @@ -0,0 +1,7 @@ +#pragma once + +enum class PublisherType{ + NA = 0, + RCMS_BENDER, + POWERCENTER +}; \ No newline at end of file diff --git a/ConditionMonitoring/SystemConfig.cpp b/ConditionMonitoring/SystemConfig.cpp new file mode 100644 index 0000000..4e159ba --- /dev/null +++ b/ConditionMonitoring/SystemConfig.cpp @@ -0,0 +1,137 @@ +#include +#include + +#include "SystemConfig.h" +#include "CustomStringUtilities.h" + +std::map SystemConfig::intParameter; +std::map SystemConfig::floatParameter; +std::map SystemConfig::stringParameter; + +std::ostream& operator<<(std::ostream& os, SystemConfig& config) { + os << "System Configuration: " << std::endl; + std::for_each(config.intParameter.begin(), config.intParameter.end(), + [&config, &os](auto& param) { os << "\t"<< param.first << ":\t" << param.second << std::endl; }); + return os; +} + + + +//Read in the data from the config file +void SystemConfig::readStandardConfig() +{ + std::fstream fileStream(filePath, std::ios::in); + if (fileStream.is_open()) { + std::stringstream buf; + while (std::getline(fileStream, fileContent)) + buf << fileContent << '\n'; + fileContent = buf.str(); + fileStream.close(); + parseConfFile(); + } + else { + LOG(ERROR) << "Couldn't open " << filePath << " for reading -> using standard configuration instead"; + } + +} + +//Place parameters to read in here +SystemConfig::SystemConfig() +{ + //define name and default value here + intParameter.emplace("read_data_interval_s", 60); + intParameter.emplace("tcp_server_port", 7777); + stringParameter.emplace("modbus_poc_ip", "127.0.0.1"); + intParameter.emplace("modbus_poc_port", 502); + intParameter.emplace("alert_read_interval", 5); + stringParameter.emplace("modbus_rtu_device", "dev/tty0"); + intParameter.emplace("modbus_rtu_baud", 9800); + intParameter.emplace("modbus_rtu_stop_bits", 1); + stringParameter.emplace("modbus_rtu_pairity", "E"); + intParameter.emplace("modbus_rtu_slave_address", 1); + intParameter.emplace("modbus_tcp_slave_address", 1); + intParameter.emplace("permanent_param_history", 1000); + floatParameter.emplace("crit_residual_current", 30.0); + intParameter.emplace("crit_residual_timerange", 12); + intParameter.emplace("update_model_rate", 100); + intParameter.emplace("narrow_block", 100); +} + + +void SystemConfig::parseConfFile() +{ + //remove comments + std::regex patternComment("#(\\w|\\s)*\n+"); + fileContent = std::regex_replace(fileContent, patternComment, "\n"); + + auto posHeader = fileContent.find("SystemConfig:"); + if (posHeader == std::string::npos){ + LOG(ERROR) << "Found no Header for the configuration file -> using standard configuration instead"; + return; + } + for (auto& param : intParameter) { + std::regex pattern{ param.first + "\\s*=\\s*\\w+" }; + std::smatch match; + if (std::regex_search(fileContent, match, pattern)) { + std::string numberString = match.str(); + CustomStringUtilities::removeAllWhitespaces(numberString); + param.second = std::stoi(numberString.substr(numberString.find('=') + 1)); + } + else { + LOG(INFO) << "No specified config paramter (int) for " << param.first << ", using the default value " << param.second; + } + } + for (auto& param : floatParameter) { + std::regex pattern{ param.first + "\\s*=\\s*\\d+\\.\\d*" }; + std::smatch match; + if (std::regex_search(fileContent, match, pattern)) { + std::string numberString = match.str(); + CustomStringUtilities::removeAllWhitespaces(numberString); + param.second = std::stof(numberString.substr(numberString.find('=') + 1)); + } + else { + LOG(INFO) << "No specified config paramter (float) for " << param.first << ", using the default value " << param.second; + } + } + for (auto& param : stringParameter) { + std::regex pattern{ param.first + "\\s*=\\s*(\\w|.)*" }; + std::smatch match; + if (std::regex_search(fileContent, match, pattern)) { + std::string paramString = match.str(); + CustomStringUtilities::removeAllWhitespaces(paramString); + param.second = paramString.substr(paramString.find('=') + 1); + } + else { + LOG(INFO) << "No specified config paramter (float) for " << param.first << ", using the default value " << param.second; + } + } +} + + +int SystemConfig::getIntConfigParameter(std::string paramName){ + auto it = intParameter.find(paramName.data()); + if (it != intParameter.end()) { + return it->second; + } + LOG(ERROR) << "Tried to read non existant int config parameter " << paramName; + return 0; +} + +float SystemConfig::getFloatConfigParameter(std::string paramName){ + auto it = floatParameter.find(paramName.data()); + if (it != floatParameter.end()) { + return it->second; + } + LOG(ERROR) << "Tried to read non existant float config parameter " << paramName; + return 0.0f; +} + +std::string SystemConfig::getStringConfigParameter(std::string paramName) +{ + auto it = stringParameter.find(paramName.data()); + if (it != stringParameter.end()) { + return it->second; + } + LOG(ERROR) << "Tried to read non existant float config parameter " << paramName; + return ""; +} diff --git a/ConditionMonitoring/SystemConfig.h b/ConditionMonitoring/SystemConfig.h new file mode 100644 index 0000000..b9f3ccd --- /dev/null +++ b/ConditionMonitoring/SystemConfig.h @@ -0,0 +1,28 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +class SystemConfig +{ +public: + void readStandardConfig(); + SystemConfig(); + static int getIntConfigParameter(std::string paramName); + static float getFloatConfigParameter(std::string paramName); + static std::string getStringConfigParameter(std::string paramName); +private: + //Parameters to read from the file (name and default value) + static std::map intParameter; + static std::map floatParameter; + static std::map stringParameter; + + const std::string filePath = "system_conf.conf"; + std::string fileContent; + void parseConfFile(); + friend std::ostream& operator<<(std::ostream& os, SystemConfig& config); +}; diff --git a/ConditionMonitoring/SystemControler.cpp b/ConditionMonitoring/SystemControler.cpp new file mode 100644 index 0000000..006b991 --- /dev/null +++ b/ConditionMonitoring/SystemControler.cpp @@ -0,0 +1,203 @@ +#include "SystemControler.h" +#include +#include "SystemConfig.h" +#include + +using namespace std::chrono; + +std::atomic dataReadingCancelled = false; +std::atomic updateServerCancelled = false; +std::atomic threadRunning = false; +std::thread* readDataThread = nullptr; + + +SystemControler::SystemControler() : + dataModel(DataModel::Instance()), dataAcquire(std::make_shared()) +{ + this->alertReadInterval = SystemConfig::getIntConfigParameter("alert_read_interval"); + this->serverPort = SystemConfig::getIntConfigParameter("tcp_server_port"); + + initializePermanentStorage(); +} + +SystemControler::~SystemControler() +{ + LOG(INFO) << "Wait until continous modbus reading thread finishes..."; + if(readDataThread != nullptr && threadRunning){ + dataReadingCancelled = true; + if(readDataThread->joinable()) + readDataThread->join(); + } + + LOG(INFO) << "Wait until server update thread finishes..."; + if(serverUpdateThread != nullptr){ + updateServerCancelled = true; + if(serverUpdateThread->joinable()) + serverUpdateThread->join(); + } + server.reset(); + + if(readDataThread != nullptr) + delete readDataThread; + if(serverUpdateThread != nullptr) + delete serverUpdateThread; + + DataModel::Destroy(); +} + + +void updateServer(std::shared_ptr server, std::chrono::milliseconds ms) { + auto now = std::chrono::system_clock::now(); + //Run as long as server exists and is not stopped + while (server != nullptr && !server->isStopped()) { + if(updateServerCancelled) + return; + + //Check for alerts + auto& alerts = DataModel::Instance()->getAlerts(); + while(alerts.size() > 0){ + net::Message msg; + auto a = alerts.pop_front(); + msg.header.id = net::MessageTypes::ServerAlert; + short bitmask = 0; + std::memcpy(&bitmask, a.readedBytes.data(), 2); + std::stringstream tmp; + tmp << "Modbus " << a.connection->getConnectionType() << " alert: " << ModbusDataPOC::getStatusMessage(bitmask); + msg << tmp.str(); + server->MessageAllClients(msg); + } + + //check for messages, specify no max message count + //return if no message is available + server->Update(-1, false); + std::this_thread::sleep_until(now + ms); + } + return; +} + + +void SystemControler::startIpTcpServer() { + try { + //start server + if(server == nullptr) + server = std::make_shared(serverPort); + + if(server->isRunning()){ + LOG(WARNING) << "Invoked server start, but server is already running"; + return; + } + + server->Start(); + + //Continous running update thread, that checks for incomming messages + serverUpdateThread = new std::thread(updateServer, server, serverTickRateMs); + } + catch (asio::system_error& e) { + LOG(FATAL) << "[SERVER]: Error " + << e.code() << " >> " + << e.what(); + } +} + + + +/* +thread whhich acquires data from all registered publishers +->pointer to dataAcquisition, which handles the readiong requests +->intervalSec: # of seconds +*/ +void enqueueRegisterThread(std::shared_ptr dataAcquisition, SystemControler* controler, const u_int intervalSec) +{ + threadRunning = true; + dataReadingCancelled = false; + LOG(INFO) << "Started continous data reading"; + while (!dataReadingCancelled) { + const auto currentTime = system_clock::now(); + + //read in the data from the publishers + dataAcquisition->enqueuePublisherRegister(); + + controler->evaluate(); + for (u_int i = 1; i <= intervalSec; i++) + { + if (dataReadingCancelled) { + LOG(INFO) << "Stopped continous data reading"; + threadRunning = false; + dataReadingCancelled = false; + return; + } + //Wait for 1 additional second and then check for cancelled thread + std::this_thread::sleep_until(currentTime + std::chrono::seconds(1) * i); + } + } + LOG(INFO) << "Stopped continous data reading"; + dataReadingCancelled = false; + threadRunning = false; +} + +//Invokes continous modbus alert reading +void SystemControler::startEnqueuingRegister() +{ + if (threadRunning && !dataReadingCancelled) { + LOG(ERROR) << "ILLEGAL STATE: Invoked starting readDataThread while thread is already running (Thread ID: " << readDataThread->get_id() << ")"; + return; + } + //Continue thread if it is already cancelled but still running + else if (threadRunning && dataReadingCancelled) { + dataReadingCancelled = false; + } + else { + if(readDataThread != nullptr) + delete readDataThread; + readDataThread = new std::thread(enqueueRegisterThread, dataAcquire, this, alertReadInterval); + } +} + +//Cancels continous modbus alert reading +void SystemControler::cancelReadingModbusAlerts() +{ + if (dataReadingCancelled){ + LOG(INFO) << "Thread is already cancelled, waiting for thread to stop"; + } + else if (!threadRunning || readDataThread == nullptr){ + LOG(ERROR) << "ILLEGAL STATE: Invoke cancelling of readDataThread while thread is not running"; + } + else{ + dataReadingCancelled = true; + if(readDataThread->joinable()) + readDataThread->join(); + } +} + +//Check wether to flush or not, else counts up +void SystemControler::flushAfterNData() +{ + dataModel->checkForFlushData(); +} + +//delegate register Publisher +void SystemControler::registerPublisher(std::unique_ptr publisher) +{ + dataAcquire->registerPublisher(std::move(publisher)); +} + +void SystemControler::evaluate(){ + if(evaluator == nullptr) + return; + auto alerts = evaluator->evaluate(); + for(auto &a : alerts){ + net::Message msg; + msg.header.id = net::MessageTypes::ServerAlert; + msg << a.message; + server->MessageAllClients(msg); + } +} + + +void SystemControler::initializePermanentStorage(){ + //register Residual Current (dummy) to perform linear regression + //set biased fo ML Algorithm + dataModel->makePermanent(ModbusRegister::BENDER_Residual_current, true); + + evaluator = std::make_unique(); +} diff --git a/ConditionMonitoring/SystemControler.h b/ConditionMonitoring/SystemControler.h new file mode 100644 index 0000000..3744858 --- /dev/null +++ b/ConditionMonitoring/SystemControler.h @@ -0,0 +1,60 @@ +#pragma once +#include +#include +#include +#include "DataAcquisition.h" +#include "PublisherType.h" +#include "Evaluator.h" +#include "NetServer.h" + + +class SystemControler +{ +private: + u_int serverPort; + u_int alertReadInterval; + + std::unique_ptr& dataModel; + std::shared_ptr dataAcquire; + std::shared_ptr evaluator; + + //Estimated raw data which are collected over one hour of continous running in Bytes + size_t dataSizePerHour = 0; + std::shared_ptr server; + + //Continous server updating with fixed tick rate, spcified in ms + std::thread* serverUpdateThread = nullptr; + std::chrono::milliseconds serverTickRateMs { 100 }; + + void initializePermanentStorage(); + +public: + SystemControler(); + ~SystemControler(); + + void startIpTcpServer(); + + //getter + std::shared_ptr& getDataAcquisitionRef() { return dataAcquire; } + + //Delegate read requests threaded to to acquire unit + void startEnqueuingRegister(); + + size_t getDataSizePerHour()const { return dataSizePerHour; }; + + void cancelReadingModbusAlerts(); + + void flushAfterNData(); + + void startModbusWorkerThread(){ dataAcquire->startModbusThread(); } + + void test(){ } + + //delegate register Publisher + void registerPublisher(std::unique_ptr publisher); + + //call evaluation manually, delegates call + void evaluate(); + + unsigned long deleteFiles(std::chrono::seconds olderThan) { return (olderThan == seconds(0)) ? dataModel->removeStoredData() : dataModel->removeStoredData(olderThan); } +}; diff --git a/ConditionMonitoring/baConditionMonitoring.pro b/ConditionMonitoring/baConditionMonitoring.pro new file mode 100644 index 0000000..0c3f604 --- /dev/null +++ b/ConditionMonitoring/baConditionMonitoring.pro @@ -0,0 +1,88 @@ +TEMPLATE = app +CONFIG += console c++17 +CONFIG -= app_bundle +CONFIG -= qt + +#INCLUDEPATH += ../AsioNetClientServer +#LIBS += -L../AsioNetClientServer/debug -lasio_inet_client_server +#LIBS += -pthread + +unix: CONFIG += link_pkgconfig +unix: PKGCONFIG += libmodbuspp + + +SOURCES += \ + CustomStringUtilities.cpp \ + DataAcquisition.cpp \ + DataModel.cpp \ + Evaluator.cpp \ + MLAnalyzer.cpp \ + MLLinReg.cpp \ + ModbusDataBender.cpp \ + ModbusDataInterface.cpp \ + ModbusDataPOC.cpp \ + ModbusInterface.cpp \ + ModbusRtu.cpp \ + ModbusTcp.cpp \ + NetServer.cpp \ + ParameterCharP.cpp \ + ParameterDouble.cpp \ + ParameterFloat.cpp \ + ParameterS16.cpp \ + ParameterUInt16.cpp \ + ParameterUInt32.cpp \ + PublisherBenderRcm.cpp \ + PublisherInterface.cpp \ + PublisherPowercenter.cpp \ + SystemConfig.cpp \ + SystemControler.cpp \ + cappedstorage.cpp \ + easylogging++.cc \ + main.cpp + +HEADERS += \ + CustomStringUtilities.h \ + DataAcquisition.h \ + DataModel.h \ + Evaluator.h \ + MLAlert.h \ + MLAnalyzer.h \ + MLLinReg.h \ + ModbusDataBender.h \ + ModbusDataInterface.h \ + ModbusDataPOC.h \ + ModbusInterface.h \ + ModbusRegister.h \ + ModbusRtu.h \ + ModbusTcp.h \ + NetServer.h \ + ParameterCharP.h \ + ParameterDouble.h \ + ParameterFloat.h \ + ParameterInterface.h \ + ParameterS16.h \ + ParameterUInt16.h \ + ParameterUInt32.h \ + PublisherBenderRcm.h \ + PublisherInterface.h \ + PublisherPowercenter.h \ + PublisherType.h \ + SystemConfig.h \ + SystemControler.h \ + asioClientServer/net_client.h \ + asioClientServer/net_common.h \ + asioClientServer/net_connection.h \ + asioClientServer/net_dequeue_ts.h \ + asioClientServer/net_message.h \ + asioClientServer/net_server.h \ + cappedstorage.h \ + modbus_interface_lib.h \ + net_server_client.h \ + ts_map.h \ + ts_queue.h \ + easylogging++.h + +DISTFILES += \ + build_baConditionMonitoring/system_conf.conf + + diff --git a/ConditionMonitoring/cappedstorage.cpp b/ConditionMonitoring/cappedstorage.cpp new file mode 100644 index 0000000..fd482a5 --- /dev/null +++ b/ConditionMonitoring/cappedstorage.cpp @@ -0,0 +1,70 @@ +#include "cappedstorage.h" +#include +#include +#include +#include "SystemConfig.h" + +CappedStorage::CappedStorage(bool _biased): biased(_biased) +{ + if(biased){ + X.resize(0, 2); + } + else + X.resize(0, 1); + accessMtx = std::make_unique(); +} + +void CappedStorage::lock(){ + accessMtx->lock(); +} + +void CappedStorage::unlock(){ + accessMtx->unlock(); +} + +void CappedStorage::store(const std::vector& d) +{ + std::scoped_lock lock(*accessMtx); + float value = 0; + std::memcpy(&value, d.data(), 4); + + const double time = c::time_point_cast(c::system_clock::now()).time_since_epoch().count(); + + X.conservativeResize(X.rows()+1, X.cols()); + y.conservativeResize(y.rows()+1, 1); + y(y.rows()-1, 0) = value; + + if(biased){ + X(X.rows()-1, 0) = 1; + X(X.rows()-1, 1) = time; + } + else + X(X.rows()-1, 0) = time; +} + + +void CappedStorage::clear() +{ + std::scoped_lock lock(*accessMtx); + X.resize(0, Eigen::NoChange_t()); +} + +//Stream operator for 1 dimensional X Matrix +std::ostream& operator<<(std::ostream& os, const CappedStorage& c){ + std::scoped_lock lock(*(c.accessMtx)); + int col =0; + if(c.biased){ + col = 1; + } + + for (int i = 0; i < c.X.rows(); i++) { + os << c.X(i, col) << ","; + } + os << std::endl; + for (int i = 0; i < c.y.size(); i++) { + os << c.y(i, 0) << ","; + } + os << std::endl; + + return os; +} diff --git a/ConditionMonitoring/cappedstorage.h b/ConditionMonitoring/cappedstorage.h new file mode 100644 index 0000000..e9c53d0 --- /dev/null +++ b/ConditionMonitoring/cappedstorage.h @@ -0,0 +1,49 @@ +#ifndef CAPPEDSTORAGE_H +#define CAPPEDSTORAGE_H + +#include +#include +#include +#include +#include +#include +#include +#include +namespace c = std::chrono; + +//Class for one dimensional double values +class CappedStorage +{ +private: + //Stored values + Eigen::Matrix X; + Eigen::Matrix y; + + std::unique_ptr accessMtx; + + //If set, storage is extended by bias column (X(:, 1)) + //to perform ML Algorithms + const bool biased; + +public: + CappedStorage(bool _biased); + + void store(const std::vector &d); + + long size() const{ return X.rows(); }; + + void clear(); + + Eigen::Matrix& getX(){return X;} + Eigen::Matrix& getY(){return y;} + long long getN(){return X.cols();} + long long getM(){return X.rows();} + + friend std::ostream &operator<<(std::ostream& os, const CappedStorage& c); + void lock(); + void unlock(); +}; + + + +#endif // CAPPEDSTORAGE_H diff --git a/ConditionMonitoring/easylogging++.h b/ConditionMonitoring/easylogging++.h new file mode 100644 index 0000000..688d537 --- /dev/null +++ b/ConditionMonitoring/easylogging++.h @@ -0,0 +1,4569 @@ +// +// Bismillah ar-Rahmaan ar-Raheem +// +// Easylogging++ v9.96.7 +// Single-header only, cross-platform logging library for C++ applications +// +// Copyright (c) 2012-2018 Zuhd Web Services +// Copyright (c) 2012-2018 @abumusamq +// +// This library is released under the MIT Licence. +// https://github.com/zuhd-org/easyloggingpp/blob/master/LICENSE +// +// https://zuhd.org +// http://muflihun.com +// + +#ifndef EASYLOGGINGPP_H +#define EASYLOGGINGPP_H +// Compilers and C++0x/C++11 Evaluation +#if __cplusplus >= 201103L +# define ELPP_CXX11 1 +#endif // __cplusplus >= 201103L +#if (defined(__GNUC__)) +# define ELPP_COMPILER_GCC 1 +#else +# define ELPP_COMPILER_GCC 0 +#endif +#if ELPP_COMPILER_GCC +# define ELPP_GCC_VERSION (__GNUC__ * 10000 \ ++ __GNUC_MINOR__ * 100 \ ++ __GNUC_PATCHLEVEL__) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ELPP_CXX0X 1 +# endif +#endif +// Visual C++ +#if defined(_MSC_VER) +# define ELPP_COMPILER_MSVC 1 +#else +# define ELPP_COMPILER_MSVC 0 +#endif +#define ELPP_CRT_DBG_WARNINGS ELPP_COMPILER_MSVC +#if ELPP_COMPILER_MSVC +# if (_MSC_VER == 1600) +# define ELPP_CXX0X 1 +# elif(_MSC_VER >= 1700) +# define ELPP_CXX11 1 +# endif +#endif +// Clang++ +#if (defined(__clang__) && (__clang__ == 1)) +# define ELPP_COMPILER_CLANG 1 +#else +# define ELPP_COMPILER_CLANG 0 +#endif +#if ELPP_COMPILER_CLANG +# if __has_include() +# include // Make __GLIBCXX__ defined when using libstdc++ +# if !defined(__GLIBCXX__) || __GLIBCXX__ >= 20150426 +# define ELPP_CLANG_SUPPORTS_THREAD +# endif // !defined(__GLIBCXX__) || __GLIBCXX__ >= 20150426 +# endif // __has_include() +#endif +#if (defined(__MINGW32__) || defined(__MINGW64__)) +# define ELPP_MINGW 1 +#else +# define ELPP_MINGW 0 +#endif +#if (defined(__CYGWIN__) && (__CYGWIN__ == 1)) +# define ELPP_CYGWIN 1 +#else +# define ELPP_CYGWIN 0 +#endif +#if (defined(__INTEL_COMPILER)) +# define ELPP_COMPILER_INTEL 1 +#else +# define ELPP_COMPILER_INTEL 0 +#endif +// Operating System Evaluation +// Windows +#if (defined(_WIN32) || defined(_WIN64)) +# define ELPP_OS_WINDOWS 1 +#else +# define ELPP_OS_WINDOWS 0 +#endif +// Linux +#if (defined(__linux) || defined(__linux__)) +# define ELPP_OS_LINUX 1 +#else +# define ELPP_OS_LINUX 0 +#endif +#if (defined(__APPLE__)) +# define ELPP_OS_MAC 1 +#else +# define ELPP_OS_MAC 0 +#endif +#if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) +# define ELPP_OS_FREEBSD 1 +#else +# define ELPP_OS_FREEBSD 0 +#endif +#if (defined(__sun)) +# define ELPP_OS_SOLARIS 1 +#else +# define ELPP_OS_SOLARIS 0 +#endif +#if (defined(_AIX)) +# define ELPP_OS_AIX 1 +#else +# define ELPP_OS_AIX 0 +#endif +#if (defined(__NetBSD__)) +# define ELPP_OS_NETBSD 1 +#else +# define ELPP_OS_NETBSD 0 +#endif +#if defined(__EMSCRIPTEN__) +# define ELPP_OS_EMSCRIPTEN 1 +#else +# define ELPP_OS_EMSCRIPTEN 0 +#endif +// Unix +#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_NETBSD || ELPP_OS_SOLARIS || ELPP_OS_AIX || ELPP_OS_EMSCRIPTEN) && (!ELPP_OS_WINDOWS)) +# define ELPP_OS_UNIX 1 +#else +# define ELPP_OS_UNIX 0 +#endif +#if (defined(__ANDROID__)) +# define ELPP_OS_ANDROID 1 +#else +# define ELPP_OS_ANDROID 0 +#endif +// Evaluating Cygwin as *nix OS +#if !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN +# undef ELPP_OS_UNIX +# undef ELPP_OS_LINUX +# define ELPP_OS_UNIX 1 +# define ELPP_OS_LINUX 1 +#endif // !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN +#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_INFO) +# define ELPP_INTERNAL_DEBUGGING_OUT_INFO std::cout +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_ERROR) +# define ELPP_INTERNAL_DEBUGGING_OUT_ERROR std::cerr +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_ENDL) +# define ELPP_INTERNAL_DEBUGGING_ENDL std::endl +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_MSG) +# define ELPP_INTERNAL_DEBUGGING_MSG(msg) msg +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +// Internal Assertions and errors +#if !defined(ELPP_DISABLE_ASSERT) +# if (defined(ELPP_DEBUG_ASSERT_FAILURE)) +# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ +std::stringstream internalInfoStream; internalInfoStream << msg; \ +ELPP_INTERNAL_DEBUGGING_OUT_ERROR \ +<< "EASYLOGGING++ ASSERTION FAILED (LINE: " << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" \ +<< ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" << ELPP_INTERNAL_DEBUGGING_ENDL; base::utils::abort(1, \ +"ELPP Assertion failure, please define ELPP_DEBUG_ASSERT_FAILURE"); } +# else +# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ +std::stringstream internalInfoStream; internalInfoStream << msg; \ +ELPP_INTERNAL_DEBUGGING_OUT_ERROR\ +<< "ASSERTION FAILURE FROM EASYLOGGING++ (LINE: " \ +<< __LINE__ << ") [" #expr << "] WITH MESSAGE \"" << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" \ +<< ELPP_INTERNAL_DEBUGGING_ENDL; } +# endif // (defined(ELPP_DEBUG_ASSERT_FAILURE)) +#else +# define ELPP_ASSERT(x, y) +#endif //(!defined(ELPP_DISABLE_ASSERT) +#if ELPP_COMPILER_MSVC +# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ +{ char buff[256]; strerror_s(buff, 256, errno); \ +ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << buff << " [" << errno << "]";} (void)0 +#else +# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ +ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << strerror(errno) << " [" << errno << "]"; (void)0 +#endif // ELPP_COMPILER_MSVC +#if defined(ELPP_DEBUG_ERRORS) +# if !defined(ELPP_INTERNAL_ERROR) +# define ELPP_INTERNAL_ERROR(msg, pe) { \ +std::stringstream internalInfoStream; internalInfoStream << " " << msg; \ +ELPP_INTERNAL_DEBUGGING_OUT_ERROR \ +<< "ERROR FROM EASYLOGGING++ (LINE: " << __LINE__ << ") " \ +<< ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << ELPP_INTERNAL_DEBUGGING_ENDL; \ +if (pe) { ELPP_INTERNAL_DEBUGGING_OUT_ERROR << " "; ELPP_INTERNAL_DEBUGGING_WRITE_PERROR; }} (void)0 +# endif +#else +# undef ELPP_INTERNAL_INFO +# define ELPP_INTERNAL_ERROR(msg, pe) +#endif // defined(ELPP_DEBUG_ERRORS) +#if (defined(ELPP_DEBUG_INFO)) +# if !(defined(ELPP_INTERNAL_INFO_LEVEL)) +# define ELPP_INTERNAL_INFO_LEVEL 9 +# endif // !(defined(ELPP_INTERNAL_INFO_LEVEL)) +# if !defined(ELPP_INTERNAL_INFO) +# define ELPP_INTERNAL_INFO(lvl, msg) { if (lvl <= ELPP_INTERNAL_INFO_LEVEL) { \ +std::stringstream internalInfoStream; internalInfoStream << " " << msg; \ +ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) \ +<< ELPP_INTERNAL_DEBUGGING_ENDL; }} +# endif +#else +# undef ELPP_INTERNAL_INFO +# define ELPP_INTERNAL_INFO(lvl, msg) +#endif // (defined(ELPP_DEBUG_INFO)) +#if (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG)) +# if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_OS_ANDROID && !ELPP_OS_EMSCRIPTEN) +# define ELPP_STACKTRACE 1 +# else +# if ELPP_COMPILER_MSVC +# pragma message("Stack trace not available for this compiler") +# else +# warning "Stack trace not available for this compiler"; +# endif // ELPP_COMPILER_MSVC +# define ELPP_STACKTRACE 0 +# endif // ELPP_COMPILER_GCC +#else +# define ELPP_STACKTRACE 0 +#endif // (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG)) +// Miscellaneous macros +#define ELPP_UNUSED(x) (void)x +#if ELPP_OS_UNIX +// Log file permissions for unix-based systems +# define ELPP_LOG_PERMS S_IRUSR | S_IWUSR | S_IXUSR | S_IWGRP | S_IRGRP | S_IXGRP | S_IWOTH | S_IXOTH +#endif // ELPP_OS_UNIX +#if defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC +# if defined(ELPP_EXPORT_SYMBOLS) +# define ELPP_EXPORT __declspec(dllexport) +# else +# define ELPP_EXPORT __declspec(dllimport) +# endif // defined(ELPP_EXPORT_SYMBOLS) +#else +# define ELPP_EXPORT +#endif // defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC +// Some special functions that are VC++ specific +#undef STRTOK +#undef STRERROR +#undef STRCAT +#undef STRCPY +#if ELPP_CRT_DBG_WARNINGS +# define STRTOK(a, b, c) strtok_s(a, b, c) +# define STRERROR(a, b, c) strerror_s(a, b, c) +# define STRCAT(a, b, len) strcat_s(a, len, b) +# define STRCPY(a, b, len) strcpy_s(a, len, b) +#else +# define STRTOK(a, b, c) strtok(a, b) +# define STRERROR(a, b, c) strerror(c) +# define STRCAT(a, b, len) strcat(a, b) +# define STRCPY(a, b, len) strcpy(a, b) +#endif +// Compiler specific support evaluations +#if (ELPP_MINGW && !defined(ELPP_FORCE_USE_STD_THREAD)) +# define ELPP_USE_STD_THREADING 0 +#else +# if ((ELPP_COMPILER_CLANG && defined(ELPP_CLANG_SUPPORTS_THREAD)) || \ + (!ELPP_COMPILER_CLANG && defined(ELPP_CXX11)) || \ + defined(ELPP_FORCE_USE_STD_THREAD)) +# define ELPP_USE_STD_THREADING 1 +# else +# define ELPP_USE_STD_THREADING 0 +# endif +#endif +#undef ELPP_FINAL +#if ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702) +# define ELPP_FINAL +#else +# define ELPP_FINAL final +#endif // ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702) +#if defined(ELPP_EXPERIMENTAL_ASYNC) +# define ELPP_ASYNC_LOGGING 1 +#else +# define ELPP_ASYNC_LOGGING 0 +#endif // defined(ELPP_EXPERIMENTAL_ASYNC) +#if defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING +# define ELPP_THREADING_ENABLED 1 +#else +# define ELPP_THREADING_ENABLED 0 +#endif // defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING +// Function macro ELPP_FUNC +#undef ELPP_FUNC +#if ELPP_COMPILER_MSVC // Visual C++ +# define ELPP_FUNC __FUNCSIG__ +#elif ELPP_COMPILER_GCC // GCC +# define ELPP_FUNC __PRETTY_FUNCTION__ +#elif ELPP_COMPILER_INTEL // Intel C++ +# define ELPP_FUNC __PRETTY_FUNCTION__ +#elif ELPP_COMPILER_CLANG // Clang++ +# define ELPP_FUNC __PRETTY_FUNCTION__ +#else +# if defined(__func__) +# define ELPP_FUNC __func__ +# else +# define ELPP_FUNC "" +# endif // defined(__func__) +#endif // defined(_MSC_VER) +#undef ELPP_VARIADIC_TEMPLATES_SUPPORTED +// Keep following line commented until features are fixed +#define ELPP_VARIADIC_TEMPLATES_SUPPORTED \ +(ELPP_COMPILER_GCC || ELPP_COMPILER_CLANG || ELPP_COMPILER_INTEL || (ELPP_COMPILER_MSVC && _MSC_VER >= 1800)) +// Logging Enable/Disable macros +#if defined(ELPP_DISABLE_LOGS) +#define ELPP_LOGGING_ENABLED 0 +#else +#define ELPP_LOGGING_ENABLED 1 +#endif +#if (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_DEBUG_LOG 1 +#else +# define ELPP_DEBUG_LOG 0 +#endif // (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_INFO_LOG 1 +#else +# define ELPP_INFO_LOG 0 +#endif // (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_WARNING_LOG 1 +#else +# define ELPP_WARNING_LOG 0 +#endif // (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_ERROR_LOG 1 +#else +# define ELPP_ERROR_LOG 0 +#endif // (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_FATAL_LOG 1 +#else +# define ELPP_FATAL_LOG 0 +#endif // (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_TRACE_LOG 1 +#else +# define ELPP_TRACE_LOG 0 +#endif // (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_VERBOSE_LOG 1 +#else +# define ELPP_VERBOSE_LOG 0 +#endif // (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!(ELPP_CXX0X || ELPP_CXX11)) +# error "C++0x (or higher) support not detected! (Is `-std=c++11' missing?)" +#endif // (!(ELPP_CXX0X || ELPP_CXX11)) +// Headers +#if defined(ELPP_SYSLOG) +# include +#endif // defined(ELPP_SYSLOG) +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(ELPP_UNICODE) +# include +# if ELPP_OS_WINDOWS +# include +# endif // ELPP_OS_WINDOWS +#endif // defined(ELPP_UNICODE) +#if ELPP_STACKTRACE +# include +# include +#endif // ELPP_STACKTRACE +#if ELPP_OS_ANDROID +# include +#endif // ELPP_OS_ANDROID +#if ELPP_OS_UNIX +# include +# include +#elif ELPP_OS_WINDOWS +# include +# include +# if defined(WIN32_LEAN_AND_MEAN) +# if defined(ELPP_WINSOCK2) +# include +# else +# include +# endif // defined(ELPP_WINSOCK2) +# endif // defined(WIN32_LEAN_AND_MEAN) +#endif // ELPP_OS_UNIX +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if ELPP_THREADING_ENABLED +# if ELPP_USE_STD_THREADING +# include +# include +# else +# if ELPP_OS_UNIX +# include +# endif // ELPP_OS_UNIX +# endif // ELPP_USE_STD_THREADING +#endif // ELPP_THREADING_ENABLED +#if ELPP_ASYNC_LOGGING +# if defined(ELPP_NO_SLEEP_FOR) +# include +# endif // defined(ELPP_NO_SLEEP_FOR) +# include +# include +# include +#endif // ELPP_ASYNC_LOGGING +#if defined(ELPP_STL_LOGGING) +// For logging STL based templates +# include +# include +# include +# include +# include +# include +# if defined(ELPP_LOG_STD_ARRAY) +# include +# endif // defined(ELPP_LOG_STD_ARRAY) +# if defined(ELPP_LOG_UNORDERED_SET) +# include +# endif // defined(ELPP_UNORDERED_SET) +#endif // defined(ELPP_STL_LOGGING) +#if defined(ELPP_QT_LOGGING) +// For logging Qt based classes & templates +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif // defined(ELPP_QT_LOGGING) +#if defined(ELPP_BOOST_LOGGING) +// For logging boost based classes & templates +# include +# include +# include +# include +# include +# include +# include +# include +#endif // defined(ELPP_BOOST_LOGGING) +#if defined(ELPP_WXWIDGETS_LOGGING) +// For logging wxWidgets based classes & templates +# include +#endif // defined(ELPP_WXWIDGETS_LOGGING) +#if defined(ELPP_UTC_DATETIME) +# define elpptime_r gmtime_r +# define elpptime_s gmtime_s +# define elpptime gmtime +#else +# define elpptime_r localtime_r +# define elpptime_s localtime_s +# define elpptime localtime +#endif // defined(ELPP_UTC_DATETIME) +// Forward declarations +namespace el { +class Logger; +class LogMessage; +class PerformanceTrackingData; +class Loggers; +class Helpers; +template class Callback; +class LogDispatchCallback; +class PerformanceTrackingCallback; +class LoggerRegistrationCallback; +class LogDispatchData; +namespace base { +class Storage; +class RegisteredLoggers; +class PerformanceTracker; +class MessageBuilder; +class Writer; +class PErrorWriter; +class LogDispatcher; +class DefaultLogBuilder; +class DefaultLogDispatchCallback; +#if ELPP_ASYNC_LOGGING +class AsyncLogDispatchCallback; +class AsyncDispatchWorker; +#endif // ELPP_ASYNC_LOGGING +class DefaultPerformanceTrackingCallback; +} // namespace base +} // namespace el +/// @brief Easylogging++ entry namespace +namespace el { +/// @brief Namespace containing base/internal functionality used by Easylogging++ +namespace base { +/// @brief Data types used by Easylogging++ +namespace type { +#undef ELPP_LITERAL +#undef ELPP_STRLEN +#undef ELPP_COUT +#if defined(ELPP_UNICODE) +# define ELPP_LITERAL(txt) L##txt +# define ELPP_STRLEN wcslen +# if defined ELPP_CUSTOM_COUT +# define ELPP_COUT ELPP_CUSTOM_COUT +# else +# define ELPP_COUT std::wcout +# endif // defined ELPP_CUSTOM_COUT +typedef wchar_t char_t; +typedef std::wstring string_t; +typedef std::wstringstream stringstream_t; +typedef std::wfstream fstream_t; +typedef std::wostream ostream_t; +#else +# define ELPP_LITERAL(txt) txt +# define ELPP_STRLEN strlen +# if defined ELPP_CUSTOM_COUT +# define ELPP_COUT ELPP_CUSTOM_COUT +# else +# define ELPP_COUT std::cout +# endif // defined ELPP_CUSTOM_COUT +typedef char char_t; +typedef std::string string_t; +typedef std::stringstream stringstream_t; +typedef std::fstream fstream_t; +typedef std::ostream ostream_t; +#endif // defined(ELPP_UNICODE) +#if defined(ELPP_CUSTOM_COUT_LINE) +# define ELPP_COUT_LINE(logLine) ELPP_CUSTOM_COUT_LINE(logLine) +#else +# define ELPP_COUT_LINE(logLine) logLine << std::flush +#endif // defined(ELPP_CUSTOM_COUT_LINE) +typedef unsigned int EnumType; +typedef unsigned short VerboseLevel; +typedef unsigned long int LineNumber; +typedef std::shared_ptr StoragePointer; +typedef std::shared_ptr LogDispatchCallbackPtr; +typedef std::shared_ptr PerformanceTrackingCallbackPtr; +typedef std::shared_ptr LoggerRegistrationCallbackPtr; +typedef std::unique_ptr PerformanceTrackerPtr; +} // namespace type +/// @brief Internal helper class that prevent copy constructor for class +/// +/// @detail When using this class simply inherit it privately +class NoCopy { + protected: + NoCopy(void) {} + private: + NoCopy(const NoCopy&); + NoCopy& operator=(const NoCopy&); +}; +/// @brief Internal helper class that makes all default constructors private. +/// +/// @detail This prevents initializing class making it static unless an explicit constructor is declared. +/// When using this class simply inherit it privately +class StaticClass { + private: + StaticClass(void); + StaticClass(const StaticClass&); + StaticClass& operator=(const StaticClass&); +}; +} // namespace base +/// @brief Represents enumeration for severity level used to determine level of logging +/// +/// @detail With Easylogging++, developers may disable or enable any level regardless of +/// what the severity is. Or they can choose to log using hierarchical logging flag +enum class Level : base::type::EnumType { + /// @brief Generic level that represents all the levels. Useful when setting global configuration for all levels + Global = 1, + /// @brief Information that can be useful to back-trace certain events - mostly useful than debug logs. + Trace = 2, + /// @brief Informational events most useful for developers to debug application + Debug = 4, + /// @brief Severe error information that will presumably abort application + Fatal = 8, + /// @brief Information representing errors in application but application will keep running + Error = 16, + /// @brief Useful when application has potentially harmful situtaions + Warning = 32, + /// @brief Information that can be highly useful and vary with verbose logging level. + Verbose = 64, + /// @brief Mainly useful to represent current progress of application + Info = 128, + /// @brief Represents unknown level + Unknown = 1010 +}; +} // namespace el +namespace std { +template<> struct hash { + public: + std::size_t operator()(const el::Level& l) const { + return hash {}(static_cast(l)); + } +}; +} +namespace el { +/// @brief Static class that contains helper functions for el::Level +class LevelHelper : base::StaticClass { + public: + /// @brief Represents minimum valid level. Useful when iterating through enum. + static const base::type::EnumType kMinValid = static_cast(Level::Trace); + /// @brief Represents maximum valid level. This is used internally and you should not need it. + static const base::type::EnumType kMaxValid = static_cast(Level::Info); + /// @brief Casts level to int, useful for iterating through enum. + static base::type::EnumType castToInt(Level level) { + return static_cast(level); + } + /// @brief Casts int(ushort) to level, useful for iterating through enum. + static Level castFromInt(base::type::EnumType l) { + return static_cast(l); + } + /// @brief Converts level to associated const char* + /// @return Upper case string based level. + static const char* convertToString(Level level); + /// @brief Converts from levelStr to Level + /// @param levelStr Upper case string based level. + /// Lower case is also valid but providing upper case is recommended. + static Level convertFromString(const char* levelStr); + /// @brief Applies specified function to each level starting from startIndex + /// @param startIndex initial value to start the iteration from. This is passed as pointer and + /// is left-shifted so this can be used inside function (fn) to represent current level. + /// @param fn function to apply with each level. This bool represent whether or not to stop iterating through levels. + static void forEachLevel(base::type::EnumType* startIndex, const std::function& fn); +}; +/// @brief Represents enumeration of ConfigurationType used to configure or access certain aspect +/// of logging +enum class ConfigurationType : base::type::EnumType { + /// @brief Determines whether or not corresponding level and logger of logging is enabled + /// You may disable all logs by using el::Level::Global + Enabled = 1, + /// @brief Whether or not to write corresponding log to log file + ToFile = 2, + /// @brief Whether or not to write corresponding level and logger log to standard output. + /// By standard output meaning termnal, command prompt etc + ToStandardOutput = 4, + /// @brief Determines format of logging corresponding level and logger. + Format = 8, + /// @brief Determines log file (full path) to write logs to for correponding level and logger + Filename = 16, + /// @brief Specifies precision of the subsecond part. It should be within range (1-6). + SubsecondPrecision = 32, + /// @brief Alias of SubsecondPrecision (for backward compatibility) + MillisecondsWidth = SubsecondPrecision, + /// @brief Determines whether or not performance tracking is enabled. + /// + /// @detail This does not depend on logger or level. Performance tracking always uses 'performance' logger + PerformanceTracking = 64, + /// @brief Specifies log file max size. + /// + /// @detail If file size of corresponding log file (for corresponding level) is >= specified size, log file will + /// be truncated and re-initiated. + MaxLogFileSize = 128, + /// @brief Specifies number of log entries to hold until we flush pending log data + LogFlushThreshold = 256, + /// @brief Represents unknown configuration + Unknown = 1010 +}; +/// @brief Static class that contains helper functions for el::ConfigurationType +class ConfigurationTypeHelper : base::StaticClass { + public: + /// @brief Represents minimum valid configuration type. Useful when iterating through enum. + static const base::type::EnumType kMinValid = static_cast(ConfigurationType::Enabled); + /// @brief Represents maximum valid configuration type. This is used internally and you should not need it. + static const base::type::EnumType kMaxValid = static_cast(ConfigurationType::MaxLogFileSize); + /// @brief Casts configuration type to int, useful for iterating through enum. + static base::type::EnumType castToInt(ConfigurationType configurationType) { + return static_cast(configurationType); + } + /// @brief Casts int(ushort) to configurationt type, useful for iterating through enum. + static ConfigurationType castFromInt(base::type::EnumType c) { + return static_cast(c); + } + /// @brief Converts configuration type to associated const char* + /// @returns Upper case string based configuration type. + static const char* convertToString(ConfigurationType configurationType); + /// @brief Converts from configStr to ConfigurationType + /// @param configStr Upper case string based configuration type. + /// Lower case is also valid but providing upper case is recommended. + static ConfigurationType convertFromString(const char* configStr); + /// @brief Applies specified function to each configuration type starting from startIndex + /// @param startIndex initial value to start the iteration from. This is passed by pointer and is left-shifted + /// so this can be used inside function (fn) to represent current configuration type. + /// @param fn function to apply with each configuration type. + /// This bool represent whether or not to stop iterating through configurations. + static inline void forEachConfigType(base::type::EnumType* startIndex, const std::function& fn); +}; +/// @brief Flags used while writing logs. This flags are set by user +enum class LoggingFlag : base::type::EnumType { + /// @brief Makes sure we have new line for each container log entry + NewLineForContainer = 1, + /// @brief Makes sure if -vmodule is used and does not specifies a module, then verbose + /// logging is allowed via that module. + AllowVerboseIfModuleNotSpecified = 2, + /// @brief When handling crashes by default, detailed crash reason will be logged as well + LogDetailedCrashReason = 4, + /// @brief Allows to disable application abortion when logged using FATAL level + DisableApplicationAbortOnFatalLog = 8, + /// @brief Flushes log with every log-entry (performance sensative) - Disabled by default + ImmediateFlush = 16, + /// @brief Enables strict file rolling + StrictLogFileSizeCheck = 32, + /// @brief Make terminal output colorful for supported terminals + ColoredTerminalOutput = 64, + /// @brief Supports use of multiple logging in same macro, e.g, CLOG(INFO, "default", "network") + MultiLoggerSupport = 128, + /// @brief Disables comparing performance tracker's checkpoints + DisablePerformanceTrackingCheckpointComparison = 256, + /// @brief Disable VModules + DisableVModules = 512, + /// @brief Disable VModules extensions + DisableVModulesExtensions = 1024, + /// @brief Enables hierarchical logging + HierarchicalLogging = 2048, + /// @brief Creates logger automatically when not available + CreateLoggerAutomatically = 4096, + /// @brief Adds spaces b/w logs that separated by left-shift operator + AutoSpacing = 8192, + /// @brief Preserves time format and does not convert it to sec, hour etc (performance tracking only) + FixedTimeFormat = 16384, + // @brief Ignore SIGINT or crash + IgnoreSigInt = 32768, +}; +namespace base { +/// @brief Namespace containing constants used internally. +namespace consts { +static const char kFormatSpecifierCharValue = 'v'; +static const char kFormatSpecifierChar = '%'; +static const unsigned int kMaxLogPerCounter = 100000; +static const unsigned int kMaxLogPerContainer = 100; +static const unsigned int kDefaultSubsecondPrecision = 3; + +#ifdef ELPP_DEFAULT_LOGGER +static const char* kDefaultLoggerId = ELPP_DEFAULT_LOGGER; +#else +static const char* kDefaultLoggerId = "default"; +#endif + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) +#ifdef ELPP_DEFAULT_PERFORMANCE_LOGGER +static const char* kPerformanceLoggerId = ELPP_DEFAULT_PERFORMANCE_LOGGER; +#else +static const char* kPerformanceLoggerId = "performance"; +#endif // ELPP_DEFAULT_PERFORMANCE_LOGGER +#endif + +#if defined(ELPP_SYSLOG) +static const char* kSysLogLoggerId = "syslog"; +#endif // defined(ELPP_SYSLOG) + +#if ELPP_OS_WINDOWS +static const char* kFilePathSeperator = "\\"; +#else +static const char* kFilePathSeperator = "/"; +#endif // ELPP_OS_WINDOWS + +static const std::size_t kSourceFilenameMaxLength = 100; +static const std::size_t kSourceLineMaxLength = 10; +static const Level kPerformanceTrackerDefaultLevel = Level::Info; +const struct { + double value; + const base::type::char_t* unit; +} kTimeFormats[] = { + { 1000.0f, ELPP_LITERAL("us") }, + { 1000.0f, ELPP_LITERAL("ms") }, + { 60.0f, ELPP_LITERAL("seconds") }, + { 60.0f, ELPP_LITERAL("minutes") }, + { 24.0f, ELPP_LITERAL("hours") }, + { 7.0f, ELPP_LITERAL("days") } +}; +static const int kTimeFormatsCount = sizeof(kTimeFormats) / sizeof(kTimeFormats[0]); +const struct { + int numb; + const char* name; + const char* brief; + const char* detail; +} kCrashSignals[] = { + // NOTE: Do not re-order, if you do please check CrashHandler(bool) constructor and CrashHandler::setHandler(..) + { + SIGABRT, "SIGABRT", "Abnormal termination", + "Program was abnormally terminated." + }, + { + SIGFPE, "SIGFPE", "Erroneous arithmetic operation", + "Arithemetic operation issue such as division by zero or operation resulting in overflow." + }, + { + SIGILL, "SIGILL", "Illegal instruction", + "Generally due to a corruption in the code or to an attempt to execute data." + }, + { + SIGSEGV, "SIGSEGV", "Invalid access to memory", + "Program is trying to read an invalid (unallocated, deleted or corrupted) or inaccessible memory." + }, + { + SIGINT, "SIGINT", "Interactive attention signal", + "Interruption generated (generally) by user or operating system." + }, +}; +static const int kCrashSignalsCount = sizeof(kCrashSignals) / sizeof(kCrashSignals[0]); +} // namespace consts +} // namespace base +typedef std::function PreRollOutCallback; +namespace base { +static inline void defaultPreRollOutCallback(const char*, std::size_t) {} +/// @brief Enum to represent timestamp unit +enum class TimestampUnit : base::type::EnumType { + Microsecond = 0, Millisecond = 1, Second = 2, Minute = 3, Hour = 4, Day = 5 +}; +/// @brief Format flags used to determine specifiers that are active for performance improvements. +enum class FormatFlags : base::type::EnumType { + DateTime = 1 << 1, + LoggerId = 1 << 2, + File = 1 << 3, + Line = 1 << 4, + Location = 1 << 5, + Function = 1 << 6, + User = 1 << 7, + Host = 1 << 8, + LogMessage = 1 << 9, + VerboseLevel = 1 << 10, + AppName = 1 << 11, + ThreadId = 1 << 12, + Level = 1 << 13, + FileBase = 1 << 14, + LevelShort = 1 << 15 +}; +/// @brief A subsecond precision class containing actual width and offset of the subsecond part +class SubsecondPrecision { + public: + SubsecondPrecision(void) { + init(base::consts::kDefaultSubsecondPrecision); + } + explicit SubsecondPrecision(int width) { + init(width); + } + bool operator==(const SubsecondPrecision& ssPrec) { + return m_width == ssPrec.m_width && m_offset == ssPrec.m_offset; + } + int m_width; + unsigned int m_offset; + private: + void init(int width); +}; +/// @brief Type alias of SubsecondPrecision +typedef SubsecondPrecision MillisecondsWidth; +/// @brief Namespace containing utility functions/static classes used internally +namespace utils { +/// @brief Deletes memory safely and points to null +template +static +typename std::enable_if::value, void>::type +safeDelete(T*& pointer) { + if (pointer == nullptr) + return; + delete pointer; + pointer = nullptr; +} +/// @brief Bitwise operations for C++11 strong enum class. This casts e into Flag_T and returns value after bitwise operation +/// Use these function as
flag = bitwise::Or(MyEnum::val1, flag);
+namespace bitwise { +template +static inline base::type::EnumType And(Enum e, base::type::EnumType flag) { + return static_cast(flag) & static_cast(e); +} +template +static inline base::type::EnumType Not(Enum e, base::type::EnumType flag) { + return static_cast(flag) & ~(static_cast(e)); +} +template +static inline base::type::EnumType Or(Enum e, base::type::EnumType flag) { + return static_cast(flag) | static_cast(e); +} +} // namespace bitwise +template +static inline void addFlag(Enum e, base::type::EnumType* flag) { + *flag = base::utils::bitwise::Or(e, *flag); +} +template +static inline void removeFlag(Enum e, base::type::EnumType* flag) { + *flag = base::utils::bitwise::Not(e, *flag); +} +template +static inline bool hasFlag(Enum e, base::type::EnumType flag) { + return base::utils::bitwise::And(e, flag) > 0x0; +} +} // namespace utils +namespace threading { +#if ELPP_THREADING_ENABLED +# if !ELPP_USE_STD_THREADING +namespace internal { +/// @brief A mutex wrapper for compiler that dont yet support std::recursive_mutex +class Mutex : base::NoCopy { + public: + Mutex(void) { +# if ELPP_OS_UNIX + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&m_underlyingMutex, &attr); + pthread_mutexattr_destroy(&attr); +# elif ELPP_OS_WINDOWS + InitializeCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + virtual ~Mutex(void) { +# if ELPP_OS_UNIX + pthread_mutex_destroy(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + DeleteCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline void lock(void) { +# if ELPP_OS_UNIX + pthread_mutex_lock(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + EnterCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline bool try_lock(void) { +# if ELPP_OS_UNIX + return (pthread_mutex_trylock(&m_underlyingMutex) == 0); +# elif ELPP_OS_WINDOWS + return TryEnterCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline void unlock(void) { +# if ELPP_OS_UNIX + pthread_mutex_unlock(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + LeaveCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + private: +# if ELPP_OS_UNIX + pthread_mutex_t m_underlyingMutex; +# elif ELPP_OS_WINDOWS + CRITICAL_SECTION m_underlyingMutex; +# endif // ELPP_OS_UNIX +}; +/// @brief Scoped lock for compiler that dont yet support std::lock_guard +template +class ScopedLock : base::NoCopy { + public: + explicit ScopedLock(M& mutex) { + m_mutex = &mutex; + m_mutex->lock(); + } + + virtual ~ScopedLock(void) { + m_mutex->unlock(); + } + private: + M* m_mutex; + ScopedLock(void); +}; +} // namespace internal +typedef base::threading::internal::Mutex Mutex; +typedef base::threading::internal::ScopedLock ScopedLock; +# else +typedef std::recursive_mutex Mutex; +typedef std::lock_guard ScopedLock; +# endif // !ELPP_USE_STD_THREADING +#else +namespace internal { +/// @brief Mutex wrapper used when multi-threading is disabled. +class NoMutex : base::NoCopy { + public: + NoMutex(void) {} + inline void lock(void) {} + inline bool try_lock(void) { + return true; + } + inline void unlock(void) {} +}; +/// @brief Lock guard wrapper used when multi-threading is disabled. +template +class NoScopedLock : base::NoCopy { + public: + explicit NoScopedLock(Mutex&) { + } + virtual ~NoScopedLock(void) { + } + private: + NoScopedLock(void); +}; +} // namespace internal +typedef base::threading::internal::NoMutex Mutex; +typedef base::threading::internal::NoScopedLock ScopedLock; +#endif // ELPP_THREADING_ENABLED +/// @brief Base of thread safe class, this class is inheritable-only +class ThreadSafe { + public: + virtual inline void acquireLock(void) ELPP_FINAL { m_mutex.lock(); } + virtual inline void releaseLock(void) ELPP_FINAL { m_mutex.unlock(); } + virtual inline base::threading::Mutex& lock(void) ELPP_FINAL { return m_mutex; } + protected: + ThreadSafe(void) {} + virtual ~ThreadSafe(void) {} + private: + base::threading::Mutex m_mutex; +}; + +#if ELPP_THREADING_ENABLED +# if !ELPP_USE_STD_THREADING +/// @brief Gets ID of currently running threading in windows systems. On unix, nothing is returned. +static std::string getCurrentThreadId(void) { + std::stringstream ss; +# if (ELPP_OS_WINDOWS) + ss << GetCurrentThreadId(); +# endif // (ELPP_OS_WINDOWS) + return ss.str(); +} +# else +/// @brief Gets ID of currently running threading using std::this_thread::get_id() +static std::string getCurrentThreadId(void) { + std::stringstream ss; + ss << std::this_thread::get_id(); + return ss.str(); +} +# endif // !ELPP_USE_STD_THREADING +#else +static inline std::string getCurrentThreadId(void) { + return std::string(); +} +#endif // ELPP_THREADING_ENABLED +} // namespace threading +namespace utils { +class File : base::StaticClass { + public: + /// @brief Creates new out file stream for specified filename. + /// @return Pointer to newly created fstream or nullptr + static base::type::fstream_t* newFileStream(const std::string& filename); + + /// @brief Gets size of file provided in stream + static std::size_t getSizeOfFile(base::type::fstream_t* fs); + + /// @brief Determines whether or not provided path exist in current file system + static bool pathExists(const char* path, bool considerFile = false); + + /// @brief Creates specified path on file system + /// @param path Path to create. + static bool createPath(const std::string& path); + /// @brief Extracts path of filename with leading slash + static std::string extractPathFromFilename(const std::string& fullPath, + const char* seperator = base::consts::kFilePathSeperator); + /// @brief builds stripped filename and puts it in buff + static void buildStrippedFilename(const char* filename, char buff[], + std::size_t limit = base::consts::kSourceFilenameMaxLength); + /// @brief builds base filename and puts it in buff + static void buildBaseFilename(const std::string& fullPath, char buff[], + std::size_t limit = base::consts::kSourceFilenameMaxLength, + const char* seperator = base::consts::kFilePathSeperator); +}; +/// @brief String utilities helper class used internally. You should not use it. +class Str : base::StaticClass { + public: + /// @brief Checks if character is digit. Dont use libc implementation of it to prevent locale issues. + static inline bool isDigit(char c) { + return c >= '0' && c <= '9'; + } + + /// @brief Matches wildcards, '*' and '?' only supported. + static bool wildCardMatch(const char* str, const char* pattern); + + static std::string& ltrim(std::string& str); + static std::string& rtrim(std::string& str); + static std::string& trim(std::string& str); + + /// @brief Determines whether or not str starts with specified string + /// @param str String to check + /// @param start String to check against + /// @return Returns true if starts with specified string, false otherwise + static bool startsWith(const std::string& str, const std::string& start); + + /// @brief Determines whether or not str ends with specified string + /// @param str String to check + /// @param end String to check against + /// @return Returns true if ends with specified string, false otherwise + static bool endsWith(const std::string& str, const std::string& end); + + /// @brief Replaces all instances of replaceWhat with 'replaceWith'. Original variable is changed for performance. + /// @param [in,out] str String to replace from + /// @param replaceWhat Character to replace + /// @param replaceWith Character to replace with + /// @return Modified version of str + static std::string& replaceAll(std::string& str, char replaceWhat, char replaceWith); + + /// @brief Replaces all instances of 'replaceWhat' with 'replaceWith'. (String version) Replaces in place + /// @param str String to replace from + /// @param replaceWhat Character to replace + /// @param replaceWith Character to replace with + /// @return Modified (original) str + static std::string& replaceAll(std::string& str, const std::string& replaceWhat, + const std::string& replaceWith); + + static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const base::type::string_t& replaceWith); +#if defined(ELPP_UNICODE) + static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const std::string& replaceWith); +#endif // defined(ELPP_UNICODE) + /// @brief Converts string to uppercase + /// @param str String to convert + /// @return Uppercase string + static std::string& toUpper(std::string& str); + + /// @brief Compares cstring equality - uses strcmp + static bool cStringEq(const char* s1, const char* s2); + + /// @brief Compares cstring equality (case-insensitive) - uses toupper(char) + /// Dont use strcasecmp because of CRT (VC++) + static bool cStringCaseEq(const char* s1, const char* s2); + + /// @brief Returns true if c exist in str + static bool contains(const char* str, char c); + + static char* convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded = true); + static char* addToBuff(const char* str, char* buf, const char* bufLim); + static char* clearBuff(char buff[], std::size_t lim); + + /// @brief Converst wchar* to char* + /// NOTE: Need to free return value after use! + static char* wcharPtrToCharPtr(const wchar_t* line); +}; +/// @brief Operating System helper static class used internally. You should not use it. +class OS : base::StaticClass { + public: +#if ELPP_OS_WINDOWS + /// @brief Gets environment variables for Windows based OS. + /// We are not using getenv(const char*) because of CRT deprecation + /// @param varname Variable name to get environment variable value for + /// @return If variable exist the value of it otherwise nullptr + static const char* getWindowsEnvironmentVariable(const char* varname); +#endif // ELPP_OS_WINDOWS +#if ELPP_OS_ANDROID + /// @brief Reads android property value + static std::string getProperty(const char* prop); + + /// @brief Reads android device name + static std::string getDeviceName(void); +#endif // ELPP_OS_ANDROID + + /// @brief Runs command on terminal and returns the output. + /// + /// @detail This is applicable only on unix based systems, for all other OS, an empty string is returned. + /// @param command Bash command + /// @return Result of bash output or empty string if no result found. + static const std::string getBashOutput(const char* command); + + /// @brief Gets environment variable. This is cross-platform and CRT safe (for VC++) + /// @param variableName Environment variable name + /// @param defaultVal If no environment variable or value found the value to return by default + /// @param alternativeBashCommand If environment variable not found what would be alternative bash command + /// in order to look for value user is looking for. E.g, for 'user' alternative command will 'whoami' + static std::string getEnvironmentVariable(const char* variableName, const char* defaultVal, + const char* alternativeBashCommand = nullptr); + /// @brief Gets current username. + static std::string currentUser(void); + + /// @brief Gets current host name or computer name. + /// + /// @detail For android systems this is device name with its manufacturer and model seperated by hyphen + static std::string currentHost(void); + /// @brief Whether or not terminal supports colors + static bool termSupportsColor(void); +}; +/// @brief Contains utilities for cross-platform date/time. This class make use of el::base::utils::Str +class DateTime : base::StaticClass { + public: + /// @brief Cross platform gettimeofday for Windows and unix platform. This can be used to determine current microsecond. + /// + /// @detail For unix system it uses gettimeofday(timeval*, timezone*) and for Windows, a seperate implementation is provided + /// @param [in,out] tv Pointer that gets updated + static void gettimeofday(struct timeval* tv); + + /// @brief Gets current date and time with a subsecond part. + /// @param format User provided date/time format + /// @param ssPrec A pointer to base::SubsecondPrecision from configuration (non-null) + /// @returns string based date time in specified format. + static std::string getDateTime(const char* format, const base::SubsecondPrecision* ssPrec); + + /// @brief Converts timeval (struct from ctime) to string using specified format and subsecond precision + static std::string timevalToString(struct timeval tval, const char* format, + const el::base::SubsecondPrecision* ssPrec); + + /// @brief Formats time to get unit accordingly, units like second if > 1000 or minutes if > 60000 etc + static base::type::string_t formatTime(unsigned long long time, base::TimestampUnit timestampUnit); + + /// @brief Gets time difference in milli/micro second depending on timestampUnit + static unsigned long long getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, + base::TimestampUnit timestampUnit); + + + static struct ::tm* buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo); + private: + static char* parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, + std::size_t msec, const base::SubsecondPrecision* ssPrec); +}; +/// @brief Command line arguments for application if specified using el::Helpers::setArgs(..) or START_EASYLOGGINGPP(..) +class CommandLineArgs { + public: + CommandLineArgs(void) { + setArgs(0, static_cast(nullptr)); + } + CommandLineArgs(int argc, const char** argv) { + setArgs(argc, argv); + } + CommandLineArgs(int argc, char** argv) { + setArgs(argc, argv); + } + virtual ~CommandLineArgs(void) {} + /// @brief Sets arguments and parses them + inline void setArgs(int argc, const char** argv) { + setArgs(argc, const_cast(argv)); + } + /// @brief Sets arguments and parses them + void setArgs(int argc, char** argv); + /// @brief Returns true if arguments contain paramKey with a value (seperated by '=') + bool hasParamWithValue(const char* paramKey) const; + /// @brief Returns value of arguments + /// @see hasParamWithValue(const char*) + const char* getParamValue(const char* paramKey) const; + /// @brief Return true if arguments has a param (not having a value) i,e without '=' + bool hasParam(const char* paramKey) const; + /// @brief Returns true if no params available. This exclude argv[0] + bool empty(void) const; + /// @brief Returns total number of arguments. This exclude argv[0] + std::size_t size(void) const; + friend base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c); + + private: + int m_argc; + char** m_argv; + std::unordered_map m_paramsWithValue; + std::vector m_params; +}; +/// @brief Abstract registry (aka repository) that provides basic interface for pointer repository specified by T_Ptr type. +/// +/// @detail Most of the functions are virtual final methods but anything implementing this abstract class should implement +/// unregisterAll() and deepCopy(const AbstractRegistry&) and write registerNew() method according to container +/// and few more methods; get() to find element, unregister() to unregister single entry. +/// Please note that this is thread-unsafe and should also implement thread-safety mechanisms in implementation. +template +class AbstractRegistry : public base::threading::ThreadSafe { + public: + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + + /// @brief Default constructor + AbstractRegistry(void) {} + + /// @brief Move constructor that is useful for base classes + AbstractRegistry(AbstractRegistry&& sr) { + if (this == &sr) { + return; + } + unregisterAll(); + m_list = std::move(sr.m_list); + } + + bool operator==(const AbstractRegistry& other) { + if (size() != other.size()) { + return false; + } + for (std::size_t i = 0; i < m_list.size(); ++i) { + if (m_list.at(i) != other.m_list.at(i)) { + return false; + } + } + return true; + } + + bool operator!=(const AbstractRegistry& other) { + if (size() != other.size()) { + return true; + } + for (std::size_t i = 0; i < m_list.size(); ++i) { + if (m_list.at(i) != other.m_list.at(i)) { + return true; + } + } + return false; + } + + /// @brief Assignment move operator + AbstractRegistry& operator=(AbstractRegistry&& sr) { + if (this == &sr) { + return *this; + } + unregisterAll(); + m_list = std::move(sr.m_list); + return *this; + } + + virtual ~AbstractRegistry(void) { + } + + /// @return Iterator pointer from start of repository + virtual inline iterator begin(void) ELPP_FINAL { + return m_list.begin(); + } + + /// @return Iterator pointer from end of repository + virtual inline iterator end(void) ELPP_FINAL { + return m_list.end(); + } + + + /// @return Constant iterator pointer from start of repository + virtual inline const_iterator cbegin(void) const ELPP_FINAL { + return m_list.cbegin(); + } + + /// @return End of repository + virtual inline const_iterator cend(void) const ELPP_FINAL { + return m_list.cend(); + } + + /// @return Whether or not repository is empty + virtual inline bool empty(void) const ELPP_FINAL { + return m_list.empty(); + } + + /// @return Size of repository + virtual inline std::size_t size(void) const ELPP_FINAL { + return m_list.size(); + } + + /// @brief Returns underlying container by reference + virtual inline Container& list(void) ELPP_FINAL { + return m_list; + } + + /// @brief Returns underlying container by constant reference. + virtual inline const Container& list(void) const ELPP_FINAL { + return m_list; + } + + /// @brief Unregisters all the pointers from current repository. + virtual void unregisterAll(void) = 0; + + protected: + virtual void deepCopy(const AbstractRegistry&) = 0; + void reinitDeepCopy(const AbstractRegistry& sr) { + unregisterAll(); + deepCopy(sr); + } + + private: + Container m_list; +}; + +/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (non-predicate version) +/// +/// @detail NOTE: This is thread-unsafe implementation (although it contains lock function, it does not use these functions) +/// of AbstractRegistry. Any implementation of this class should be +/// explicitly (by using lock functions) +template +class Registry : public AbstractRegistry> { + public: + typedef typename Registry::iterator iterator; + typedef typename Registry::const_iterator const_iterator; + + Registry(void) {} + + /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. + Registry(const Registry& sr) : AbstractRegistry>() { + if (this == &sr) { + return; + } + this->reinitDeepCopy(sr); + } + + /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element + /// @see unregisterAll() + /// @see deepCopy(const AbstractRegistry&) + Registry& operator=(const Registry& sr) { + if (this == &sr) { + return *this; + } + this->reinitDeepCopy(sr); + return *this; + } + + virtual ~Registry(void) { + unregisterAll(); + } + + protected: + virtual void unregisterAll(void) ELPP_FINAL { + if (!this->empty()) { + for (auto&& curr : this->list()) { + base::utils::safeDelete(curr.second); + } + this->list().clear(); + } + } + +/// @brief Registers new registry to repository. + virtual void registerNew(const T_Key& uniqKey, T_Ptr* ptr) ELPP_FINAL { + unregister(uniqKey); + this->list().insert(std::make_pair(uniqKey, ptr)); + } + +/// @brief Unregisters single entry mapped to specified unique key + void unregister(const T_Key& uniqKey) { + T_Ptr* existing = get(uniqKey); + if (existing != nullptr) { + this->list().erase(uniqKey); + base::utils::safeDelete(existing); + } + } + +/// @brief Gets pointer from repository. If none found, nullptr is returned. + T_Ptr* get(const T_Key& uniqKey) { + iterator it = this->list().find(uniqKey); + return it == this->list().end() + ? nullptr + : it->second; + } + + private: + virtual void deepCopy(const AbstractRegistry>& sr) ELPP_FINAL { + for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) { + registerNew(it->first, new T_Ptr(*it->second)); + } + } +}; + +/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (predicate version) +/// +/// @detail NOTE: This is thread-unsafe implementation of AbstractRegistry. Any implementation of this class +/// should be made thread-safe explicitly +template +class RegistryWithPred : public AbstractRegistry> { + public: + typedef typename RegistryWithPred::iterator iterator; + typedef typename RegistryWithPred::const_iterator const_iterator; + + RegistryWithPred(void) { + } + + virtual ~RegistryWithPred(void) { + unregisterAll(); + } + + /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. + RegistryWithPred(const RegistryWithPred& sr) : AbstractRegistry>() { + if (this == &sr) { + return; + } + this->reinitDeepCopy(sr); + } + + /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element + /// @see unregisterAll() + /// @see deepCopy(const AbstractRegistry&) + RegistryWithPred& operator=(const RegistryWithPred& sr) { + if (this == &sr) { + return *this; + } + this->reinitDeepCopy(sr); + return *this; + } + + friend base::type::ostream_t& operator<<(base::type::ostream_t& os, const RegistryWithPred& sr) { + for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { + os << ELPP_LITERAL(" ") << **it << ELPP_LITERAL("\n"); + } + return os; + } + + protected: + virtual void unregisterAll(void) ELPP_FINAL { + if (!this->empty()) { + for (auto&& curr : this->list()) { + base::utils::safeDelete(curr); + } + this->list().clear(); + } + } + + virtual void unregister(T_Ptr*& ptr) ELPP_FINAL { + if (ptr) { + iterator iter = this->begin(); + for (; iter != this->end(); ++iter) { + if (ptr == *iter) { + break; + } + } + if (iter != this->end() && *iter != nullptr) { + this->list().erase(iter); + base::utils::safeDelete(*iter); + } + } + } + + virtual inline void registerNew(T_Ptr* ptr) ELPP_FINAL { + this->list().push_back(ptr); + } + +/// @brief Gets pointer from repository with speicifed arguments. Arguments are passed to predicate +/// in order to validate pointer. + template + T_Ptr* get(const T& arg1, const T2 arg2) { + iterator iter = std::find_if(this->list().begin(), this->list().end(), Pred(arg1, arg2)); + if (iter != this->list().end() && *iter != nullptr) { + return *iter; + } + return nullptr; + } + + private: + virtual void deepCopy(const AbstractRegistry>& sr) { + for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { + registerNew(new T_Ptr(**it)); + } + } +}; +class Utils { + public: + template + static bool installCallback(const std::string& id, std::unordered_map* mapT) { + if (mapT->find(id) == mapT->end()) { + mapT->insert(std::make_pair(id, TPtr(new T()))); + return true; + } + return false; + } + + template + static void uninstallCallback(const std::string& id, std::unordered_map* mapT) { + if (mapT->find(id) != mapT->end()) { + mapT->erase(id); + } + } + + template + static T* callback(const std::string& id, std::unordered_map* mapT) { + typename std::unordered_map::iterator iter = mapT->find(id); + if (iter != mapT->end()) { + return static_cast(iter->second.get()); + } + return nullptr; + } +}; +} // namespace utils +} // namespace base +/// @brief Base of Easylogging++ friendly class +/// +/// @detail After inheriting this class publicly, implement pure-virtual function `void log(std::ostream&) const` +class Loggable { + public: + virtual ~Loggable(void) {} + virtual void log(el::base::type::ostream_t&) const = 0; + private: + friend inline el::base::type::ostream_t& operator<<(el::base::type::ostream_t& os, const Loggable& loggable) { + loggable.log(os); + return os; + } +}; +namespace base { +/// @brief Represents log format containing flags and date format. This is used internally to start initial log +class LogFormat : public Loggable { + public: + LogFormat(void); + LogFormat(Level level, const base::type::string_t& format); + LogFormat(const LogFormat& logFormat); + LogFormat(LogFormat&& logFormat); + LogFormat& operator=(const LogFormat& logFormat); + virtual ~LogFormat(void) {} + bool operator==(const LogFormat& other); + + /// @brief Updates format to be used while logging. + /// @param userFormat User provided format + void parseFromFormat(const base::type::string_t& userFormat); + + inline Level level(void) const { + return m_level; + } + + inline const base::type::string_t& userFormat(void) const { + return m_userFormat; + } + + inline const base::type::string_t& format(void) const { + return m_format; + } + + inline const std::string& dateTimeFormat(void) const { + return m_dateTimeFormat; + } + + inline base::type::EnumType flags(void) const { + return m_flags; + } + + inline bool hasFlag(base::FormatFlags flag) const { + return base::utils::hasFlag(flag, m_flags); + } + + virtual void log(el::base::type::ostream_t& os) const { + os << m_format; + } + + protected: + /// @brief Updates date time format if available in currFormat. + /// @param index Index where %datetime, %date or %time was found + /// @param [in,out] currFormat current format that is being used to format + virtual void updateDateFormat(std::size_t index, base::type::string_t& currFormat) ELPP_FINAL; + + /// @brief Updates %level from format. This is so that we dont have to do it at log-writing-time. It uses m_format and m_level + virtual void updateFormatSpec(void) ELPP_FINAL; + + inline void addFlag(base::FormatFlags flag) { + base::utils::addFlag(flag, &m_flags); + } + + private: + Level m_level; + base::type::string_t m_userFormat; + base::type::string_t m_format; + std::string m_dateTimeFormat; + base::type::EnumType m_flags; + std::string m_currentUser; + std::string m_currentHost; + friend class el::Logger; // To resolve loggerId format specifier easily +}; +} // namespace base +/// @brief Resolving function for format specifier +typedef std::function FormatSpecifierValueResolver; +/// @brief User-provided custom format specifier +/// @see el::Helpers::installCustomFormatSpecifier +/// @see FormatSpecifierValueResolver +class CustomFormatSpecifier { + public: + CustomFormatSpecifier(const char* formatSpecifier, const FormatSpecifierValueResolver& resolver) : + m_formatSpecifier(formatSpecifier), m_resolver(resolver) {} + inline const char* formatSpecifier(void) const { + return m_formatSpecifier; + } + inline const FormatSpecifierValueResolver& resolver(void) const { + return m_resolver; + } + inline bool operator==(const char* formatSpecifier) { + return strcmp(m_formatSpecifier, formatSpecifier) == 0; + } + + private: + const char* m_formatSpecifier; + FormatSpecifierValueResolver m_resolver; +}; +/// @brief Represents single configuration that has representing level, configuration type and a string based value. +/// +/// @detail String based value means any value either its boolean, integer or string itself, it will be embedded inside quotes +/// and will be parsed later. +/// +/// Consider some examples below: +/// * el::Configuration confEnabledInfo(el::Level::Info, el::ConfigurationType::Enabled, "true"); +/// * el::Configuration confMaxLogFileSizeInfo(el::Level::Info, el::ConfigurationType::MaxLogFileSize, "2048"); +/// * el::Configuration confFilenameInfo(el::Level::Info, el::ConfigurationType::Filename, "/var/log/my.log"); +class Configuration : public Loggable { + public: + Configuration(const Configuration& c); + Configuration& operator=(const Configuration& c); + + virtual ~Configuration(void) { + } + + /// @brief Full constructor used to sets value of configuration + Configuration(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Gets level of current configuration + inline Level level(void) const { + return m_level; + } + + /// @brief Gets configuration type of current configuration + inline ConfigurationType configurationType(void) const { + return m_configurationType; + } + + /// @brief Gets string based configuration value + inline const std::string& value(void) const { + return m_value; + } + + /// @brief Set string based configuration value + /// @param value Value to set. Values have to be std::string; For boolean values use "true", "false", for any integral values + /// use them in quotes. They will be parsed when configuring + inline void setValue(const std::string& value) { + m_value = value; + } + + virtual void log(el::base::type::ostream_t& os) const; + + /// @brief Used to find configuration from configuration (pointers) repository. Avoid using it. + class Predicate { + public: + Predicate(Level level, ConfigurationType configurationType); + + bool operator()(const Configuration* conf) const; + + private: + Level m_level; + ConfigurationType m_configurationType; + }; + + private: + Level m_level; + ConfigurationType m_configurationType; + std::string m_value; +}; + +/// @brief Thread-safe Configuration repository +/// +/// @detail This repository represents configurations for all the levels and configuration type mapped to a value. +class Configurations : public base::utils::RegistryWithPred { + public: + /// @brief Default constructor with empty repository + Configurations(void); + + /// @brief Constructor used to set configurations using configuration file. + /// @param configurationFile Full path to configuration file + /// @param useDefaultsForRemaining Lets you set the remaining configurations to default. + /// @param base If provided, this configuration will be based off existing repository that this argument is pointing to. + /// @see parseFromFile(const std::string&, Configurations* base) + /// @see setRemainingToDefault() + Configurations(const std::string& configurationFile, bool useDefaultsForRemaining = true, + Configurations* base = nullptr); + + virtual ~Configurations(void) { + } + + /// @brief Parses configuration from file. + /// @param configurationFile Full path to configuration file + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration file. + /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you + /// do not proceed without successful parse. + bool parseFromFile(const std::string& configurationFile, Configurations* base = nullptr); + + /// @brief Parse configurations from configuration string. + /// + /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary + /// new line characters are provided. + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration text. + /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you + /// do not proceed without successful parse. + bool parseFromText(const std::string& configurationsString, Configurations* base = nullptr); + + /// @brief Sets configuration based-off an existing configurations. + /// @param base Pointer to existing configurations. + void setFromBase(Configurations* base); + + /// @brief Determines whether or not specified configuration type exists in the repository. + /// + /// @detail Returns as soon as first level is found. + /// @param configurationType Type of configuration to check existence for. + bool hasConfiguration(ConfigurationType configurationType); + + /// @brief Determines whether or not specified configuration type exists for specified level + /// @param level Level to check + /// @param configurationType Type of configuration to check existence for. + bool hasConfiguration(Level level, ConfigurationType configurationType); + + /// @brief Sets value of configuration for specified level. + /// + /// @detail Any existing configuration for specified level will be replaced. Also note that configuration types + /// ConfigurationType::SubsecondPrecision and ConfigurationType::PerformanceTracking will be ignored if not set for + /// Level::Global because these configurations are not dependant on level. + /// @param level Level to set configuration for (el::Level). + /// @param configurationType Type of configuration (el::ConfigurationType) + /// @param value A string based value. Regardless of what the data type of configuration is, it will always be string + /// from users' point of view. This is then parsed later to be used internally. + /// @see Configuration::setValue(const std::string& value) + /// @see el::Level + /// @see el::ConfigurationType + void set(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Sets single configuration based on other single configuration. + /// @see set(Level level, ConfigurationType configurationType, const std::string& value) + void set(Configuration* conf); + + inline Configuration* get(Level level, ConfigurationType configurationType) { + base::threading::ScopedLock scopedLock(lock()); + return RegistryWithPred::get(level, configurationType); + } + + /// @brief Sets configuration for all levels. + /// @param configurationType Type of configuration + /// @param value String based value + /// @see Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) + inline void setGlobally(ConfigurationType configurationType, const std::string& value) { + setGlobally(configurationType, value, false); + } + + /// @brief Clears repository so that all the configurations are unset + inline void clear(void) { + base::threading::ScopedLock scopedLock(lock()); + unregisterAll(); + } + + /// @brief Gets configuration file used in parsing this configurations. + /// + /// @detail If this repository was set manually or by text this returns empty string. + inline const std::string& configurationFile(void) const { + return m_configurationFile; + } + + /// @brief Sets configurations to "factory based" configurations. + void setToDefault(void); + + /// @brief Lets you set the remaining configurations to default. + /// + /// @detail By remaining, it means that the level/type a configuration does not exist for. + /// This function is useful when you want to minimize chances of failures, e.g, if you have a configuration file that sets + /// configuration for all the configurations except for Enabled or not, we use this so that ENABLED is set to default i.e, + /// true. If you dont do this explicitly (either by calling this function or by using second param in Constructor + /// and try to access a value, an error is thrown + void setRemainingToDefault(void); + + /// @brief Parser used internally to parse configurations from file or text. + /// + /// @detail This class makes use of base::utils::Str. + /// You should not need this unless you are working on some tool for Easylogging++ + class Parser : base::StaticClass { + public: + /// @brief Parses configuration from file. + /// @param configurationFile Full path to configuration file + /// @param sender Sender configurations pointer. Usually 'this' is used from calling class + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration file. + /// @return True if successfully parsed, false otherwise. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you + /// do not proceed without successful parse. + static bool parseFromFile(const std::string& configurationFile, Configurations* sender, + Configurations* base = nullptr); + + /// @brief Parse configurations from configuration string. + /// + /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary + /// new line characters are provided. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you + /// do not proceed without successful parse (This is recommended) + /// @param configurationsString the configuration in plain text format + /// @param sender Sender configurations pointer. Usually 'this' is used from calling class + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration text. + /// @return True if successfully parsed, false otherwise. + static bool parseFromText(const std::string& configurationsString, Configurations* sender, + Configurations* base = nullptr); + + private: + friend class el::Loggers; + static void ignoreComments(std::string* line); + static bool isLevel(const std::string& line); + static bool isComment(const std::string& line); + static inline bool isConfig(const std::string& line); + static bool parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr, Level* currLevel, + Configurations* conf); + }; + + private: + std::string m_configurationFile; + bool m_isFromFile; + friend class el::Loggers; + + /// @brief Unsafely sets configuration if does not already exist + void unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Thread unsafe set + void unsafeSet(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Sets configurations for all levels including Level::Global if includeGlobalLevel is true + /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) + void setGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel); + + /// @brief Sets configurations (Unsafely) for all levels including Level::Global if includeGlobalLevel is true + /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) + void unsafeSetGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel); +}; + +namespace base { +typedef std::shared_ptr FileStreamPtr; +typedef std::unordered_map LogStreamsReferenceMap; +/// @brief Configurations with data types. +/// +/// @detail el::Configurations have string based values. This is whats used internally in order to read correct configurations. +/// This is to perform faster while writing logs using correct configurations. +/// +/// This is thread safe and final class containing non-virtual destructor (means nothing should inherit this class) +class TypedConfigurations : public base::threading::ThreadSafe { + public: + /// @brief Constructor to initialize (construct) the object off el::Configurations + /// @param configurations Configurations pointer/reference to base this typed configurations off. + /// @param logStreamsReference Use ELPP->registeredLoggers()->logStreamsReference() + TypedConfigurations(Configurations* configurations, base::LogStreamsReferenceMap* logStreamsReference); + + TypedConfigurations(const TypedConfigurations& other); + + virtual ~TypedConfigurations(void) { + } + + const Configurations* configurations(void) const { + return m_configurations; + } + + bool enabled(Level level); + bool toFile(Level level); + const std::string& filename(Level level); + bool toStandardOutput(Level level); + const base::LogFormat& logFormat(Level level); + const base::SubsecondPrecision& subsecondPrecision(Level level = Level::Global); + const base::MillisecondsWidth& millisecondsWidth(Level level = Level::Global); + bool performanceTracking(Level level = Level::Global); + base::type::fstream_t* fileStream(Level level); + std::size_t maxLogFileSize(Level level); + std::size_t logFlushThreshold(Level level); + + private: + Configurations* m_configurations; + std::unordered_map m_enabledMap; + std::unordered_map m_toFileMap; + std::unordered_map m_filenameMap; + std::unordered_map m_toStandardOutputMap; + std::unordered_map m_logFormatMap; + std::unordered_map m_subsecondPrecisionMap; + std::unordered_map m_performanceTrackingMap; + std::unordered_map m_fileStreamMap; + std::unordered_map m_maxLogFileSizeMap; + std::unordered_map m_logFlushThresholdMap; + base::LogStreamsReferenceMap* m_logStreamsReference; + + friend class el::Helpers; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::DefaultLogDispatchCallback; + friend class el::base::LogDispatcher; + + template + inline Conf_T getConfigByVal(Level level, const std::unordered_map* confMap, const char* confName) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeGetConfigByVal(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope + } + + template + inline Conf_T& getConfigByRef(Level level, std::unordered_map* confMap, const char* confName) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeGetConfigByRef(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope + } + + template + Conf_T unsafeGetConfigByVal(Level level, const std::unordered_map* confMap, const char* confName) { + ELPP_UNUSED(confName); + typename std::unordered_map::const_iterator it = confMap->find(level); + if (it == confMap->end()) { + try { + return confMap->at(Level::Global); + } catch (...) { + ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" + << LevelHelper::convertToString(level) << "]" + << std::endl << "Please ensure you have properly configured logger.", false); + return Conf_T(); + } + } + return it->second; + } + + template + Conf_T& unsafeGetConfigByRef(Level level, std::unordered_map* confMap, const char* confName) { + ELPP_UNUSED(confName); + typename std::unordered_map::iterator it = confMap->find(level); + if (it == confMap->end()) { + try { + return confMap->at(Level::Global); + } catch (...) { + ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" + << LevelHelper::convertToString(level) << "]" + << std::endl << "Please ensure you have properly configured logger.", false); + } + } + return it->second; + } + + template + void setValue(Level level, const Conf_T& value, std::unordered_map* confMap, + bool includeGlobalLevel = true) { + // If map is empty and we are allowed to add into generic level (Level::Global), do it! + if (confMap->empty() && includeGlobalLevel) { + confMap->insert(std::make_pair(Level::Global, value)); + return; + } + // If same value exist in generic level already, dont add it to explicit level + typename std::unordered_map::iterator it = confMap->find(Level::Global); + if (it != confMap->end() && it->second == value) { + return; + } + // Now make sure we dont double up values if we really need to add it to explicit level + it = confMap->find(level); + if (it == confMap->end()) { + // Value not found for level, add new + confMap->insert(std::make_pair(level, value)); + } else { + // Value found, just update value + confMap->at(level) = value; + } + } + + void build(Configurations* configurations); + unsigned long getULong(std::string confVal); + std::string resolveFilename(const std::string& filename); + void insertFile(Level level, const std::string& fullFilename); + bool unsafeValidateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback); + + inline bool validateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeValidateFileRolling(level, preRollOutCallback); + } +}; +/// @brief Class that keeps record of current line hit for occasional logging +class HitCounter { + public: + HitCounter(void) : + m_filename(""), + m_lineNumber(0), + m_hitCounts(0) { + } + + HitCounter(const char* filename, base::type::LineNumber lineNumber) : + m_filename(filename), + m_lineNumber(lineNumber), + m_hitCounts(0) { + } + + HitCounter(const HitCounter& hitCounter) : + m_filename(hitCounter.m_filename), + m_lineNumber(hitCounter.m_lineNumber), + m_hitCounts(hitCounter.m_hitCounts) { + } + + HitCounter& operator=(const HitCounter& hitCounter) { + if (&hitCounter != this) { + m_filename = hitCounter.m_filename; + m_lineNumber = hitCounter.m_lineNumber; + m_hitCounts = hitCounter.m_hitCounts; + } + return *this; + } + + virtual ~HitCounter(void) { + } + + /// @brief Resets location of current hit counter + inline void resetLocation(const char* filename, base::type::LineNumber lineNumber) { + m_filename = filename; + m_lineNumber = lineNumber; + } + + /// @brief Validates hit counts and resets it if necessary + inline void validateHitCounts(std::size_t n) { + if (m_hitCounts >= base::consts::kMaxLogPerCounter) { + m_hitCounts = (n >= 1 ? base::consts::kMaxLogPerCounter % n : 0); + } + ++m_hitCounts; + } + + inline const char* filename(void) const { + return m_filename; + } + + inline base::type::LineNumber lineNumber(void) const { + return m_lineNumber; + } + + inline std::size_t hitCounts(void) const { + return m_hitCounts; + } + + inline void increment(void) { + ++m_hitCounts; + } + + class Predicate { + public: + Predicate(const char* filename, base::type::LineNumber lineNumber) + : m_filename(filename), + m_lineNumber(lineNumber) { + } + inline bool operator()(const HitCounter* counter) { + return ((counter != nullptr) && + (strcmp(counter->m_filename, m_filename) == 0) && + (counter->m_lineNumber == m_lineNumber)); + } + + private: + const char* m_filename; + base::type::LineNumber m_lineNumber; + }; + + private: + const char* m_filename; + base::type::LineNumber m_lineNumber; + std::size_t m_hitCounts; +}; +/// @brief Repository for hit counters used across the application +class RegisteredHitCounters : public base::utils::RegistryWithPred { + public: + /// @brief Validates counter for every N, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateEveryN(const char* filename, base::type::LineNumber lineNumber, std::size_t n); + + /// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateAfterN(const char* filename, base::type::LineNumber lineNumber, std::size_t n); + + /// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateNTimes(const char* filename, base::type::LineNumber lineNumber, std::size_t n); + + /// @brief Gets hit counter registered at specified position + inline const base::HitCounter* getCounter(const char* filename, base::type::LineNumber lineNumber) { + base::threading::ScopedLock scopedLock(lock()); + return get(filename, lineNumber); + } +}; +/// @brief Action to be taken for dispatching +enum class DispatchAction : base::type::EnumType { + None = 1, NormalLog = 2, SysLog = 4 +}; +} // namespace base +template +class Callback : protected base::threading::ThreadSafe { + public: + Callback(void) : m_enabled(true) {} + inline bool enabled(void) const { + return m_enabled; + } + inline void setEnabled(bool enabled) { + base::threading::ScopedLock scopedLock(lock()); + m_enabled = enabled; + } + protected: + virtual void handle(const T* handlePtr) = 0; + private: + bool m_enabled; +}; +class LogDispatchData { + public: + LogDispatchData() : m_logMessage(nullptr), m_dispatchAction(base::DispatchAction::None) {} + inline const LogMessage* logMessage(void) const { + return m_logMessage; + } + inline base::DispatchAction dispatchAction(void) const { + return m_dispatchAction; + } + inline void setLogMessage(LogMessage* logMessage) { + m_logMessage = logMessage; + } + inline void setDispatchAction(base::DispatchAction dispatchAction) { + m_dispatchAction = dispatchAction; + } + private: + LogMessage* m_logMessage; + base::DispatchAction m_dispatchAction; + friend class base::LogDispatcher; + +}; +class LogDispatchCallback : public Callback { + protected: + virtual void handle(const LogDispatchData* data); + base::threading::Mutex& fileHandle(const LogDispatchData* data); + private: + friend class base::LogDispatcher; + std::unordered_map> m_fileLocks; + base::threading::Mutex m_fileLocksMapLock; +}; +class PerformanceTrackingCallback : public Callback { + private: + friend class base::PerformanceTracker; +}; +class LoggerRegistrationCallback : public Callback { + private: + friend class base::RegisteredLoggers; +}; +class LogBuilder : base::NoCopy { + public: + LogBuilder() : m_termSupportsColor(base::utils::OS::termSupportsColor()) {} + virtual ~LogBuilder(void) { + ELPP_INTERNAL_INFO(3, "Destroying log builder...") + } + virtual base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const = 0; + void convertToColoredOutput(base::type::string_t* logLine, Level level); + private: + bool m_termSupportsColor; + friend class el::base::DefaultLogDispatchCallback; +}; +typedef std::shared_ptr LogBuilderPtr; +/// @brief Represents a logger holding ID and configurations we need to write logs +/// +/// @detail This class does not write logs itself instead its used by writer to read configuations from. +class Logger : public base::threading::ThreadSafe, public Loggable { + public: + Logger(const std::string& id, base::LogStreamsReferenceMap* logStreamsReference); + Logger(const std::string& id, const Configurations& configurations, base::LogStreamsReferenceMap* logStreamsReference); + Logger(const Logger& logger); + Logger& operator=(const Logger& logger); + + virtual ~Logger(void) { + base::utils::safeDelete(m_typedConfigurations); + } + + virtual inline void log(el::base::type::ostream_t& os) const { + os << m_id.c_str(); + } + + /// @brief Configures the logger using specified configurations. + void configure(const Configurations& configurations); + + /// @brief Reconfigures logger using existing configurations + void reconfigure(void); + + inline const std::string& id(void) const { + return m_id; + } + + inline const std::string& parentApplicationName(void) const { + return m_parentApplicationName; + } + + inline void setParentApplicationName(const std::string& parentApplicationName) { + m_parentApplicationName = parentApplicationName; + } + + inline Configurations* configurations(void) { + return &m_configurations; + } + + inline base::TypedConfigurations* typedConfigurations(void) { + return m_typedConfigurations; + } + + static bool isValidId(const std::string& id); + + /// @brief Flushes logger to sync all log files for all levels + void flush(void); + + void flush(Level level, base::type::fstream_t* fs); + + inline bool isFlushNeeded(Level level) { + return ++m_unflushedCount.find(level)->second >= m_typedConfigurations->logFlushThreshold(level); + } + + inline LogBuilder* logBuilder(void) const { + return m_logBuilder.get(); + } + + inline void setLogBuilder(const LogBuilderPtr& logBuilder) { + m_logBuilder = logBuilder; + } + + inline bool enabled(Level level) const { + return m_typedConfigurations->enabled(level); + } + +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED +# define LOGGER_LEVEL_WRITERS_SIGNATURES(FUNCTION_NAME)\ +template \ +inline void FUNCTION_NAME(const char*, const T&, const Args&...);\ +template \ +inline void FUNCTION_NAME(const T&); + + template + inline void verbose(int, const char*, const T&, const Args&...); + + template + inline void verbose(int, const T&); + + LOGGER_LEVEL_WRITERS_SIGNATURES(info) + LOGGER_LEVEL_WRITERS_SIGNATURES(debug) + LOGGER_LEVEL_WRITERS_SIGNATURES(warn) + LOGGER_LEVEL_WRITERS_SIGNATURES(error) + LOGGER_LEVEL_WRITERS_SIGNATURES(fatal) + LOGGER_LEVEL_WRITERS_SIGNATURES(trace) +# undef LOGGER_LEVEL_WRITERS_SIGNATURES +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED + private: + std::string m_id; + base::TypedConfigurations* m_typedConfigurations; + base::type::stringstream_t m_stream; + std::string m_parentApplicationName; + bool m_isConfigured; + Configurations m_configurations; + std::unordered_map m_unflushedCount; + base::LogStreamsReferenceMap* m_logStreamsReference; + LogBuilderPtr m_logBuilder; + + friend class el::LogMessage; + friend class el::Loggers; + friend class el::Helpers; + friend class el::base::RegisteredLoggers; + friend class el::base::DefaultLogDispatchCallback; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::PErrorWriter; + friend class el::base::Storage; + friend class el::base::PerformanceTracker; + friend class el::base::LogDispatcher; + + Logger(void); + +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED + template + void log_(Level, int, const char*, const T&, const Args&...); + + template + inline void log_(Level, int, const T&); + + template + void log(Level, const char*, const T&, const Args&...); + + template + inline void log(Level, const T&); +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED + + void initUnflushedCount(void); + + inline base::type::stringstream_t& stream(void) { + return m_stream; + } + + void resolveLoggerFormatSpec(void) const; +}; +namespace base { +/// @brief Loggers repository +class RegisteredLoggers : public base::utils::Registry { + public: + explicit RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder); + + virtual ~RegisteredLoggers(void) { + unsafeFlushAll(); + } + + inline void setDefaultConfigurations(const Configurations& configurations) { + base::threading::ScopedLock scopedLock(lock()); + m_defaultConfigurations.setFromBase(const_cast(&configurations)); + } + + inline Configurations* defaultConfigurations(void) { + return &m_defaultConfigurations; + } + + Logger* get(const std::string& id, bool forceCreation = true); + + template + inline bool installLoggerRegistrationCallback(const std::string& id) { + return base::utils::Utils::installCallback(id, + &m_loggerRegistrationCallbacks); + } + + template + inline void uninstallLoggerRegistrationCallback(const std::string& id) { + base::utils::Utils::uninstallCallback(id, &m_loggerRegistrationCallbacks); + } + + template + inline T* loggerRegistrationCallback(const std::string& id) { + return base::utils::Utils::callback(id, &m_loggerRegistrationCallbacks); + } + + bool remove(const std::string& id); + + inline bool has(const std::string& id) { + return get(id, false) != nullptr; + } + + inline void unregister(Logger*& logger) { + base::threading::ScopedLock scopedLock(lock()); + base::utils::Registry::unregister(logger->id()); + } + + inline base::LogStreamsReferenceMap* logStreamsReference(void) { + return &m_logStreamsReference; + } + + inline void flushAll(void) { + base::threading::ScopedLock scopedLock(lock()); + unsafeFlushAll(); + } + + inline void setDefaultLogBuilder(LogBuilderPtr& logBuilderPtr) { + base::threading::ScopedLock scopedLock(lock()); + m_defaultLogBuilder = logBuilderPtr; + } + + private: + LogBuilderPtr m_defaultLogBuilder; + Configurations m_defaultConfigurations; + base::LogStreamsReferenceMap m_logStreamsReference; + std::unordered_map m_loggerRegistrationCallbacks; + friend class el::base::Storage; + + void unsafeFlushAll(void); +}; +/// @brief Represents registries for verbose logging +class VRegistry : base::NoCopy, public base::threading::ThreadSafe { + public: + explicit VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags); + + /// @brief Sets verbose level. Accepted range is 0-9 + void setLevel(base::type::VerboseLevel level); + + inline base::type::VerboseLevel level(void) const { + return m_level; + } + + inline void clearModules(void) { + base::threading::ScopedLock scopedLock(lock()); + m_modules.clear(); + } + + void setModules(const char* modules); + + bool allowed(base::type::VerboseLevel vlevel, const char* file); + + inline const std::unordered_map& modules(void) const { + return m_modules; + } + + void setFromArgs(const base::utils::CommandLineArgs* commandLineArgs); + + /// @brief Whether or not vModules enabled + inline bool vModulesEnabled(void) { + return !base::utils::hasFlag(LoggingFlag::DisableVModules, *m_pFlags); + } + + private: + base::type::VerboseLevel m_level; + base::type::EnumType* m_pFlags; + std::unordered_map m_modules; +}; +} // namespace base +class LogMessage { + public: + LogMessage(Level level, const std::string& file, base::type::LineNumber line, const std::string& func, + base::type::VerboseLevel verboseLevel, Logger* logger) : + m_level(level), m_file(file), m_line(line), m_func(func), + m_verboseLevel(verboseLevel), m_logger(logger), m_message(logger->stream().str()) { + } + inline Level level(void) const { + return m_level; + } + inline const std::string& file(void) const { + return m_file; + } + inline base::type::LineNumber line(void) const { + return m_line; + } + inline const std::string& func(void) const { + return m_func; + } + inline base::type::VerboseLevel verboseLevel(void) const { + return m_verboseLevel; + } + inline Logger* logger(void) const { + return m_logger; + } + inline const base::type::string_t& message(void) const { + return m_message; + } + private: + Level m_level; + std::string m_file; + base::type::LineNumber m_line; + std::string m_func; + base::type::VerboseLevel m_verboseLevel; + Logger* m_logger; + base::type::string_t m_message; +}; +namespace base { +#if ELPP_ASYNC_LOGGING +class AsyncLogItem { + public: + explicit AsyncLogItem(const LogMessage& logMessage, const LogDispatchData& data, const base::type::string_t& logLine) + : m_logMessage(logMessage), m_dispatchData(data), m_logLine(logLine) {} + virtual ~AsyncLogItem() {} + inline LogMessage* logMessage(void) { + return &m_logMessage; + } + inline LogDispatchData* data(void) { + return &m_dispatchData; + } + inline base::type::string_t logLine(void) { + return m_logLine; + } + private: + LogMessage m_logMessage; + LogDispatchData m_dispatchData; + base::type::string_t m_logLine; +}; +class AsyncLogQueue : public base::threading::ThreadSafe { + public: + virtual ~AsyncLogQueue() { + ELPP_INTERNAL_INFO(6, "~AsyncLogQueue"); + } + + inline AsyncLogItem next(void) { + base::threading::ScopedLock scopedLock(lock()); + AsyncLogItem result = m_queue.front(); + m_queue.pop(); + return result; + } + + inline void push(const AsyncLogItem& item) { + base::threading::ScopedLock scopedLock(lock()); + m_queue.push(item); + } + inline void pop(void) { + base::threading::ScopedLock scopedLock(lock()); + m_queue.pop(); + } + inline AsyncLogItem front(void) { + base::threading::ScopedLock scopedLock(lock()); + return m_queue.front(); + } + inline bool empty(void) { + base::threading::ScopedLock scopedLock(lock()); + return m_queue.empty(); + } + private: + std::queue m_queue; +}; +class IWorker { + public: + virtual ~IWorker() {} + virtual void start() = 0; +}; +#endif // ELPP_ASYNC_LOGGING +/// @brief Easylogging++ management storage +class Storage : base::NoCopy, public base::threading::ThreadSafe { + public: +#if ELPP_ASYNC_LOGGING + Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker); +#else + explicit Storage(const LogBuilderPtr& defaultLogBuilder); +#endif // ELPP_ASYNC_LOGGING + + virtual ~Storage(void); + + inline bool validateEveryNCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t occasion) { + return hitCounters()->validateEveryN(filename, lineNumber, occasion); + } + + inline bool validateAfterNCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + return hitCounters()->validateAfterN(filename, lineNumber, n); + } + + inline bool validateNTimesCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + return hitCounters()->validateNTimes(filename, lineNumber, n); + } + + inline base::RegisteredHitCounters* hitCounters(void) const { + return m_registeredHitCounters; + } + + inline base::RegisteredLoggers* registeredLoggers(void) const { + return m_registeredLoggers; + } + + inline base::VRegistry* vRegistry(void) const { + return m_vRegistry; + } + +#if ELPP_ASYNC_LOGGING + inline base::AsyncLogQueue* asyncLogQueue(void) const { + return m_asyncLogQueue; + } +#endif // ELPP_ASYNC_LOGGING + + inline const base::utils::CommandLineArgs* commandLineArgs(void) const { + return &m_commandLineArgs; + } + + inline void addFlag(LoggingFlag flag) { + base::utils::addFlag(flag, &m_flags); + } + + inline void removeFlag(LoggingFlag flag) { + base::utils::removeFlag(flag, &m_flags); + } + + inline bool hasFlag(LoggingFlag flag) const { + return base::utils::hasFlag(flag, m_flags); + } + + inline base::type::EnumType flags(void) const { + return m_flags; + } + + inline void setFlags(base::type::EnumType flags) { + m_flags = flags; + } + + inline void setPreRollOutCallback(const PreRollOutCallback& callback) { + m_preRollOutCallback = callback; + } + + inline void unsetPreRollOutCallback(void) { + m_preRollOutCallback = base::defaultPreRollOutCallback; + } + + inline PreRollOutCallback& preRollOutCallback(void) { + return m_preRollOutCallback; + } + + bool hasCustomFormatSpecifier(const char* formatSpecifier); + void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier); + bool uninstallCustomFormatSpecifier(const char* formatSpecifier); + + const std::vector* customFormatSpecifiers(void) const { + return &m_customFormatSpecifiers; + } + + base::threading::Mutex& customFormatSpecifiersLock() { + return m_customFormatSpecifiersLock; + } + + inline void setLoggingLevel(Level level) { + m_loggingLevel = level; + } + + template + inline bool installLogDispatchCallback(const std::string& id) { + return base::utils::Utils::installCallback(id, &m_logDispatchCallbacks); + } + + template + inline void uninstallLogDispatchCallback(const std::string& id) { + base::utils::Utils::uninstallCallback(id, &m_logDispatchCallbacks); + } + template + inline T* logDispatchCallback(const std::string& id) { + return base::utils::Utils::callback(id, &m_logDispatchCallbacks); + } + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + template + inline bool installPerformanceTrackingCallback(const std::string& id) { + return base::utils::Utils::installCallback(id, + &m_performanceTrackingCallbacks); + } + + template + inline void uninstallPerformanceTrackingCallback(const std::string& id) { + base::utils::Utils::uninstallCallback(id, + &m_performanceTrackingCallbacks); + } + + template + inline T* performanceTrackingCallback(const std::string& id) { + return base::utils::Utils::callback(id, &m_performanceTrackingCallbacks); + } +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + + /// @brief Sets thread name for current thread. Requires std::thread + inline void setThreadName(const std::string& name) { + if (name.empty()) return; + base::threading::ScopedLock scopedLock(m_threadNamesLock); + m_threadNames[base::threading::getCurrentThreadId()] = name; + } + + inline std::string getThreadName(const std::string& threadId) { + base::threading::ScopedLock scopedLock(m_threadNamesLock); + std::unordered_map::const_iterator it = m_threadNames.find(threadId); + if (it == m_threadNames.end()) { + return threadId; + } + return it->second; + } + private: + base::RegisteredHitCounters* m_registeredHitCounters; + base::RegisteredLoggers* m_registeredLoggers; + base::type::EnumType m_flags; + base::VRegistry* m_vRegistry; +#if ELPP_ASYNC_LOGGING + base::AsyncLogQueue* m_asyncLogQueue; + base::IWorker* m_asyncDispatchWorker; +#endif // ELPP_ASYNC_LOGGING + base::utils::CommandLineArgs m_commandLineArgs; + PreRollOutCallback m_preRollOutCallback; + std::unordered_map m_logDispatchCallbacks; + std::unordered_map m_performanceTrackingCallbacks; + std::unordered_map m_threadNames; + std::vector m_customFormatSpecifiers; + base::threading::Mutex m_customFormatSpecifiersLock; + base::threading::Mutex m_threadNamesLock; + Level m_loggingLevel; + + friend class el::Helpers; + friend class el::base::DefaultLogDispatchCallback; + friend class el::LogBuilder; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::PerformanceTracker; + friend class el::base::LogDispatcher; + + void setApplicationArguments(int argc, char** argv); + + inline void setApplicationArguments(int argc, const char** argv) { + setApplicationArguments(argc, const_cast(argv)); + } +}; +extern ELPP_EXPORT base::type::StoragePointer elStorage; +#define ELPP el::base::elStorage +class DefaultLogDispatchCallback : public LogDispatchCallback { + protected: + void handle(const LogDispatchData* data); + private: + const LogDispatchData* m_data; + void dispatch(base::type::string_t&& logLine); +}; +#if ELPP_ASYNC_LOGGING +class AsyncLogDispatchCallback : public LogDispatchCallback { + protected: + void handle(const LogDispatchData* data); +}; +class AsyncDispatchWorker : public base::IWorker, public base::threading::ThreadSafe { + public: + AsyncDispatchWorker(); + virtual ~AsyncDispatchWorker(); + + bool clean(void); + void emptyQueue(void); + virtual void start(void); + void handle(AsyncLogItem* logItem); + void run(void); + + void setContinueRunning(bool value) { + base::threading::ScopedLock scopedLock(m_continueRunningLock); + m_continueRunning = value; + } + + bool continueRunning(void) const { + return m_continueRunning; + } + private: + std::condition_variable cv; + bool m_continueRunning; + base::threading::Mutex m_continueRunningLock; +}; +#endif // ELPP_ASYNC_LOGGING +} // namespace base +namespace base { +class DefaultLogBuilder : public LogBuilder { + public: + base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const; +}; +/// @brief Dispatches log messages +class LogDispatcher : base::NoCopy { + public: + LogDispatcher(bool proceed, LogMessage* logMessage, base::DispatchAction dispatchAction) : + m_proceed(proceed), + m_logMessage(logMessage), + m_dispatchAction(std::move(dispatchAction)) { + } + + void dispatch(void); + + private: + bool m_proceed; + LogMessage* m_logMessage; + base::DispatchAction m_dispatchAction; +}; +#if defined(ELPP_STL_LOGGING) +/// @brief Workarounds to write some STL logs +/// +/// @detail There is workaround needed to loop through some stl containers. In order to do that, we need iterable containers +/// of same type and provide iterator interface and pass it on to writeIterator(). +/// Remember, this is passed by value in constructor so that we dont change original containers. +/// This operation is as expensive as Big-O(std::min(class_.size(), base::consts::kMaxLogPerContainer)) +namespace workarounds { +/// @brief Abstract IterableContainer template that provides interface for iterable classes of type T +template +class IterableContainer { + public: + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + IterableContainer(void) {} + virtual ~IterableContainer(void) {} + iterator begin(void) { + return getContainer().begin(); + } + iterator end(void) { + return getContainer().end(); + } + private: + virtual Container& getContainer(void) = 0; +}; +/// @brief Implements IterableContainer and provides iterable std::priority_queue class +template, typename Comparator = std::less> +class IterablePriorityQueue : public IterableContainer, + public std::priority_queue { + public: + IterablePriorityQueue(std::priority_queue queue_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { + this->push(queue_.top()); + queue_.pop(); + } + } + private: + inline Container& getContainer(void) { + return this->c; + } +}; +/// @brief Implements IterableContainer and provides iterable std::queue class +template> +class IterableQueue : public IterableContainer, public std::queue { + public: + IterableQueue(std::queue queue_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { + this->push(queue_.front()); + queue_.pop(); + } + } + private: + inline Container& getContainer(void) { + return this->c; + } +}; +/// @brief Implements IterableContainer and provides iterable std::stack class +template> +class IterableStack : public IterableContainer, public std::stack { + public: + IterableStack(std::stack stack_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !stack_.empty()) { + this->push(stack_.top()); + stack_.pop(); + } + } + private: + inline Container& getContainer(void) { + return this->c; + } +}; +} // namespace workarounds +#endif // defined(ELPP_STL_LOGGING) +// Log message builder +class MessageBuilder { + public: + MessageBuilder(void) : m_logger(nullptr), m_containerLogSeperator(ELPP_LITERAL("")) {} + void initialize(Logger* logger); + +# define ELPP_SIMPLE_LOG(LOG_TYPE)\ +MessageBuilder& operator<<(LOG_TYPE msg) {\ +m_logger->stream() << msg;\ +if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) {\ +m_logger->stream() << " ";\ +}\ +return *this;\ +} + + inline MessageBuilder& operator<<(const std::string& msg) { + return operator<<(msg.c_str()); + } + ELPP_SIMPLE_LOG(char) + ELPP_SIMPLE_LOG(bool) + ELPP_SIMPLE_LOG(signed short) + ELPP_SIMPLE_LOG(unsigned short) + ELPP_SIMPLE_LOG(signed int) + ELPP_SIMPLE_LOG(unsigned int) + ELPP_SIMPLE_LOG(signed long) + ELPP_SIMPLE_LOG(unsigned long) + ELPP_SIMPLE_LOG(float) + ELPP_SIMPLE_LOG(double) + ELPP_SIMPLE_LOG(char*) + ELPP_SIMPLE_LOG(const char*) + ELPP_SIMPLE_LOG(const void*) + ELPP_SIMPLE_LOG(long double) + inline MessageBuilder& operator<<(const std::wstring& msg) { + return operator<<(msg.c_str()); + } + MessageBuilder& operator<<(const wchar_t* msg); + // ostream manipulators + inline MessageBuilder& operator<<(std::ostream& (*OStreamMani)(std::ostream&)) { + m_logger->stream() << OStreamMani; + return *this; + } +#define ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(temp) \ +template \ +inline MessageBuilder& operator<<(const temp& template_inst) { \ +return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ +} +#define ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(temp) \ +template \ +inline MessageBuilder& operator<<(const temp& template_inst) { \ +return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ +} +#define ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(temp) \ +template \ +inline MessageBuilder& operator<<(const temp& template_inst) { \ +return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ +} +#define ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(temp) \ +template \ +inline MessageBuilder& operator<<(const temp& template_inst) { \ +return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ +} +#define ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(temp) \ +template \ +inline MessageBuilder& operator<<(const temp& template_inst) { \ +return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ +} + +#if defined(ELPP_STL_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::list) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::deque) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::set) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::multiset) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::map) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::multimap) + template + inline MessageBuilder& operator<<(const std::queue& queue_) { + base::workarounds::IterableQueue iterableQueue_ = + static_cast >(queue_); + return writeIterator(iterableQueue_.begin(), iterableQueue_.end(), iterableQueue_.size()); + } + template + inline MessageBuilder& operator<<(const std::stack& stack_) { + base::workarounds::IterableStack iterableStack_ = + static_cast >(stack_); + return writeIterator(iterableStack_.begin(), iterableStack_.end(), iterableStack_.size()); + } + template + inline MessageBuilder& operator<<(const std::priority_queue& priorityQueue_) { + base::workarounds::IterablePriorityQueue iterablePriorityQueue_ = + static_cast >(priorityQueue_); + return writeIterator(iterablePriorityQueue_.begin(), iterablePriorityQueue_.end(), iterablePriorityQueue_.size()); + } + template + MessageBuilder& operator<<(const std::pair& pair_) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(pair_.first)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(pair_.second)); + m_logger->stream() << ELPP_LITERAL(")"); + return *this; + } + template + MessageBuilder& operator<<(const std::bitset& bitset_) { + m_logger->stream() << ELPP_LITERAL("["); + operator << (bitset_.to_string()); + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } +# if defined(ELPP_LOG_STD_ARRAY) + template + inline MessageBuilder& operator<<(const std::array& array) { + return writeIterator(array.begin(), array.end(), array.size()); + } +# endif // defined(ELPP_LOG_STD_ARRAY) +# if defined(ELPP_LOG_UNORDERED_MAP) + ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_map) + ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_multimap) +# endif // defined(ELPP_LOG_UNORDERED_MAP) +# if defined(ELPP_LOG_UNORDERED_SET) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_set) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_multiset) +# endif // defined(ELPP_LOG_UNORDERED_SET) +#endif // defined(ELPP_STL_LOGGING) +#if defined(ELPP_QT_LOGGING) + inline MessageBuilder& operator<<(const QString& msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << msg.toStdWString(); +# else + m_logger->stream() << msg.toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(const QByteArray& msg) { + return operator << (QString(msg)); + } + inline MessageBuilder& operator<<(const QStringRef& msg) { + return operator<<(msg.toString()); + } + inline MessageBuilder& operator<<(qint64 msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << QString::number(msg).toStdWString(); +# else + m_logger->stream() << QString::number(msg).toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(quint64 msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << QString::number(msg).toStdWString(); +# else + m_logger->stream() << QString::number(msg).toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(QChar msg) { + m_logger->stream() << msg.toLatin1(); + return *this; + } + inline MessageBuilder& operator<<(const QLatin1String& msg) { + m_logger->stream() << msg.latin1(); + return *this; + } + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QList) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QVector) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QQueue) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QSet) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QLinkedList) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QStack) + template + MessageBuilder& operator<<(const QPair& pair_) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(pair_.first)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(pair_.second)); + m_logger->stream() << ELPP_LITERAL(")"); + return *this; + } + template + MessageBuilder& operator<<(const QMap& map_) { + m_logger->stream() << ELPP_LITERAL("["); + QList keys = map_.keys(); + typename QList::const_iterator begin = keys.begin(); + typename QList::const_iterator end = keys.end(); + int max_ = static_cast(base::consts::kMaxLogPerContainer); // to prevent warning + for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(*begin)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(map_.value(*begin))); + m_logger->stream() << ELPP_LITERAL(")"); + m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeperator : ELPP_LITERAL("")); + } + if (begin != end) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } + template + inline MessageBuilder& operator<<(const QMultiMap& map_) { + operator << (static_cast>(map_)); + return *this; + } + template + MessageBuilder& operator<<(const QHash& hash_) { + m_logger->stream() << ELPP_LITERAL("["); + QList keys = hash_.keys(); + typename QList::const_iterator begin = keys.begin(); + typename QList::const_iterator end = keys.end(); + int max_ = static_cast(base::consts::kMaxLogPerContainer); // prevent type warning + for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(*begin)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(hash_.value(*begin))); + m_logger->stream() << ELPP_LITERAL(")"); + m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeperator : ELPP_LITERAL("")); + } + if (begin != end) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } + template + inline MessageBuilder& operator<<(const QMultiHash& multiHash_) { + operator << (static_cast>(multiHash_)); + return *this; + } +#endif // defined(ELPP_QT_LOGGING) +#if defined(ELPP_BOOST_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::stable_vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::list) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::deque) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::map) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::flat_map) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::set) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::flat_set) +#endif // defined(ELPP_BOOST_LOGGING) + + /// @brief Macro used internally that can be used externally to make containers easylogging++ friendly + /// + /// @detail This macro expands to write an ostream& operator<< for container. This container is expected to + /// have begin() and end() methods that return respective iterators + /// @param ContainerType Type of container e.g, MyList from WX_DECLARE_LIST(int, MyList); in wxwidgets + /// @param SizeMethod Method used to get size of container. + /// @param ElementInstance Instance of element to be fed out. Insance name is "elem". See WXELPP_ENABLED macro + /// for an example usage +#define MAKE_CONTAINERELPP_FRIENDLY(ContainerType, SizeMethod, ElementInstance) \ +el::base::type::ostream_t& operator<<(el::base::type::ostream_t& ss, const ContainerType& container) {\ +const el::base::type::char_t* sep = ELPP->hasFlag(el::LoggingFlag::NewLineForContainer) ? \ +ELPP_LITERAL("\n ") : ELPP_LITERAL(", ");\ +ContainerType::const_iterator elem = container.begin();\ +ContainerType::const_iterator endElem = container.end();\ +std::size_t size_ = container.SizeMethod; \ +ss << ELPP_LITERAL("[");\ +for (std::size_t i = 0; elem != endElem && i < el::base::consts::kMaxLogPerContainer; ++i, ++elem) { \ +ss << ElementInstance;\ +ss << ((i < size_ - 1) ? sep : ELPP_LITERAL(""));\ +}\ +if (elem != endElem) {\ +ss << ELPP_LITERAL("...");\ +}\ +ss << ELPP_LITERAL("]");\ +return ss;\ +} +#if defined(ELPP_WXWIDGETS_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(wxVector) +# define ELPP_WX_PTR_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), *(*elem)) +# define ELPP_WX_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), (*elem)) +# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), \ +ELPP_LITERAL("(") << elem->first << ELPP_LITERAL(", ") << elem->second << ELPP_LITERAL(")") +#else +# define ELPP_WX_PTR_ENABLED(ContainerType) +# define ELPP_WX_ENABLED(ContainerType) +# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) +#endif // defined(ELPP_WXWIDGETS_LOGGING) + // Other classes + template + ELPP_SIMPLE_LOG(const Class&) +#undef ELPP_SIMPLE_LOG +#undef ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG + private: + Logger* m_logger; + const base::type::char_t* m_containerLogSeperator; + + template + MessageBuilder& writeIterator(Iterator begin_, Iterator end_, std::size_t size_) { + m_logger->stream() << ELPP_LITERAL("["); + for (std::size_t i = 0; begin_ != end_ && i < base::consts::kMaxLogPerContainer; ++i, ++begin_) { + operator << (*begin_); + m_logger->stream() << ((i < size_ - 1) ? m_containerLogSeperator : ELPP_LITERAL("")); + } + if (begin_ != end_) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { + m_logger->stream() << " "; + } + return *this; + } +}; +/// @brief Writes nothing - Used when certain log is disabled +class NullWriter : base::NoCopy { + public: + NullWriter(void) {} + + // Null manipulator + inline NullWriter& operator<<(std::ostream& (*)(std::ostream&)) { + return *this; + } + + template + inline NullWriter& operator<<(const T&) { + return *this; + } + + inline operator bool() { + return true; + } +}; +/// @brief Main entry point of each logging +class Writer : base::NoCopy { + public: + Writer(Level level, const char* file, base::type::LineNumber line, + const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, + base::type::VerboseLevel verboseLevel = 0) : + m_msg(nullptr), m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel), + m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) { + } + + Writer(LogMessage* msg, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog) : + m_msg(msg), m_level(msg != nullptr ? msg->level() : Level::Unknown), + m_line(0), m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) { + } + + virtual ~Writer(void) { + processDispatch(); + } + + template + inline Writer& operator<<(const T& log) { +#if ELPP_LOGGING_ENABLED + if (m_proceed) { + m_messageBuilder << log; + } +#endif // ELPP_LOGGING_ENABLED + return *this; + } + + inline Writer& operator<<(std::ostream& (*log)(std::ostream&)) { +#if ELPP_LOGGING_ENABLED + if (m_proceed) { + m_messageBuilder << log; + } +#endif // ELPP_LOGGING_ENABLED + return *this; + } + + inline operator bool() { + return true; + } + + Writer& construct(Logger* logger, bool needLock = true); + Writer& construct(int count, const char* loggerIds, ...); + protected: + LogMessage* m_msg; + Level m_level; + const char* m_file; + const base::type::LineNumber m_line; + const char* m_func; + base::type::VerboseLevel m_verboseLevel; + Logger* m_logger; + bool m_proceed; + base::MessageBuilder m_messageBuilder; + base::DispatchAction m_dispatchAction; + std::vector m_loggerIds; + friend class el::Helpers; + + void initializeLogger(const std::string& loggerId, bool lookup = true, bool needLock = true); + void processDispatch(); + void triggerDispatch(void); +}; +class PErrorWriter : public base::Writer { + public: + PErrorWriter(Level level, const char* file, base::type::LineNumber line, + const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, + base::type::VerboseLevel verboseLevel = 0) : + base::Writer(level, file, line, func, dispatchAction, verboseLevel) { + } + + virtual ~PErrorWriter(void); +}; +} // namespace base +// Logging from Logger class. Why this is here? Because we have Storage and Writer class available +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED +template +void Logger::log_(Level level, int vlevel, const char* s, const T& value, const Args&... args) { + base::MessageBuilder b; + b.initialize(this); + while (*s) { + if (*s == base::consts::kFormatSpecifierChar) { + if (*(s + 1) == base::consts::kFormatSpecifierChar) { + ++s; + } else { + if (*(s + 1) == base::consts::kFormatSpecifierCharValue) { + ++s; + b << value; + log_(level, vlevel, ++s, args...); + return; + } + } + } + b << *s++; + } + ELPP_INTERNAL_ERROR("Too many arguments provided. Unable to handle. Please provide more format specifiers", false); +} +template +void Logger::log_(Level level, int vlevel, const T& log) { + if (level == Level::Verbose) { + if (ELPP->vRegistry()->allowed(vlevel, __FILE__)) { + base::Writer(Level::Verbose, "FILE", 0, "FUNCTION", + base::DispatchAction::NormalLog, vlevel).construct(this, false) << log; + } else { + stream().str(ELPP_LITERAL("")); + releaseLock(); + } + } else { + base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log; + } +} +template +inline void Logger::log(Level level, const char* s, const T& value, const Args&... args) { + acquireLock(); // released in Writer! + log_(level, 0, s, value, args...); +} +template +inline void Logger::log(Level level, const T& log) { + acquireLock(); // released in Writer! + log_(level, 0, log); +} +# if ELPP_VERBOSE_LOG +template +inline void Logger::verbose(int vlevel, const char* s, const T& value, const Args&... args) { + acquireLock(); // released in Writer! + log_(el::Level::Verbose, vlevel, s, value, args...); +} +template +inline void Logger::verbose(int vlevel, const T& log) { + acquireLock(); // released in Writer! + log_(el::Level::Verbose, vlevel, log); +} +# else +template +inline void Logger::verbose(int, const char*, const T&, const Args&...) { + return; +} +template +inline void Logger::verbose(int, const T&) { + return; +} +# endif // ELPP_VERBOSE_LOG +# define LOGGER_LEVEL_WRITERS(FUNCTION_NAME, LOG_LEVEL)\ +template \ +inline void Logger::FUNCTION_NAME(const char* s, const T& value, const Args&... args) {\ +log(LOG_LEVEL, s, value, args...);\ +}\ +template \ +inline void Logger::FUNCTION_NAME(const T& value) {\ +log(LOG_LEVEL, value);\ +} +# define LOGGER_LEVEL_WRITERS_DISABLED(FUNCTION_NAME, LOG_LEVEL)\ +template \ +inline void Logger::FUNCTION_NAME(const char*, const T&, const Args&...) {\ +return;\ +}\ +template \ +inline void Logger::FUNCTION_NAME(const T&) {\ +return;\ +} + +# if ELPP_INFO_LOG +LOGGER_LEVEL_WRITERS(info, Level::Info) +# else +LOGGER_LEVEL_WRITERS_DISABLED(info, Level::Info) +# endif // ELPP_INFO_LOG +# if ELPP_DEBUG_LOG +LOGGER_LEVEL_WRITERS(debug, Level::Debug) +# else +LOGGER_LEVEL_WRITERS_DISABLED(debug, Level::Debug) +# endif // ELPP_DEBUG_LOG +# if ELPP_WARNING_LOG +LOGGER_LEVEL_WRITERS(warn, Level::Warning) +# else +LOGGER_LEVEL_WRITERS_DISABLED(warn, Level::Warning) +# endif // ELPP_WARNING_LOG +# if ELPP_ERROR_LOG +LOGGER_LEVEL_WRITERS(error, Level::Error) +# else +LOGGER_LEVEL_WRITERS_DISABLED(error, Level::Error) +# endif // ELPP_ERROR_LOG +# if ELPP_FATAL_LOG +LOGGER_LEVEL_WRITERS(fatal, Level::Fatal) +# else +LOGGER_LEVEL_WRITERS_DISABLED(fatal, Level::Fatal) +# endif // ELPP_FATAL_LOG +# if ELPP_TRACE_LOG +LOGGER_LEVEL_WRITERS(trace, Level::Trace) +# else +LOGGER_LEVEL_WRITERS_DISABLED(trace, Level::Trace) +# endif // ELPP_TRACE_LOG +# undef LOGGER_LEVEL_WRITERS +# undef LOGGER_LEVEL_WRITERS_DISABLED +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED +#if ELPP_COMPILER_MSVC +# define ELPP_VARIADIC_FUNC_MSVC(variadicFunction, variadicArgs) variadicFunction variadicArgs +# define ELPP_VARIADIC_FUNC_MSVC_RUN(variadicFunction, ...) ELPP_VARIADIC_FUNC_MSVC(variadicFunction, (__VA_ARGS__)) +# define el_getVALength(...) ELPP_VARIADIC_FUNC_MSVC_RUN(el_resolveVALength, 0, ## __VA_ARGS__,\ +10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +#else +# if ELPP_COMPILER_CLANG +# define el_getVALength(...) el_resolveVALength(0, __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +# else +# define el_getVALength(...) el_resolveVALength(0, ## __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +# endif // ELPP_COMPILER_CLANG +#endif // ELPP_COMPILER_MSVC +#define el_resolveVALength(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N +#define ELPP_WRITE_LOG(writer, level, dispatchAction, ...) \ +writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_IF(writer, condition, level, dispatchAction, ...) if (condition) \ +writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_EVERY_N(writer, occasion, level, dispatchAction, ...) \ +ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion) && \ +writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_AFTER_N(writer, n, level, dispatchAction, ...) \ +ELPP->validateAfterNCounter(__FILE__, __LINE__, n) && \ +writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_N_TIMES(writer, n, level, dispatchAction, ...) \ +ELPP->validateNTimesCounter(__FILE__, __LINE__, n) && \ +writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) +class PerformanceTrackingData { + public: + enum class DataType : base::type::EnumType { + Checkpoint = 1, Complete = 2 + }; + // Do not use constructor, will run into multiple definition error, use init(PerformanceTracker*) + explicit PerformanceTrackingData(DataType dataType) : m_performanceTracker(nullptr), + m_dataType(dataType), m_firstCheckpoint(false), m_file(""), m_line(0), m_func("") {} + inline const std::string* blockName(void) const; + inline const struct timeval* startTime(void) const; + inline const struct timeval* endTime(void) const; + inline const struct timeval* lastCheckpointTime(void) const; + inline const base::PerformanceTracker* performanceTracker(void) const { + return m_performanceTracker; + } + inline PerformanceTrackingData::DataType dataType(void) const { + return m_dataType; + } + inline bool firstCheckpoint(void) const { + return m_firstCheckpoint; + } + inline std::string checkpointId(void) const { + return m_checkpointId; + } + inline const char* file(void) const { + return m_file; + } + inline base::type::LineNumber line(void) const { + return m_line; + } + inline const char* func(void) const { + return m_func; + } + inline const base::type::string_t* formattedTimeTaken() const { + return &m_formattedTimeTaken; + } + inline const std::string& loggerId(void) const; + private: + base::PerformanceTracker* m_performanceTracker; + base::type::string_t m_formattedTimeTaken; + PerformanceTrackingData::DataType m_dataType; + bool m_firstCheckpoint; + std::string m_checkpointId; + const char* m_file; + base::type::LineNumber m_line; + const char* m_func; + inline void init(base::PerformanceTracker* performanceTracker, bool firstCheckpoint = false) { + m_performanceTracker = performanceTracker; + m_firstCheckpoint = firstCheckpoint; + } + + friend class el::base::PerformanceTracker; +}; +namespace base { +/// @brief Represents performanceTracker block of code that conditionally adds performance status to log +/// either when goes outside the scope of when checkpoint() is called +class PerformanceTracker : public base::threading::ThreadSafe, public Loggable { + public: + PerformanceTracker(const std::string& blockName, + base::TimestampUnit timestampUnit = base::TimestampUnit::Millisecond, + const std::string& loggerId = std::string(el::base::consts::kPerformanceLoggerId), + bool scopedLog = true, Level level = base::consts::kPerformanceTrackerDefaultLevel); + /// @brief Copy constructor + PerformanceTracker(const PerformanceTracker& t) : + m_blockName(t.m_blockName), m_timestampUnit(t.m_timestampUnit), m_loggerId(t.m_loggerId), m_scopedLog(t.m_scopedLog), + m_level(t.m_level), m_hasChecked(t.m_hasChecked), m_lastCheckpointId(t.m_lastCheckpointId), m_enabled(t.m_enabled), + m_startTime(t.m_startTime), m_endTime(t.m_endTime), m_lastCheckpointTime(t.m_lastCheckpointTime) { + } + virtual ~PerformanceTracker(void); + /// @brief A checkpoint for current performanceTracker block. + void checkpoint(const std::string& id = std::string(), const char* file = __FILE__, + base::type::LineNumber line = __LINE__, + const char* func = ""); + inline Level level(void) const { + return m_level; + } + private: + std::string m_blockName; + base::TimestampUnit m_timestampUnit; + std::string m_loggerId; + bool m_scopedLog; + Level m_level; + bool m_hasChecked; + std::string m_lastCheckpointId; + bool m_enabled; + struct timeval m_startTime, m_endTime, m_lastCheckpointTime; + + PerformanceTracker(void); + + friend class el::PerformanceTrackingData; + friend class base::DefaultPerformanceTrackingCallback; + + const inline base::type::string_t getFormattedTimeTaken() const { + return getFormattedTimeTaken(m_startTime); + } + + const base::type::string_t getFormattedTimeTaken(struct timeval startTime) const; + + virtual inline void log(el::base::type::ostream_t& os) const { + os << getFormattedTimeTaken(); + } +}; +class DefaultPerformanceTrackingCallback : public PerformanceTrackingCallback { + protected: + void handle(const PerformanceTrackingData* data) { + m_data = data; + base::type::stringstream_t ss; + if (m_data->dataType() == PerformanceTrackingData::DataType::Complete) { + ss << ELPP_LITERAL("Executed [") << m_data->blockName()->c_str() << ELPP_LITERAL("] in [") << + *m_data->formattedTimeTaken() << ELPP_LITERAL("]"); + } else { + ss << ELPP_LITERAL("Performance checkpoint"); + if (!m_data->checkpointId().empty()) { + ss << ELPP_LITERAL(" [") << m_data->checkpointId().c_str() << ELPP_LITERAL("]"); + } + ss << ELPP_LITERAL(" for block [") << m_data->blockName()->c_str() << ELPP_LITERAL("] : [") << + *m_data->performanceTracker(); + if (!ELPP->hasFlag(LoggingFlag::DisablePerformanceTrackingCheckpointComparison) + && m_data->performanceTracker()->m_hasChecked) { + ss << ELPP_LITERAL(" ([") << *m_data->formattedTimeTaken() << ELPP_LITERAL("] from "); + if (m_data->performanceTracker()->m_lastCheckpointId.empty()) { + ss << ELPP_LITERAL("last checkpoint"); + } else { + ss << ELPP_LITERAL("checkpoint '") << m_data->performanceTracker()->m_lastCheckpointId.c_str() << ELPP_LITERAL("'"); + } + ss << ELPP_LITERAL(")]"); + } else { + ss << ELPP_LITERAL("]"); + } + } + el::base::Writer(m_data->performanceTracker()->level(), m_data->file(), m_data->line(), m_data->func()).construct(1, + m_data->loggerId().c_str()) << ss.str(); + } + private: + const PerformanceTrackingData* m_data; +}; +} // namespace base +inline const std::string* PerformanceTrackingData::blockName() const { + return const_cast(&m_performanceTracker->m_blockName); +} +inline const struct timeval* PerformanceTrackingData::startTime() const { + return const_cast(&m_performanceTracker->m_startTime); +} +inline const struct timeval* PerformanceTrackingData::endTime() const { + return const_cast(&m_performanceTracker->m_endTime); +} +inline const struct timeval* PerformanceTrackingData::lastCheckpointTime() const { + return const_cast(&m_performanceTracker->m_lastCheckpointTime); +} +inline const std::string& PerformanceTrackingData::loggerId(void) const { + return m_performanceTracker->m_loggerId; +} +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) +namespace base { +/// @brief Contains some internal debugging tools like crash handler and stack tracer +namespace debug { +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) +class StackTrace : base::NoCopy { + public: + static const unsigned int kMaxStack = 64; + static const unsigned int kStackStart = 2; // We want to skip c'tor and StackTrace::generateNew() + class StackTraceEntry { + public: + StackTraceEntry(std::size_t index, const std::string& loc, const std::string& demang, const std::string& hex, + const std::string& addr); + StackTraceEntry(std::size_t index, const std::string& loc) : + m_index(index), + m_location(loc) { + } + std::size_t m_index; + std::string m_location; + std::string m_demangled; + std::string m_hex; + std::string m_addr; + friend std::ostream& operator<<(std::ostream& ss, const StackTraceEntry& si); + + private: + StackTraceEntry(void); + }; + + StackTrace(void) { + generateNew(); + } + + virtual ~StackTrace(void) { + } + + inline std::vector& getLatestStack(void) { + return m_stack; + } + + friend std::ostream& operator<<(std::ostream& os, const StackTrace& st); + + private: + std::vector m_stack; + + void generateNew(void); +}; +/// @brief Handles unexpected crashes +class CrashHandler : base::NoCopy { + public: + typedef void (*Handler)(int); + + explicit CrashHandler(bool useDefault); + explicit CrashHandler(const Handler& cHandler) { + setHandler(cHandler); + } + void setHandler(const Handler& cHandler); + + private: + Handler m_handler; +}; +#else +class CrashHandler { + public: + explicit CrashHandler(bool) {} +}; +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) +} // namespace debug +} // namespace base +extern base::debug::CrashHandler elCrashHandler; +#define MAKE_LOGGABLE(ClassType, ClassInstance, OutputStreamInstance) \ +el::base::type::ostream_t& operator<<(el::base::type::ostream_t& OutputStreamInstance, const ClassType& ClassInstance) +/// @brief Initializes syslog with process ID, options and facility. calls closelog() on d'tor +class SysLogInitializer { + public: + SysLogInitializer(const char* processIdent, int options = 0, int facility = 0) { +#if defined(ELPP_SYSLOG) + openlog(processIdent, options, facility); +#else + ELPP_UNUSED(processIdent); + ELPP_UNUSED(options); + ELPP_UNUSED(facility); +#endif // defined(ELPP_SYSLOG) + } + virtual ~SysLogInitializer(void) { +#if defined(ELPP_SYSLOG) + closelog(); +#endif // defined(ELPP_SYSLOG) + } +}; +#define ELPP_INITIALIZE_SYSLOG(id, opt, fac) el::SysLogInitializer elSyslogInit(id, opt, fac) +/// @brief Static helpers for developers +class Helpers : base::StaticClass { + public: + /// @brief Shares logging repository (base::Storage) + static inline void setStorage(base::type::StoragePointer storage) { + ELPP = storage; + } + /// @return Main storage repository + static inline base::type::StoragePointer storage() { + return ELPP; + } + /// @brief Sets application arguments and figures out whats active for logging and whats not. + static inline void setArgs(int argc, char** argv) { + ELPP->setApplicationArguments(argc, argv); + } + /// @copydoc setArgs(int argc, char** argv) + static inline void setArgs(int argc, const char** argv) { + ELPP->setApplicationArguments(argc, const_cast(argv)); + } + /// @brief Sets thread name for current thread. Requires std::thread + static inline void setThreadName(const std::string& name) { + ELPP->setThreadName(name); + } + static inline std::string getThreadName() { + return ELPP->getThreadName(base::threading::getCurrentThreadId()); + } +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + /// @brief Overrides default crash handler and installs custom handler. + /// @param crashHandler A functor with no return type that takes single int argument. + /// Handler is a typedef with specification: void (*Handler)(int) + static inline void setCrashHandler(const el::base::debug::CrashHandler::Handler& crashHandler) { + el::elCrashHandler.setHandler(crashHandler); + } + /// @brief Abort due to crash with signal in parameter + /// @param sig Crash signal + static void crashAbort(int sig, const char* sourceFile = "", unsigned int long line = 0); + /// @brief Logs reason of crash as per sig + /// @param sig Crash signal + /// @param stackTraceIfAvailable Includes stack trace if available + /// @param level Logging level + /// @param logger Logger to use for logging + static void logCrashReason(int sig, bool stackTraceIfAvailable = false, + Level level = Level::Fatal, const char* logger = base::consts::kDefaultLoggerId); +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + /// @brief Installs pre rollout callback, this callback is triggered when log file is about to be rolled out + /// (can be useful for backing up) + static inline void installPreRollOutCallback(const PreRollOutCallback& callback) { + ELPP->setPreRollOutCallback(callback); + } + /// @brief Uninstalls pre rollout callback + static inline void uninstallPreRollOutCallback(void) { + ELPP->unsetPreRollOutCallback(); + } + /// @brief Installs post log dispatch callback, this callback is triggered when log is dispatched + template + static inline bool installLogDispatchCallback(const std::string& id) { + return ELPP->installLogDispatchCallback(id); + } + /// @brief Uninstalls log dispatch callback + template + static inline void uninstallLogDispatchCallback(const std::string& id) { + ELPP->uninstallLogDispatchCallback(id); + } + template + static inline T* logDispatchCallback(const std::string& id) { + return ELPP->logDispatchCallback(id); + } +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + /// @brief Installs post performance tracking callback, this callback is triggered when performance tracking is finished + template + static inline bool installPerformanceTrackingCallback(const std::string& id) { + return ELPP->installPerformanceTrackingCallback(id); + } + /// @brief Uninstalls post performance tracking handler + template + static inline void uninstallPerformanceTrackingCallback(const std::string& id) { + ELPP->uninstallPerformanceTrackingCallback(id); + } + template + static inline T* performanceTrackingCallback(const std::string& id) { + return ELPP->performanceTrackingCallback(id); + } +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + /// @brief Converts template to std::string - useful for loggable classes to log containers within log(std::ostream&) const + template + static std::string convertTemplateToStdString(const T& templ) { + el::Logger* logger = + ELPP->registeredLoggers()->get(el::base::consts::kDefaultLoggerId); + if (logger == nullptr) { + return std::string(); + } + base::MessageBuilder b; + b.initialize(logger); + logger->acquireLock(); + b << templ; +#if defined(ELPP_UNICODE) + std::string s = std::string(logger->stream().str().begin(), logger->stream().str().end()); +#else + std::string s = logger->stream().str(); +#endif // defined(ELPP_UNICODE) + logger->stream().str(ELPP_LITERAL("")); + logger->releaseLock(); + return s; + } + /// @brief Returns command line arguments (pointer) provided to easylogging++ + static inline const el::base::utils::CommandLineArgs* commandLineArgs(void) { + return ELPP->commandLineArgs(); + } + /// @brief Reserve space for custom format specifiers for performance + /// @see std::vector::reserve + static inline void reserveCustomFormatSpecifiers(std::size_t size) { + ELPP->m_customFormatSpecifiers.reserve(size); + } + /// @brief Installs user defined format specifier and handler + static inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { + ELPP->installCustomFormatSpecifier(customFormatSpecifier); + } + /// @brief Uninstalls user defined format specifier and handler + static inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) { + return ELPP->uninstallCustomFormatSpecifier(formatSpecifier); + } + /// @brief Returns true if custom format specifier is installed + static inline bool hasCustomFormatSpecifier(const char* formatSpecifier) { + return ELPP->hasCustomFormatSpecifier(formatSpecifier); + } + static inline void validateFileRolling(Logger* logger, Level level) { + if (ELPP == nullptr || logger == nullptr) return; + logger->m_typedConfigurations->validateFileRolling(level, ELPP->preRollOutCallback()); + } +}; +/// @brief Static helpers to deal with loggers and their configurations +class Loggers : base::StaticClass { + public: + /// @brief Gets existing or registers new logger + static Logger* getLogger(const std::string& identity, bool registerIfNotAvailable = true); + /// @brief Changes default log builder for future loggers + static void setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr); + /// @brief Installs logger registration callback, this callback is triggered when new logger is registered + template + static inline bool installLoggerRegistrationCallback(const std::string& id) { + return ELPP->registeredLoggers()->installLoggerRegistrationCallback(id); + } + /// @brief Uninstalls log dispatch callback + template + static inline void uninstallLoggerRegistrationCallback(const std::string& id) { + ELPP->registeredLoggers()->uninstallLoggerRegistrationCallback(id); + } + template + static inline T* loggerRegistrationCallback(const std::string& id) { + return ELPP->registeredLoggers()->loggerRegistrationCallback(id); + } + /// @brief Unregisters logger - use it only when you know what you are doing, you may unregister + /// loggers initialized / used by third-party libs. + static bool unregisterLogger(const std::string& identity); + /// @brief Whether or not logger with id is registered + static bool hasLogger(const std::string& identity); + /// @brief Reconfigures specified logger with new configurations + static Logger* reconfigureLogger(Logger* logger, const Configurations& configurations); + /// @brief Reconfigures logger with new configurations after looking it up using identity + static Logger* reconfigureLogger(const std::string& identity, const Configurations& configurations); + /// @brief Reconfigures logger's single configuration + static Logger* reconfigureLogger(const std::string& identity, ConfigurationType configurationType, + const std::string& value); + /// @brief Reconfigures all the existing loggers with new configurations + static void reconfigureAllLoggers(const Configurations& configurations); + /// @brief Reconfigures single configuration for all the loggers + static inline void reconfigureAllLoggers(ConfigurationType configurationType, const std::string& value) { + reconfigureAllLoggers(Level::Global, configurationType, value); + } + /// @brief Reconfigures single configuration for all the loggers for specified level + static void reconfigureAllLoggers(Level level, ConfigurationType configurationType, + const std::string& value); + /// @brief Sets default configurations. This configuration is used for future (and conditionally for existing) loggers + static void setDefaultConfigurations(const Configurations& configurations, + bool reconfigureExistingLoggers = false); + /// @brief Returns current default + static const Configurations* defaultConfigurations(void); + /// @brief Returns log stream reference pointer if needed by user + static const base::LogStreamsReferenceMap* logStreamsReference(void); + /// @brief Default typed configuration based on existing defaultConf + static base::TypedConfigurations defaultTypedConfigurations(void); + /// @brief Populates all logger IDs in current repository. + /// @param [out] targetList List of fill up. + static std::vector* populateAllLoggerIds(std::vector* targetList); + /// @brief Sets configurations from global configuration file. + static void configureFromGlobal(const char* globalConfigurationFilePath); + /// @brief Configures loggers using command line arg. Ensure you have already set command line args, + /// @return False if invalid argument or argument with no value provided, true if attempted to configure logger. + /// If true is returned that does not mean it has been configured successfully, it only means that it + /// has attempeted to configure logger using configuration file provided in argument + static bool configureFromArg(const char* argKey); + /// @brief Flushes all loggers for all levels - Be careful if you dont know how many loggers are registered + static void flushAll(void); + /// @brief Adds logging flag used internally. + static inline void addFlag(LoggingFlag flag) { + ELPP->addFlag(flag); + } + /// @brief Removes logging flag used internally. + static inline void removeFlag(LoggingFlag flag) { + ELPP->removeFlag(flag); + } + /// @brief Determines whether or not certain flag is active + static inline bool hasFlag(LoggingFlag flag) { + return ELPP->hasFlag(flag); + } + /// @brief Adds flag and removes it when scope goes out + class ScopedAddFlag { + public: + ScopedAddFlag(LoggingFlag flag) : m_flag(flag) { + Loggers::addFlag(m_flag); + } + ~ScopedAddFlag(void) { + Loggers::removeFlag(m_flag); + } + private: + LoggingFlag m_flag; + }; + /// @brief Removes flag and add it when scope goes out + class ScopedRemoveFlag { + public: + ScopedRemoveFlag(LoggingFlag flag) : m_flag(flag) { + Loggers::removeFlag(m_flag); + } + ~ScopedRemoveFlag(void) { + Loggers::addFlag(m_flag); + } + private: + LoggingFlag m_flag; + }; + /// @brief Sets hierarchy for logging. Needs to enable logging flag (HierarchicalLogging) + static void setLoggingLevel(Level level) { + ELPP->setLoggingLevel(level); + } + /// @brief Sets verbose level on the fly + static void setVerboseLevel(base::type::VerboseLevel level); + /// @brief Gets current verbose level + static base::type::VerboseLevel verboseLevel(void); + /// @brief Sets vmodules as specified (on the fly) + static void setVModules(const char* modules); + /// @brief Clears vmodules + static void clearVModules(void); +}; +class VersionInfo : base::StaticClass { + public: + /// @brief Current version number + static const std::string version(void); + + /// @brief Release date of current version + static const std::string releaseDate(void); +}; +} // namespace el +#undef VLOG_IS_ON +/// @brief Determines whether verbose logging is on for specified level current file. +#define VLOG_IS_ON(verboseLevel) (ELPP->vRegistry()->allowed(verboseLevel, __FILE__)) +#undef TIMED_BLOCK +#undef TIMED_SCOPE +#undef TIMED_SCOPE_IF +#undef TIMED_FUNC +#undef TIMED_FUNC_IF +#undef ELPP_MIN_UNIT +#if defined(ELPP_PERFORMANCE_MICROSECONDS) +# define ELPP_MIN_UNIT el::base::TimestampUnit::Microsecond +#else +# define ELPP_MIN_UNIT el::base::TimestampUnit::Millisecond +#endif // (defined(ELPP_PERFORMANCE_MICROSECONDS)) +/// @brief Performance tracked scope. Performance gets written when goes out of scope using +/// 'performance' logger. +/// +/// @detail Please note in order to check the performance at a certain time you can use obj->checkpoint(); +/// @see el::base::PerformanceTracker +/// @see el::base::PerformanceTracker::checkpoint +// Note: Do not surround this definition with null macro because of obj instance +#define TIMED_SCOPE_IF(obj, blockname, condition) el::base::type::PerformanceTrackerPtr obj( condition ? \ + new el::base::PerformanceTracker(blockname, ELPP_MIN_UNIT) : nullptr ) +#define TIMED_SCOPE(obj, blockname) TIMED_SCOPE_IF(obj, blockname, true) +#define TIMED_BLOCK(obj, blockName) for (struct { int i; el::base::type::PerformanceTrackerPtr timer; } obj = { 0, \ + el::base::type::PerformanceTrackerPtr(new el::base::PerformanceTracker(blockName, ELPP_MIN_UNIT)) }; obj.i < 1; ++obj.i) +/// @brief Performance tracked function. Performance gets written when goes out of scope using +/// 'performance' logger. +/// +/// @detail Please note in order to check the performance at a certain time you can use obj->checkpoint(); +/// @see el::base::PerformanceTracker +/// @see el::base::PerformanceTracker::checkpoint +#define TIMED_FUNC_IF(obj,condition) TIMED_SCOPE_IF(obj, ELPP_FUNC, condition) +#define TIMED_FUNC(obj) TIMED_SCOPE(obj, ELPP_FUNC) +#undef PERFORMANCE_CHECKPOINT +#undef PERFORMANCE_CHECKPOINT_WITH_ID +#define PERFORMANCE_CHECKPOINT(obj) obj->checkpoint(std::string(), __FILE__, __LINE__, ELPP_FUNC) +#define PERFORMANCE_CHECKPOINT_WITH_ID(obj, id) obj->checkpoint(id, __FILE__, __LINE__, ELPP_FUNC) +#undef ELPP_COUNTER +#undef ELPP_COUNTER_POS +/// @brief Gets hit counter for file/line +#define ELPP_COUNTER (ELPP->hitCounters()->getCounter(__FILE__, __LINE__)) +/// @brief Gets hit counter position for file/line, -1 if not registered yet +#define ELPP_COUNTER_POS (ELPP_COUNTER == nullptr ? -1 : ELPP_COUNTER->hitCounts()) +// Undef levels to support LOG(LEVEL) +#undef INFO +#undef WARNING +#undef DEBUG +#undef ERROR +#undef FATAL +#undef TRACE +#undef VERBOSE +// Undef existing +#undef CINFO +#undef CWARNING +#undef CDEBUG +#undef CFATAL +#undef CERROR +#undef CTRACE +#undef CVERBOSE +#undef CINFO_IF +#undef CWARNING_IF +#undef CDEBUG_IF +#undef CERROR_IF +#undef CFATAL_IF +#undef CTRACE_IF +#undef CVERBOSE_IF +#undef CINFO_EVERY_N +#undef CWARNING_EVERY_N +#undef CDEBUG_EVERY_N +#undef CERROR_EVERY_N +#undef CFATAL_EVERY_N +#undef CTRACE_EVERY_N +#undef CVERBOSE_EVERY_N +#undef CINFO_AFTER_N +#undef CWARNING_AFTER_N +#undef CDEBUG_AFTER_N +#undef CERROR_AFTER_N +#undef CFATAL_AFTER_N +#undef CTRACE_AFTER_N +#undef CVERBOSE_AFTER_N +#undef CINFO_N_TIMES +#undef CWARNING_N_TIMES +#undef CDEBUG_N_TIMES +#undef CERROR_N_TIMES +#undef CFATAL_N_TIMES +#undef CTRACE_N_TIMES +#undef CVERBOSE_N_TIMES +// Normal logs +#if ELPP_INFO_LOG +# define CINFO(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE(writer, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel)) writer(\ +el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#else +# define CVERBOSE(writer, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// Conditional logs +#if ELPP_INFO_LOG +# define CINFO_IF(writer, condition_, dispatchAction, ...) \ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_IF(writer, condition_, dispatchAction, ...)\ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_IF(writer, condition_, dispatchAction, ...)\ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_IF(writer, condition_, dispatchAction, ...)\ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_IF(writer, condition_, dispatchAction, ...)\ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_IF(writer, condition_, dispatchAction, ...)\ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel) && (condition_)) writer( \ +el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#else +# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// Occasional logs +#if ELPP_INFO_LOG +# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...)\ +CVERBOSE_IF(writer, ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// After N logs +#if ELPP_INFO_LOG +# define CINFO_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...)\ +CVERBOSE_IF(writer, ELPP->validateAfterNCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// N Times logs +#if ELPP_INFO_LOG +# define CINFO_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...)\ +CVERBOSE_IF(writer, ELPP->validateNTimesCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// +// Custom Loggers - Requires (level, dispatchAction, loggerId/s) +// +// undef existing +#undef CLOG +#undef CLOG_VERBOSE +#undef CVLOG +#undef CLOG_IF +#undef CLOG_VERBOSE_IF +#undef CVLOG_IF +#undef CLOG_EVERY_N +#undef CVLOG_EVERY_N +#undef CLOG_AFTER_N +#undef CVLOG_AFTER_N +#undef CLOG_N_TIMES +#undef CVLOG_N_TIMES +// Normal logs +#define CLOG(LEVEL, ...)\ +C##LEVEL(el::base::Writer, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG(vlevel, ...) CVERBOSE(el::base::Writer, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// Conditional logs +#define CLOG_IF(condition, LEVEL, ...)\ +C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_IF(condition, vlevel, ...)\ +CVERBOSE_IF(el::base::Writer, condition, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// Hit counts based logs +#define CLOG_EVERY_N(n, LEVEL, ...)\ +C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_EVERY_N(n, vlevel, ...)\ +CVERBOSE_EVERY_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CLOG_AFTER_N(n, LEVEL, ...)\ +C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_AFTER_N(n, vlevel, ...)\ +CVERBOSE_AFTER_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CLOG_N_TIMES(n, LEVEL, ...)\ +C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_N_TIMES(n, vlevel, ...)\ +CVERBOSE_N_TIMES(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// +// Default Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros +// +// undef existing +#undef LOG +#undef VLOG +#undef LOG_IF +#undef VLOG_IF +#undef LOG_EVERY_N +#undef VLOG_EVERY_N +#undef LOG_AFTER_N +#undef VLOG_AFTER_N +#undef LOG_N_TIMES +#undef VLOG_N_TIMES +#undef ELPP_CURR_FILE_LOGGER_ID +#if defined(ELPP_DEFAULT_LOGGER) +# define ELPP_CURR_FILE_LOGGER_ID ELPP_DEFAULT_LOGGER +#else +# define ELPP_CURR_FILE_LOGGER_ID el::base::consts::kDefaultLoggerId +#endif +#undef ELPP_TRACE +#define ELPP_TRACE CLOG(TRACE, ELPP_CURR_FILE_LOGGER_ID) +// Normal logs +#define LOG(LEVEL) CLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG(vlevel) CVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Conditional logs +#define LOG_IF(condition, LEVEL) CLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_IF(condition, vlevel) CVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Hit counts based logs +#define LOG_EVERY_N(n, LEVEL) CLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_EVERY_N(n, vlevel) CVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define LOG_AFTER_N(n, LEVEL) CLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_AFTER_N(n, vlevel) CVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define LOG_N_TIMES(n, LEVEL) CLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_N_TIMES(n, vlevel) CVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Generic PLOG() +#undef CPLOG +#undef CPLOG_IF +#undef PLOG +#undef PLOG_IF +#undef DCPLOG +#undef DCPLOG_IF +#undef DPLOG +#undef DPLOG_IF +#define CPLOG(LEVEL, ...)\ +C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CPLOG_IF(condition, LEVEL, ...)\ +C##LEVEL##_IF(el::base::PErrorWriter, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define DCPLOG(LEVEL, ...)\ +if (ELPP_DEBUG_LOG) C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define DCPLOG_IF(condition, LEVEL, ...)\ +C##LEVEL##_IF(el::base::PErrorWriter, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define PLOG(LEVEL) CPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define PLOG_IF(condition, LEVEL) CPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DPLOG(LEVEL) DCPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DPLOG_IF(condition, LEVEL) DCPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +// Generic SYSLOG() +#undef CSYSLOG +#undef CSYSLOG_IF +#undef CSYSLOG_EVERY_N +#undef CSYSLOG_AFTER_N +#undef CSYSLOG_N_TIMES +#undef SYSLOG +#undef SYSLOG_IF +#undef SYSLOG_EVERY_N +#undef SYSLOG_AFTER_N +#undef SYSLOG_N_TIMES +#undef DCSYSLOG +#undef DCSYSLOG_IF +#undef DCSYSLOG_EVERY_N +#undef DCSYSLOG_AFTER_N +#undef DCSYSLOG_N_TIMES +#undef DSYSLOG +#undef DSYSLOG_IF +#undef DSYSLOG_EVERY_N +#undef DSYSLOG_AFTER_N +#undef DSYSLOG_N_TIMES +#if defined(ELPP_SYSLOG) +# define CSYSLOG(LEVEL, ...)\ +C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_IF(condition, LEVEL, ...)\ +C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_EVERY_N(n, LEVEL, ...) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_AFTER_N(n, LEVEL, ...) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_N_TIMES(n, LEVEL, ...) C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define SYSLOG(LEVEL) CSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_IF(condition, LEVEL) CSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_EVERY_N(n, LEVEL) CSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_AFTER_N(n, LEVEL) CSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_N_TIMES(n, LEVEL) CSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DCSYSLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_IF(condition, LEVEL, ...)\ +C##LEVEL##_IF(el::base::Writer, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_EVERY_N(n, LEVEL, ...)\ +if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_AFTER_N(n, LEVEL, ...)\ +if (ELPP_DEBUG_LOG) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_N_TIMES(n, LEVEL, ...)\ +if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DSYSLOG(LEVEL) DCSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_IF(condition, LEVEL) DCSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_EVERY_N(n, LEVEL) DCSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_AFTER_N(n, LEVEL) DCSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_N_TIMES(n, LEVEL) DCSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) +#else +# define CSYSLOG(LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() +# define SYSLOG(LEVEL) el::base::NullWriter() +# define SYSLOG_IF(condition, LEVEL) el::base::NullWriter() +# define SYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() +# define SYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() +# define SYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() +# define DCSYSLOG(LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() +# define DSYSLOG(LEVEL) el::base::NullWriter() +# define DSYSLOG_IF(condition, LEVEL) el::base::NullWriter() +# define DSYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() +# define DSYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() +# define DSYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() +#endif // defined(ELPP_SYSLOG) +// +// Custom Debug Only Loggers - Requires (level, loggerId/s) +// +// undef existing +#undef DCLOG +#undef DCVLOG +#undef DCLOG_IF +#undef DCVLOG_IF +#undef DCLOG_EVERY_N +#undef DCVLOG_EVERY_N +#undef DCLOG_AFTER_N +#undef DCVLOG_AFTER_N +#undef DCLOG_N_TIMES +#undef DCVLOG_N_TIMES +// Normal logs +#define DCLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG(LEVEL, __VA_ARGS__) +#define DCLOG_VERBOSE(vlevel, ...) if (ELPP_DEBUG_LOG) CLOG_VERBOSE(vlevel, __VA_ARGS__) +#define DCVLOG(vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG(vlevel, __VA_ARGS__) +// Conditional logs +#define DCLOG_IF(condition, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_IF(condition, LEVEL, __VA_ARGS__) +#define DCVLOG_IF(condition, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_IF(condition, vlevel, __VA_ARGS__) +// Hit counts based logs +#define DCLOG_EVERY_N(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_EVERY_N(n, LEVEL, __VA_ARGS__) +#define DCVLOG_EVERY_N(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_EVERY_N(n, vlevel, __VA_ARGS__) +#define DCLOG_AFTER_N(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_AFTER_N(n, LEVEL, __VA_ARGS__) +#define DCVLOG_AFTER_N(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_AFTER_N(n, vlevel, __VA_ARGS__) +#define DCLOG_N_TIMES(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_N_TIMES(n, LEVEL, __VA_ARGS__) +#define DCVLOG_N_TIMES(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_N_TIMES(n, vlevel, __VA_ARGS__) +// +// Default Debug Only Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros +// +#if !defined(ELPP_NO_DEBUG_MACROS) +// undef existing +#undef DLOG +#undef DVLOG +#undef DLOG_IF +#undef DVLOG_IF +#undef DLOG_EVERY_N +#undef DVLOG_EVERY_N +#undef DLOG_AFTER_N +#undef DVLOG_AFTER_N +#undef DLOG_N_TIMES +#undef DVLOG_N_TIMES +// Normal logs +#define DLOG(LEVEL) DCLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG(vlevel) DCVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Conditional logs +#define DLOG_IF(condition, LEVEL) DCLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_IF(condition, vlevel) DCVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Hit counts based logs +#define DLOG_EVERY_N(n, LEVEL) DCLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_EVERY_N(n, vlevel) DCVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define DLOG_AFTER_N(n, LEVEL) DCLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_AFTER_N(n, vlevel) DCVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define DLOG_N_TIMES(n, LEVEL) DCLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_N_TIMES(n, vlevel) DCVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#endif // defined(ELPP_NO_DEBUG_MACROS) +#if !defined(ELPP_NO_CHECK_MACROS) +// Check macros +#undef CCHECK +#undef CPCHECK +#undef CCHECK_EQ +#undef CCHECK_NE +#undef CCHECK_LT +#undef CCHECK_GT +#undef CCHECK_LE +#undef CCHECK_GE +#undef CCHECK_BOUNDS +#undef CCHECK_NOTNULL +#undef CCHECK_STRCASEEQ +#undef CCHECK_STRCASENE +#undef CHECK +#undef PCHECK +#undef CHECK_EQ +#undef CHECK_NE +#undef CHECK_LT +#undef CHECK_GT +#undef CHECK_LE +#undef CHECK_GE +#undef CHECK_BOUNDS +#undef CHECK_NOTNULL +#undef CHECK_STRCASEEQ +#undef CHECK_STRCASENE +#define CCHECK(condition, ...) CLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] " +#define CPCHECK(condition, ...) CPLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] " +#define CHECK(condition) CCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define PCHECK(condition) CPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define CCHECK_EQ(a, b, ...) CCHECK(a == b, __VA_ARGS__) +#define CCHECK_NE(a, b, ...) CCHECK(a != b, __VA_ARGS__) +#define CCHECK_LT(a, b, ...) CCHECK(a < b, __VA_ARGS__) +#define CCHECK_GT(a, b, ...) CCHECK(a > b, __VA_ARGS__) +#define CCHECK_LE(a, b, ...) CCHECK(a <= b, __VA_ARGS__) +#define CCHECK_GE(a, b, ...) CCHECK(a >= b, __VA_ARGS__) +#define CCHECK_BOUNDS(val, min, max, ...) CCHECK(val >= min && val <= max, __VA_ARGS__) +#define CHECK_EQ(a, b) CCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_NE(a, b) CCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_LT(a, b) CCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_GT(a, b) CCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_LE(a, b) CCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_GE(a, b) CCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_BOUNDS(val, min, max) CCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID) +#define CCHECK_NOTNULL(ptr, ...) CCHECK((ptr) != nullptr, __VA_ARGS__) +#define CCHECK_STREQ(str1, str2, ...) CLOG_IF(!el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \ +<< "Check failed: [" << #str1 << " == " << #str2 << "] " +#define CCHECK_STRNE(str1, str2, ...) CLOG_IF(el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \ +<< "Check failed: [" << #str1 << " != " << #str2 << "] " +#define CCHECK_STRCASEEQ(str1, str2, ...) CLOG_IF(!el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \ +<< "Check failed: [" << #str1 << " == " << #str2 << "] " +#define CCHECK_STRCASENE(str1, str2, ...) CLOG_IF(el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \ +<< "Check failed: [" << #str1 << " != " << #str2 << "] " +#define CHECK_NOTNULL(ptr) CCHECK_NOTNULL((ptr), ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STREQ(str1, str2) CCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRNE(str1, str2) CCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRCASEEQ(str1, str2) CCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRCASENE(str1, str2) CCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#undef DCCHECK +#undef DCCHECK_EQ +#undef DCCHECK_NE +#undef DCCHECK_LT +#undef DCCHECK_GT +#undef DCCHECK_LE +#undef DCCHECK_GE +#undef DCCHECK_BOUNDS +#undef DCCHECK_NOTNULL +#undef DCCHECK_STRCASEEQ +#undef DCCHECK_STRCASENE +#undef DCPCHECK +#undef DCHECK +#undef DCHECK_EQ +#undef DCHECK_NE +#undef DCHECK_LT +#undef DCHECK_GT +#undef DCHECK_LE +#undef DCHECK_GE +#undef DCHECK_BOUNDS_ +#undef DCHECK_NOTNULL +#undef DCHECK_STRCASEEQ +#undef DCHECK_STRCASENE +#undef DPCHECK +#define DCCHECK(condition, ...) if (ELPP_DEBUG_LOG) CCHECK(condition, __VA_ARGS__) +#define DCCHECK_EQ(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_EQ(a, b, __VA_ARGS__) +#define DCCHECK_NE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_NE(a, b, __VA_ARGS__) +#define DCCHECK_LT(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_LT(a, b, __VA_ARGS__) +#define DCCHECK_GT(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_GT(a, b, __VA_ARGS__) +#define DCCHECK_LE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_LE(a, b, __VA_ARGS__) +#define DCCHECK_GE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_GE(a, b, __VA_ARGS__) +#define DCCHECK_BOUNDS(val, min, max, ...) if (ELPP_DEBUG_LOG) CCHECK_BOUNDS(val, min, max, __VA_ARGS__) +#define DCCHECK_NOTNULL(ptr, ...) if (ELPP_DEBUG_LOG) CCHECK_NOTNULL((ptr), __VA_ARGS__) +#define DCCHECK_STREQ(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STREQ(str1, str2, __VA_ARGS__) +#define DCCHECK_STRNE(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRNE(str1, str2, __VA_ARGS__) +#define DCCHECK_STRCASEEQ(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRCASEEQ(str1, str2, __VA_ARGS__) +#define DCCHECK_STRCASENE(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRCASENE(str1, str2, __VA_ARGS__) +#define DCPCHECK(condition, ...) if (ELPP_DEBUG_LOG) CPCHECK(condition, __VA_ARGS__) +#define DCHECK(condition) DCCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_EQ(a, b) DCCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_NE(a, b) DCCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_LT(a, b) DCCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_GT(a, b) DCCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_LE(a, b) DCCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_GE(a, b) DCCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_BOUNDS(val, min, max) DCCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_NOTNULL(ptr) DCCHECK_NOTNULL((ptr), ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STREQ(str1, str2) DCCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRNE(str1, str2) DCCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRCASEEQ(str1, str2) DCCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRCASENE(str1, str2) DCCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DPCHECK(condition) DCPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#endif // defined(ELPP_NO_CHECK_MACROS) +#if defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING) +# define ELPP_USE_DEF_CRASH_HANDLER false +#else +# define ELPP_USE_DEF_CRASH_HANDLER true +#endif // defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING) +#define ELPP_CRASH_HANDLER_INIT +#define ELPP_INIT_EASYLOGGINGPP(val) \ +namespace el { \ +namespace base { \ +el::base::type::StoragePointer elStorage(val); \ +} \ +el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER); \ +} + +#if ELPP_ASYNC_LOGGING +# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()),\ +new el::base::AsyncDispatchWorker())) +#else +# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()))) +#endif // ELPP_ASYNC_LOGGING +#define INITIALIZE_NULL_EASYLOGGINGPP \ +namespace el {\ +namespace base {\ +el::base::type::StoragePointer elStorage;\ +}\ +el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ +} +#define SHARE_EASYLOGGINGPP(initializedStorage)\ +namespace el {\ +namespace base {\ +el::base::type::StoragePointer elStorage(initializedStorage);\ +}\ +el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ +} + +#if defined(ELPP_UNICODE) +# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv); std::locale::global(std::locale("")) +#else +# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv) +#endif // defined(ELPP_UNICODE) +#endif // EASYLOGGINGPP_H diff --git a/ConditionMonitoring/main.cpp b/ConditionMonitoring/main.cpp new file mode 100644 index 0000000..b0ec5db --- /dev/null +++ b/ConditionMonitoring/main.cpp @@ -0,0 +1,84 @@ +//Entry point of application, crates central controller class and initializes the publishers + +#include +#include +#include +#include "SystemControler.h" +#include "SystemConfig.h" +#include +#include "PublisherBenderRcm.h" +#include "PublisherPowercenter.h" +INITIALIZE_EASYLOGGINGPP + +using namespace std; + +typedef std::unique_ptr unique_publisher_ptr; +typedef unsigned int u_int; + + +int main() { + + //Load global System configuration for parameters + SystemConfig configuration; + configuration.readStandardConfig(); + + + //Load config file and apply on all loggers + std::filesystem::create_directory("log"); + el::Configurations conf("log/logger_config.conf"); + el::Loggers::reconfigureAllLoggers(conf); + + LOG(INFO) << "Started execution"; + + //Create the System Controller + SystemControler controler; + + //register publisher at controler + auto poc = std::make_unique(); + auto bender = std::make_unique(); + + //controler.registerPublisher(std::move(poc)); + controler.registerPublisher(std::move(bender)); + + //Opens port for tcp/ip client connection + controler.startIpTcpServer(); + + //Modbus Worker Thread, processes the modbus register queue + controler.startModbusWorkerThread(); + + //Frequently enqueing of modbus register + controler.startEnqueuingRegister(); + + + //Test environment on main thread + while(true) + { + char key = cin.get(); + switch (key) { + case 'l': + controler.deleteFiles(std::chrono::seconds(0)); + break; + + case 'c': + controler.cancelReadingModbusAlerts(); + break; + case 'e': + controler.evaluate(); + break; + case 'q': + return 0; + case 'p': + std::cout << configuration << std::endl; + break; + case 'f': + controler.flushAfterNData(); + break; + default: + break; + } + } + + LOG(INFO) << "End of execution\n"; + + return 0; +} diff --git a/ConditionMonitoring/mltimeseries.cpp b/ConditionMonitoring/mltimeseries.cpp new file mode 100644 index 0000000..3aad27f --- /dev/null +++ b/ConditionMonitoring/mltimeseries.cpp @@ -0,0 +1,6 @@ +#include "mltimeseries.h" + +MLtimeSeries::MLtimeSeries() +{ + +} diff --git a/ConditionMonitoring/mltimeseries.h b/ConditionMonitoring/mltimeseries.h new file mode 100644 index 0000000..f200f90 --- /dev/null +++ b/ConditionMonitoring/mltimeseries.h @@ -0,0 +1,12 @@ +#ifndef MLTIMESERIES_H +#define MLTIMESERIES_H +#include "MLAnalyzer.h" + +template +class MLtimeSeries : public MLAnalyzer +{ +public: + MLtimeSeries(); +}; + +#endif // MLTIMESERIES_H diff --git a/ConditionMonitoring/modbus_interface_lib.h b/ConditionMonitoring/modbus_interface_lib.h new file mode 100644 index 0000000..4603b83 --- /dev/null +++ b/ConditionMonitoring/modbus_interface_lib.h @@ -0,0 +1,7 @@ +#pragma once + +#include "ModbusInterface.h" +#include "ModbusRtu.h" +#include "ModbusTcp.h" + +#include diff --git a/ConditionMonitoring/net_server_client.h b/ConditionMonitoring/net_server_client.h new file mode 100644 index 0000000..40c7de7 --- /dev/null +++ b/ConditionMonitoring/net_server_client.h @@ -0,0 +1,64 @@ +/* + MMO Client/Server Framework using ASIO + "Happy Birthday Mrs Javidx9!" - javidx9 + + Videos: + Part #1: https://youtu.be/2hNdkYInj4g + Part #2: https://youtu.be/UbjxGvrDrbw + + License (OLC-3) + ~~~~~~~~~~~~~~~ + + Copyright 2018 - 2020 OneLoneCoder.com + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions or derivations of source code must retain the above + copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions or derivative works in binary form must reproduce + the above copyright notice. This list of conditions and the following + disclaimer must be reproduced in the documentation and/or other + materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Links + ~~~~~ + YouTube: https://www.youtube.com/javidx9 + Discord: https://discord.gg/WhwHUMV + Twitter: https://www.twitter.com/javidx9 + Twitch: https://www.twitch.tv/javidx9 + GitHub: https://www.github.com/onelonecoder + Homepage: https://www.onelonecoder.com + + Author + ~~~~~~ + David Barr, aka javidx9, ©OneLoneCoder 2019, 2020 + +*/ + +#pragma once + +#include "asioClientServer/net_common.h" +#include "asioClientServer/net_dequeue_ts.h" +#include "asioClientServer/net_message.h" +#include "asioClientServer/net_client.h" +#include "asioClientServer/net_server.h" +#include "asioClientServer/net_connection.h" diff --git a/ConditionMonitoring/ts_map.h b/ConditionMonitoring/ts_map.h new file mode 100644 index 0000000..2f902c1 --- /dev/null +++ b/ConditionMonitoring/ts_map.h @@ -0,0 +1,90 @@ +#ifndef TS_MAP_H +#define TS_MAP_H + + +#pragma once +#include +#include +#include + +template +class ts_map +{ + public: + ts_map() = default; + ts_map(const ts_map&) = delete; + virtual ~ts_map() { clear(); } + + public: + void emplaceOrOverwrite(KEY key, VALUE value){ + std::scoped_lock lock(muxMap); + auto it = map.find(key); + if(it != map.end()){ + it->second = value; + } + else{ + map.emplace(key, value); + } + } + + void emplace(KEY key, VALUE&& value){ + std::scoped_lock lock(muxMap); + map.emplace(key, std::move(value)); + } + + auto find(KEY key){ + std::scoped_lock lock(muxMap); + + return map.find(key); + } + + + // Returns true if Queue has no items + bool empty() + { + std::scoped_lock lock(muxMap); + return map.empty(); + } + + // Returns number of items in Queue + size_t size() + { + std::scoped_lock lock(muxMap); + return map.size(); + } + + // Clears Queue + void clear() + { + std::scoped_lock lock(muxMap); + std::map empty; + std::swap(map, empty); + } + + void wait() + { + while (empty()) + { + std::unique_lock ul(muxBlocking); + cvBlocking.wait(ul); + } + } + + auto begin() const{ + return map.begin(); + } + + + auto end() const{ + return map.end(); + } + + protected: + std::mutex muxMap; + std::map map; + std::condition_variable cvBlocking; + std::mutex muxBlocking; + +}; + +#endif // TS_MAP_H diff --git a/ConditionMonitoring/ts_queue.h b/ConditionMonitoring/ts_queue.h new file mode 100644 index 0000000..d49f592 --- /dev/null +++ b/ConditionMonitoring/ts_queue.h @@ -0,0 +1,143 @@ +/* +MMO Client/Server Framework using ASIO +"Happy Birthday Mrs Javidx9!" - javidx9 + +Videos: +Part #1: https://youtu.be/2hNdkYInj4g +Part #2: https://youtu.be/UbjxGvrDrbw + +License (OLC-3) +~~~~~~~~~~~~~~~ + +Copyright 2018 - 2020 OneLoneCoder.com + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions or derivations of source code must retain the above +copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions or derivative works in binary form must reproduce +the above copyright notice. This list of conditions and the following +disclaimer must be reproduced in the documentation and/or other +materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived +from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Links +~~~~~ +YouTube: https://www.youtube.com/javidx9 +Discord: https://discord.gg/WhwHUMV +Twitter: https://www.twitter.com/javidx9 +Twitch: https://www.twitch.tv/javidx9 +GitHub: https://www.github.com/onelonecoder +Homepage: https://www.onelonecoder.com + +Author +~~~~~~ +David Barr, aka javidx9, �OneLoneCoder 2019, 2020 + +*/ + + +#pragma once +#include +#include +#include +/* +Threadsafe Queue to be accessed from different threads +*/ +template +class ts_queue +{ + public: + ts_queue() = default; + ts_queue(const ts_queue&) = delete; + virtual ~ts_queue() { clear(); } + + public: + // Returns and maintains item at front of Queue + const T& front() + { + std::scoped_lock lock(muxQueue); + return queue.front(); + } + + // Returns and maintains item at back of Queue + const T& back() + { + std::scoped_lock lock(muxQueue); + return queue.back(); + } + + // Removes and returns the first item from Queue + T pop_front() + { + std::scoped_lock lock(muxQueue); + auto t = std::move(queue.front()); + queue.pop(); + return t; + } + + // Adds an item to front of Queue + void push(const T& item) + { + std::scoped_lock lock(muxQueue); + queue.push(std::move(item)); + std::unique_lock ul(muxBlocking); + cvBlocking.notify_one(); + } + + // Returns true if Queue has no items + bool empty() + { + std::scoped_lock lock(muxQueue); + return queue.empty(); + } + + // Returns number of items in Queue + size_t size() + { + std::scoped_lock lock(muxQueue); + return queue.size(); + } + + // Clears Queue + void clear() + { + std::scoped_lock lock(muxQueue); + std::queue empty; + std::swap(queue, empty); + } + + void wait() + { + while (empty()) + { + std::unique_lock ul(muxBlocking); + cvBlocking.wait(ul); + } + } + + protected: + std::mutex muxQueue; + std::queue queue; + std::condition_variable cvBlocking; + std::mutex muxBlocking; + +};