commit 2c6c03869c3383f3cff1fb01ecee947d154dc79b Author: JDierkse Date: Fri May 1 11:24:10 2020 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..16386c4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +*.o.* +*.d.* +*.a.* +.*.swp +.AppleDouble +.debug +.gdbinit* +gdb* +core-* +*-core +core.* +*.core +DataStorageAPI* +!DataStorageAPI.cc +!DataStorageAPI.ini +DataCondenser* +!DataCondenser.cc +!DataCondenser.cpp +!DataCondenser.h +!DataCondenser.ini +DataImporter* +!DataImporter.cc +!DataImporter.cpp +!DataImporter.h +!DataImporter.ini +test* +!test.cc diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..7da2786 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Makefiles"] + path = Makefiles + url = https://gogs.dierkse.nl/JDierkse/Makefiles.git diff --git a/API/Makefile b/API/Makefile new file mode 120000 index 0000000..d0b0e8e --- /dev/null +++ b/API/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/API/WebAPI.cpp b/API/WebAPI.cpp new file mode 100644 index 0000000..9bd5383 --- /dev/null +++ b/API/WebAPI.cpp @@ -0,0 +1,227 @@ +#include "WebAPI.h" +#include "DataInterface/GraphClient.h" +#include +#include +#include +#include +#include +#include + + +namespace DataStorageInterface { +namespace API { + +// API Description + +// Log Data +// Goal: Log data into the Database +// Use: +// URL: http:///API/log//// + +// GraphHeader Query +// Goal: +// Use: +// URL: http:///API/graphheader/// + +// Graph Query +// Goal: +// Use: +// URL: http:///API/graph/ +// URL: http:///API/graph/// + +// Settings Query +// Goal: Change / query settings +// Use: +// URL: http:///API/settings// + +WebAPI::WebAPI() +{ +} + +WebAPI::~WebAPI() +{ +} + +std::string WebAPI::ProcessQuery(DataStorage::DataStorageClient* pDataStorageClient, DataInterface::GraphClient* pGraphClient, const std::string& uri, const std::vector& /*postData*/) +{ + std::string output; + + std::stringstream ss; + ss << "API Query: " << uri << std::endl; + Logging::Log(Logging::Severity::Debug, ss.str()); + + if (StringAlgorithm::istarts_with(uri, "/log/")) + output = ProcessLogQuery(pDataStorageClient, uri); + else if (StringAlgorithm::istarts_with(uri, "/graphheader/")) + output = ProcessGraphHeaderQuery(pGraphClient, uri); + else if (StringAlgorithm::istarts_with(uri, "/graph/")) + output = ProcessGraphQuery(pGraphClient, uri); + else if (StringAlgorithm::istarts_with(uri, "/settings/")) + output = ProcessSettingsQuery(uri); + else + output = "{\"result\": \"error\"}\r\n"; + + return output; +} + +std::string WebAPI::ProcessLogQuery(DataStorage::DataStorageClient* pDataStorageClient, const std::string& uri) +{ + // /log//// + + std::string output; + std::string error = "{\"result\": \"error\"}\r\n"; + + std::vector uriTokens = StringAlgorithm::split(uri, '/'); + if (uriTokens.size() < 6) + return error; + + if (uriTokens[2].empty() || + uriTokens[3].empty() || + uriTokens[4].empty()) + return error; + + int timestamp = std::stoi(uriTokens[2]); + int deviceId = std::stoi(uriTokens[3]); + int dataId = DataStorage::DataId(uriTokens[4]).Id(); + std::string value = uriTokens[5]; + + std::stringstream ss; + ss << "Log Query: " << std::endl; + ss << " " << timestamp << std::endl; + ss << " " << deviceId << std::endl; + ss << " " << dataId << std::endl; + ss << " " << value << std::endl; + Logging::Log(Logging::Severity::Debug, ss.str()); + + // TODO: Actually Log... + + output = "{\"result\": \"success\"}\r\n"; + + return output; +} + +std::string WebAPI::ProcessGraphHeaderQuery(DataInterface::GraphClient* pGraphClient, const std::string& uri) +{ + // /graphheader/// + + std::string output; + std::string error = "{\"result\": \"error\"}\r\n"; + + std::vector uriTokens = StringAlgorithm::split(uri, '/'); + + if (uriTokens.size() != 5) + return error; + + if (uriTokens[2].empty() || + uriTokens[3].empty()) + return error; + + try + { + int deviceId = stoi(uriTokens[2]); + std::vector dataIds; + + for (auto& id : StringAlgorithm::split(uriTokens[3], '-')) + dataIds.push_back(DataStorage::DataId(id).Id()); + + std::string period = uriTokens[4]; + if (uriTokens[4].empty()) + period = "day"; + + nlohmann::json json = GetGraphHeader(pGraphClient, dataIds, period); + output = json.dump(); + } + catch (const std::exception& e) + { + std::cout << "ProcessGraphHeaderQuery() - Error: " << e.what() << std::endl; + return error; + } + + return output; +} + +std::string WebAPI::ProcessGraphQuery(DataInterface::GraphClient* pGraphClient, const std::string& uri) +{ + // /graph/// + + std::string output; + std::string error = "{\"result\": \"error\"}\r\n"; + + std::vector uriTokens = StringAlgorithm::split(uri, '/'); + + if (uriTokens.size() != 5) + return error; + + if (uriTokens[2].empty() || + uriTokens[3].empty()) + return error; + + try + { + int deviceId = stoi(uriTokens[2]); + std::vector dataIds; + + for (auto& id : StringAlgorithm::split(uriTokens[3], '-')) + dataIds.push_back(DataStorage::DataId(id).Id()); + + std::string period = uriTokens[4]; + if (uriTokens[4].empty()) + period = "day"; + + nlohmann::json json = GetGraphData(pGraphClient, deviceId, dataIds, period); + output = json.dump(); + } + catch (const std::exception& e) + { + std::cout << "ProcessGraphQuery() - Error: " << e.what() << std::endl; + return error; + } + + return output; +} + +std::string WebAPI::ProcessSettingsQuery(const std::string& uri) +{ + // /settings// + + std::string success = "{\"result\": \"success\"}\r\n"; + std::string error = "{\"result\": \"error\"}\r\n"; + + std::vector uriTokens = StringAlgorithm::split(uri, '/'); + + if (uriTokens.size() < 4) + return std::string(); + + std::string setting = uriTokens[2]; + std::string value = uriTokens[3]; + + // Generalize in Settings struct? + if (StringAlgorithm::iequals(setting, "debug")) + { + if (StringAlgorithm::iequals(value, "on")) + Logging::SetLogMask(Logging::Severity::Debug); + else if (StringAlgorithm::iequals(value, "off")) + Logging::SetLogMask(Logging::Severity::Info); + else + return error; + } + else + { + return error; + } + + return success; +} + +nlohmann::json WebAPI::GetGraphHeader(DataInterface::GraphClient* pGraphClient, const std::vector& dataIds, const std::string& timespan) +{ + return pGraphClient->GetGraphHeader(dataIds, DataStorage::Conversions::Timespan(timespan)); +} + +nlohmann::json WebAPI::GetGraphData(DataInterface::GraphClient* pGraphClient, int deviceId, const std::vector& dataIds, const std::string& timespan) +{ + return pGraphClient->GetGraphData(deviceId, dataIds, DataStorage::Conversions::Timespan(timespan)); +} + +} // namespace API +} // namespace DataStorageInterface diff --git a/API/WebAPI.h b/API/WebAPI.h new file mode 100644 index 0000000..3bb5b76 --- /dev/null +++ b/API/WebAPI.h @@ -0,0 +1,49 @@ +#ifndef API_WEBAPI_H +#define API_WEBAPI_H + +#include +#include +#include +#include + + +namespace DataStorage { + +class DataStorageClient; + +} // namespace DataStorage + +namespace DataStorageInterface { + +namespace DataInterface { + +class GraphClient; + +} // namespace DataInterface + +namespace API { + +class WebAPI +{ +public: + WebAPI(); + ~WebAPI(); + + static std::string ProcessQuery(DataStorage::DataStorageClient* pDataStorageClient, DataInterface::GraphClient* pGraphClient, const std::string& uri, const std::vector& postData); + +private: + static std::string ProcessLogQuery(DataStorage::DataStorageClient* pDataStorageClient, const std::string& uri); + + static std::string ProcessGraphHeaderQuery(DataInterface::GraphClient* pGraphClient, const std::string& uri); + static std::string ProcessGraphQuery(DataInterface::GraphClient* pGraphClient, const std::string& uri); + + static std::string ProcessSettingsQuery(const std::string& uri); + + static nlohmann::json GetGraphHeader(DataInterface::GraphClient* pGraphClient, const std::vector& dataIds, const std::string& timespan); + static nlohmann::json GetGraphData(DataInterface::GraphClient* pGraphClient, int deviceId, const std::vector& dataIds, const std::string& timespan); +}; + +} // namespace API +} // namespace DataStorageInterface + +#endif // UTIL_API_WEBAPI_H diff --git a/Application/DataCondenser.cc b/Application/DataCondenser.cc new file mode 100644 index 0000000..7d2d1a0 --- /dev/null +++ b/Application/DataCondenser.cc @@ -0,0 +1,63 @@ +#include "DataInterface/DataCondenser.h" +#include +#include +#include +#include +#include +#include + + +int main(int argc, char** argv) +{ + try + { + Logging::OpenLog(); + Logging::SetLogMask(Logging::Severity::Info); + + INIReader iniReader("DataCondenser.ini"); + if (iniReader.ParseError() != 0) + throw std::runtime_error("Can't read DataCondenser.ini."); + + MySQL::MySQLClient mySQLClient; + { + std::string hostname = iniReader.Get("MySQL", "Hostname", ""); + if (hostname.empty()) + throw std::runtime_error("MySQL Hostname directive missing in ini file."); + + std::string username = iniReader.Get("MySQL", "Username", ""); + if (username.empty()) + throw std::runtime_error("MySQL Username directive missing in ini file."); + + std::string password = iniReader.Get("MySQL", "Password", ""); + if (password.empty()) + throw std::runtime_error("MySQL Password directive missing in ini file."); + + std::string database = iniReader.Get("MySQL", "Database", ""); + if (database.empty()) + throw std::runtime_error("MySQL Database directive missing in ini file."); + + mySQLClient.Connect(hostname, username, password, database); + } + + DataStorageInterface::DataInterface::DataCondenser condenser(&mySQLClient); + condenser.CondenseData(); + + Logging::CloseLog(); + } + catch (const std::exception& e) + { + std::cerr << "Exception caught" << std::endl; + + std::stringstream ss; + ss << "Type : " << typeid(e).name() << std::endl; + ss << "ERROR: " << e.what() << std::endl; + + Logging::Log(Logging::Severity::Error, ss.str()); + + Logging::CloseLog(); + + return -1; + } + + return 0; +} diff --git a/Application/DataImporter.cc b/Application/DataImporter.cc new file mode 100644 index 0000000..6a91c92 --- /dev/null +++ b/Application/DataImporter.cc @@ -0,0 +1,89 @@ +#include "DataInterface/DataImporter.h" +#include +#include +#include +#include +#include +#include +#include +#include + + +int main(int argc, char** argv) +{ + try + { + Logging::OpenLog(); + Logging::SetLogMask(Logging::Severity::Debug); + + std::string databaseFile = ""; + clipp::group cli { + clipp::value("SQLite Database file", databaseFile) + }; + + if (!clipp::parse(argc, argv, cli)) + { + std::stringstream ss; + ss << clipp::make_man_page(cli, argv[0]); + Logging::Log(Logging::Severity::Info, ss.str()); + + Logging::CloseLog(); + + return 1; + } + + { + std::ifstream f(databaseFile); + if (!f.good()) + throw std::runtime_error("Can't read SQLite Database file."); + } + + INIReader iniReader("DataImporter.ini"); + if (iniReader.ParseError() != 0) + throw std::runtime_error("Can't read DataImporter.ini."); + + MySQL::MySQLClient mySQLClient; + { + std::string hostname = iniReader.Get("MySQL", "Hostname", ""); + if (hostname.empty()) + throw std::runtime_error("MySQL Hostname directive missing in ini file."); + + std::string username = iniReader.Get("MySQL", "Username", ""); + if (username.empty()) + throw std::runtime_error("MySQL Username directive missing in ini file."); + + std::string password = iniReader.Get("MySQL", "Password", ""); + if (password.empty()) + throw std::runtime_error("MySQL Password directive missing in ini file."); + + std::string database = iniReader.Get("MySQL", "Database", ""); + if (database.empty()) + throw std::runtime_error("MySQL Database directive missing in ini file."); + + mySQLClient.Connect(hostname, username, password, database); + } + + SQLite::SQLiteClient sqliteClient("./datalog.db"); + + DataStorageInterface::DataInterface::DataImporter importer(&mySQLClient, &sqliteClient); + importer.ImportData(); + + Logging::CloseLog(); + } + catch (const std::exception& e) + { + std::cerr << "Exception caught" << std::endl; + + std::stringstream ss; + ss << "Type : " << typeid(e).name() << std::endl; + ss << "ERROR: " << e.what() << std::endl; + + Logging::Log(Logging::Severity::Error, ss.str()); + + Logging::CloseLog(); + + return -1; + } + + return 0; +} diff --git a/Application/DataStorageAPI.cc b/Application/DataStorageAPI.cc new file mode 100644 index 0000000..2dec180 --- /dev/null +++ b/Application/DataStorageAPI.cc @@ -0,0 +1,77 @@ +#include "API/WebAPI.h" +#include "DataInterface/GraphClient.h" +#include +#include +#include +#include +#include +#include + + +int main(int argc, char** argv) +{ + try + { + Logging::OpenLog(); + Logging::SetLogMask(Logging::Severity::Info); + + INIReader iniReader("DataStorageAPI.ini"); + if (iniReader.ParseError() != 0) + throw std::runtime_error("Can't read DataLogger.ini."); + + Logging::Log(Logging::Severity::Info, "Starting DataStorageAPI"); + + int port = iniReader.GetInteger("DataStorageAPI", "Port", 0); + if (port == 0) + throw std::runtime_error("Port directive missing in ini file."); + + std::string table = iniReader.Get("DataStorageAPI", "Table", ""); + if (table.empty()) + throw std::runtime_error("Table directive missing in ini file."); + + std::shared_ptr pMySQLClient = std::make_shared(); + { + std::string hostname = iniReader.Get("MySQL", "Hostname", ""); + if (hostname.empty()) + throw std::runtime_error("MySQL Hostname directive missing in ini file."); + + std::string username = iniReader.Get("MySQL", "Username", ""); + if (username.empty()) + throw std::runtime_error("MySQL Username directive missing in ini file."); + + std::string password = iniReader.Get("MySQL", "Password", ""); + if (password.empty()) + throw std::runtime_error("MySQL Password directive missing in ini file."); + + std::string database = iniReader.Get("MySQL", "Database", ""); + if (database.empty()) + throw std::runtime_error("MySQL Database directive missing in ini file."); + + pMySQLClient->Connect(hostname, username, password, database); + } + + DataStorage::DataStorageClient dataStorageClient(pMySQLClient, table); + DataStorageInterface::DataInterface::GraphClient graphClient(pMySQLClient.get()); + auto callback = std::bind(&DataStorageInterface::API::WebAPI::ProcessQuery, &dataStorageClient, &graphClient, std::placeholders::_1, std::placeholders::_2); + + Http::HttpServer server(port, callback); + Logging::Log(Logging::Severity::Info, "Startup Complete"); + server.Wait(); + + Logging::Log(Logging::Severity::Info, "Stopping DataStorageAPI..."); + + Logging::CloseLog(); + } + catch (const std::exception& e) + { + std::stringstream ss; + ss << "ERROR: " << e.what() << std::endl; + + Logging::Log(Logging::Severity::Error, ss.str()); + Logging::CloseLog(); + + return -1; + } + + return 0; +} diff --git a/Application/Makefile b/Application/Makefile new file mode 120000 index 0000000..d0b0e8e --- /dev/null +++ b/Application/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/DataCondenser.ini b/DataCondenser.ini new file mode 100644 index 0000000..fe9a62b --- /dev/null +++ b/DataCondenser.ini @@ -0,0 +1,8 @@ +[DataCondenser] + +[MySQL] +Hostname = +Username = +Password = +Database = + diff --git a/DataImporter.ini b/DataImporter.ini new file mode 100644 index 0000000..d7d6b4b --- /dev/null +++ b/DataImporter.ini @@ -0,0 +1,8 @@ +[DataImporter] + +[MySQL] +Hostname = +Username = +Password = +Database = + diff --git a/DataInterface/DataCondenser.cpp b/DataInterface/DataCondenser.cpp new file mode 100644 index 0000000..6ec4828 --- /dev/null +++ b/DataInterface/DataCondenser.cpp @@ -0,0 +1,406 @@ +#include "DataCondenser.h" +#include "Util/Util.h" +#include +#include +#include +#include +#include + + +namespace DataStorageInterface { +namespace DataInterface { + +DataCondenser::DataCondenser(MySQL::MySQLClient* pMySQLClient) : + m_pMySQLClient(pMySQLClient) +{ +} + +void DataCondenser::CondenseData() +{ + auto deviceIDs = GetDeviceIDs(); + + for (int dataId = 0; dataId < 3; ++dataId) + { + std::cout << "DataID: " << dataId << std::endl; + + //std::cout << "Daily" << std::endl; + //for (auto& deviceId : deviceIDs) + // WriteReducedData(deviceId, dataId, RRA::Daily); + std::cout << "Weekly" << std::endl; + for (auto& deviceId : deviceIDs) + WriteReducedData(deviceId, dataId, RRA::Weekly); + std::cout << "Monthly" << std::endl; + for (auto& deviceId : deviceIDs) + WriteReducedData(deviceId, dataId, RRA::Monthly); + std::cout << "Yearly" << std::endl; + for (auto& deviceId : deviceIDs) + WriteReducedData(deviceId, dataId, RRA::Yearly); + } + + CleanData(); +} + +int DataCondenser::GetPeriodStamp(int timestamp, const RRA::type& rra) +{ + int modulo = timestamp % static_cast(rra); + return timestamp - modulo; +} + +std::vector DataCondenser::GetDeviceIDs() +{ + std::stringstream query; + query << "SELECT DISTINCT `device_id` FROM `datalog` ORDER BY `device_id`;"; + auto resultSet = m_pMySQLClient->ExecuteQuery(query.str()); + + std::vector returnValue; + while (resultSet.Next()) + returnValue.push_back(resultSet.Int("device_id")); + + return returnValue; +} + +int DataCondenser::GetNewestPeriodStamp(const std::string& sourceTable, int deviceId, int dataId) +{ + std::stringstream query; + query << "SELECT MAX(UNIX_TIMESTAMP(`timestamp`)) AS timestamp FROM `" << sourceTable << "` WHERE `device_id` = '" << deviceId <<"' AND `data_id` = '" << dataId << "';"; + auto resultSet = m_pMySQLClient->ExecuteQuery(query.str()); + if (resultSet.Next()) + return resultSet.Int("timestamp"); + + return 0; +} + +MySQL::MySQLResultSet DataCondenser::GetRawData(const std::string& sourceTable, int deviceId, int dataId, int newestPeriodStamp) +{ + std::stringstream query; + query << "SELECT UNIX_TIMESTAMP(`timestamp`) AS timestamp, `value` AS `min-value`, `value` AS `mean-value`, `value` AS `max-value` FROM `" << sourceTable << "` WHERE `device_id` = '" << deviceId <<"' AND `data_id` = '" << dataId << "' "; + if (newestPeriodStamp == -1) + query << "LIMIT 1;"; + else + query << "AND `timestamp` > FROM_UNIXTIME(" << newestPeriodStamp << ") ORDER BY `timestamp` ASC;"; + + return m_pMySQLClient->ExecuteQuery(query.str()); +} + +MySQL::MySQLResultSet DataCondenser::GetReducedData(const std::string& sourceTable, int deviceId, int dataId, int newestPeriodStamp) +{ + std::stringstream query; + query << "SELECT UNIX_TIMESTAMP(`timestamp`) AS timestamp, `min-value`, `mean-value`, `max-value` FROM `" << sourceTable << "` WHERE `device_id` = '" << deviceId <<"' AND `data_id` = '" << dataId << "' "; + if (newestPeriodStamp == -1) + query << "LIMIT 1;"; + else + query << "AND `timestamp` > FROM_UNIXTIME(" << newestPeriodStamp << ") ORDER BY `timestamp` ASC;"; + + return m_pMySQLClient->ExecuteQuery(query.str()); +} + +int DataCondenser::GetMaximumMySQLPacketSize() +{ + std::stringstream query; + query << "show variables like 'max_allowed_packet';"; + MySQL::MySQLResultSet resultSet = m_pMySQLClient->ExecuteQuery(query.str()); + + if (resultSet.RowsCount() != 1) + throw std::runtime_error("Unexpected reply when retrieving max_allowed_packet."); + + resultSet.Next(); + return resultSet.Int("Value"); +} + +DataType::type DataCondenser::GetDataType(int deviceId, int dataId) +{ + auto resultSet = GetRawData("datalog", deviceId, dataId, -1); + if (resultSet.Next()) + return DataInterface::GetDataType(resultSet.String("mean-value")); + + return DataType::None; +} + +std::string DataCondenser::GetSourceTable(const RRA::type& rra) +{ + switch (rra) + { + case RRA::Daily: + return "datalog"; + case RRA::Weekly: + return "datalog"; + case RRA::Monthly: + return "datalog-weekly"; + case RRA::Yearly: + return "datalog-monthly"; + default: + case RRA::Raw: + return std::string(); + } +} + +std::string DataCondenser::GetDestinationTable(const RRA::type& rra) +{ + switch (rra) + { + case RRA::Daily: + return "datalog-daily"; + case RRA::Weekly: + return "datalog-weekly"; + case RRA::Monthly: + return "datalog-monthly"; + case RRA::Yearly: + return "datalog-yearly"; + default: + case RRA::Raw: + return std::string(); + } +} + +int DataCondenser::StringStreamSize(std::stringstream& ss) +{ + ss.seekp(0, std::ios_base::end); + return ss.tellp(); +} + +void DataCondenser::InitializeInsertQuery(std::stringstream& query, const std::string& table) +{ + query.str(""); + query << "INSERT INTO `" << table << "` (`device_id`, `data_id`, `timestamp`, `min-value`, `mean-value`, `max-value`) VALUES "; +} + +void DataCondenser::ExecuteInsertQuery(std::stringstream& query) +{ + query.seekp(-1, std::ios_base::end); + query << ";"; + m_pMySQLClient->Execute(query.str()); +} + +void DataCondenser::WriteReducedData(int deviceId, int dataId, const RRA::type& rra) +{ + DataType::type dataType = GetDataType(deviceId, dataId); + + if (dataType == DataType::None) + std::cout << " DataType: None" << std::endl; + else if (dataType == DataType::Float) + std::cout << " DataType: Float" << std::endl; + else if (dataType == DataType::String) + std::cout << " DataType: String" << std::endl; + + switch (dataType) + { + case DataType::Float: + WriteReducedFloatData(deviceId, dataId, rra); + break; + case DataType::String: + WriteReducedStringData(deviceId, dataId, rra); + break; + default: + case DataType::None: + break; + } +} + +void DataCondenser::WriteReducedFloatData(int deviceId, int dataId, const RRA::type& rra) +{ + int maxMySQLPacketSize = GetMaximumMySQLPacketSize() - 2; + + std::string sourceTable = GetSourceTable(rra); + std::string destinationTable = GetDestinationTable(rra); + + int newestSourcePeriodStamp = GetNewestPeriodStamp(sourceTable, deviceId, dataId); + int newestDestinationPeriodStamp = GetNewestPeriodStamp(destinationTable, deviceId, dataId); + + std::cout << " DeviceID: " << deviceId << std::endl; + std::cout << " DataID: " << dataId << std::endl; + std::cout << " Newest Src : " << newestSourcePeriodStamp << std::endl; + std::cout << " Newest Dest: " << newestDestinationPeriodStamp << std::endl; + + std::stringstream query; + MySQL::MySQLResultSet resultSet; + if (sourceTable == "datalog") + resultSet = GetRawData(sourceTable, deviceId, dataId, newestDestinationPeriodStamp); + else + resultSet = GetReducedData(sourceTable, deviceId, dataId, newestDestinationPeriodStamp); + + std::cout << " Source Count: " << resultSet.RowsCount() << std::endl; + + int samples = 0; + float min = std::numeric_limits::max(); + float mean = 0; + float max = std::numeric_limits::min(); + + int writeCount = 0; + + InitializeInsertQuery(query, destinationTable); + + std::stringstream queryPart; + + int periodStamp = 0; + while (resultSet.Next()) + { + int currentPeriodStamp = GetPeriodStamp(resultSet.Int("timestamp"), rra); + if (currentPeriodStamp != periodStamp) + { + if (samples > 0) + { + mean = mean / samples; + + queryPart.str(""); + queryPart << "('" << deviceId << "', '" << dataId << "', FROM_UNIXTIME(" << currentPeriodStamp << "), '" << min << "', '" << mean << "', '" << max << "'),"; + ++writeCount; + + int packetSize = StringStreamSize(query) + StringStreamSize(queryPart); + if (packetSize > maxMySQLPacketSize) + { + ExecuteInsertQuery(query); + InitializeInsertQuery(query, destinationTable); + std::cout << " Write Count: " << writeCount << std::endl; + writeCount = 0; + } + + query << queryPart.str(); + } + + if (currentPeriodStamp > newestSourcePeriodStamp) + break; + + samples = 0; + min = std::numeric_limits::max(); + mean = 0; + max = std::numeric_limits::min(); + periodStamp = currentPeriodStamp; + } + + float minValue, meanValue, maxValue; + minValue = static_cast(resultSet.Double("min-value")); + meanValue = static_cast(resultSet.Double("mean-value")); + maxValue = static_cast(resultSet.Double("max-value")); + + if (minValue < min) + min = minValue; + if (maxValue > max) + max = maxValue; + mean += meanValue; + ++samples; + } + + if (writeCount > 0) + ExecuteInsertQuery(query); + + std::cout << " Write Count: " << writeCount << std::endl << std::endl; +} + +void DataCondenser::WriteReducedStringData(int deviceId, int dataId, const RRA::type& rra) +{ + int maxMySQLPacketSize = GetMaximumMySQLPacketSize() - 2; + + std::string sourceTable = GetSourceTable(rra); + std::string destinationTable = GetDestinationTable(rra); + + int newestSourcePeriodStamp = GetNewestPeriodStamp(sourceTable, deviceId, dataId); + int newestDestinationPeriodStamp = GetNewestPeriodStamp(destinationTable, deviceId, dataId); + + std::cout << " DeviceID: " << deviceId << std::endl; + std::cout << " DataID: " << dataId << std::endl; + std::cout << " Newest Src : " << newestSourcePeriodStamp << std::endl; + std::cout << " Newest Dest: " << newestDestinationPeriodStamp << std::endl; + + std::stringstream query; + MySQL::MySQLResultSet resultSet; + if (sourceTable == "datalog") + resultSet = GetRawData(sourceTable, deviceId, dataId, newestDestinationPeriodStamp); + else + resultSet = GetReducedData(sourceTable, deviceId, dataId, newestDestinationPeriodStamp); + + std::cout << " Source Count: " << resultSet.RowsCount() << std::endl; + + int samples = 0; + std::vector values; + + int writeCount = 0; + + InitializeInsertQuery(query, destinationTable); + + std::stringstream queryPart; + + int periodStamp = 0; + while (resultSet.Next()) + { + int currentPeriodStamp = GetPeriodStamp(resultSet.Int("timestamp"), rra); + if (currentPeriodStamp != periodStamp) + { + if (samples > 0) + { + std::map map; + for (auto& value : values) + { + auto iterator = map.find(value); + if (iterator == map.end()) + map.insert(std::pair(value, 1)); + else + map[value] += 1; + } + + auto iterator = map.begin(); + for (std::map::iterator it = map.begin(); it != map.end(); ++it) + { + if (it->second > iterator->second) + iterator = it; + } + + queryPart.str(""); + queryPart << "('" << deviceId << "', '" << dataId << "', FROM_UNIXTIME(" << currentPeriodStamp << "), '', '" << iterator->first << "', ''),"; + + int packetSize = StringStreamSize(query) + StringStreamSize(queryPart); + if (packetSize > maxMySQLPacketSize) + { + ExecuteInsertQuery(query); + InitializeInsertQuery(query, destinationTable); + std::cout << " Write Count: " << writeCount << std::endl; + writeCount = 0; + } + + query << queryPart.str(); + ++writeCount; + } + + if (currentPeriodStamp > newestSourcePeriodStamp) + break; + + samples = 0; + values.clear(); + periodStamp = currentPeriodStamp; + } + + std::string meanValue = resultSet.String("mean-value"); + values.push_back(meanValue); + ++samples; + } + + if (writeCount > 0) + ExecuteInsertQuery(query); + + std::cout << " Write Count: " << writeCount << std::endl << std::endl; +} + +void DataCondenser::CleanData() +{ + std::cout << "Cleaning: " << std::endl; + + CleanData(RRA::Daily); + CleanData(RRA::Weekly); + CleanData(RRA::Monthly); + CleanData(RRA::Yearly); +} + +void DataCondenser::CleanData(const RRA::type& rra) +{ + std::string table = GetDestinationTable(rra); + int periodInSeconds = static_cast(rra) * 400; + int timestamp = Util::GetTimestamp() + Util::GetUTCOffset() - periodInSeconds; + + std::cout << " Cleaning " << table << " (<" << timestamp << ")" << std::endl; + + std::stringstream query; + query << "DELETE FROM `" << table << "` WHERE `timestamp` < FROM_UNIXTIME(" << timestamp << ");"; + m_pMySQLClient->Execute(query.str()); +} + +} // namespace DataInterface +} // namespace DataStorageInterface diff --git a/DataInterface/DataCondenser.h b/DataInterface/DataCondenser.h new file mode 100644 index 0000000..bfddcbb --- /dev/null +++ b/DataInterface/DataCondenser.h @@ -0,0 +1,52 @@ +#ifndef DATAINTERFACE_DATACONDENSER_H +#define DATAINTERFACE_DATACONDENSER_H + +#include "DataType.h" +#include "RRA.h" +#include +#include +#include + + +namespace DataStorageInterface { +namespace DataInterface { + +class DataCondenser +{ +public: + DataCondenser(MySQL::MySQLClient* pMySQLClient); + + void CondenseData(); + +private: + int GetPeriodStamp(int timestamp, const RRA::type& rra); + std::vector GetDeviceIDs(); + int GetNewestPeriodStamp(const std::string& sourceTable, int deviceId, int dataId); + MySQL::MySQLResultSet GetRawData(const std::string& sourceTable, int deviceId, int dataId, int newestPeriodStamp); + MySQL::MySQLResultSet GetReducedData(const std::string& sourceTable, int deviceId, int dataId, int newestPeriodStamp); + + int GetMaximumMySQLPacketSize(); + + DataType::type GetDataType(int deviceId, int dataId); + std::string GetSourceTable(const RRA::type& rra); + std::string GetDestinationTable(const RRA::type& rra); + int StringStreamSize(std::stringstream& ss); + + void InitializeInsertQuery(std::stringstream& query, const std::string& table); + void ExecuteInsertQuery(std::stringstream& query); + + void WriteReducedFloatData(int deviceId, int dataId, const RRA::type& rra); + void WriteReducedStringData(int deviceId, int dataId, const RRA::type& rra); + void WriteReducedData(int deviceId, int dataId, const RRA::type& rra); + + void CleanData(); + void CleanData(const RRA::type& rra); + +private: + MySQL::MySQLClient* m_pMySQLClient; +}; + +} // namespace DataInterface +} // namespace DataStorageInterface + +#endif // DATAINTERFACE_DATACONDENSER_H diff --git a/DataInterface/DataImporter.cpp b/DataInterface/DataImporter.cpp new file mode 100644 index 0000000..fd0bd9b --- /dev/null +++ b/DataInterface/DataImporter.cpp @@ -0,0 +1,139 @@ +#include "DataImporter.h" +#include +#include +#include +#include +#include +#include + + +namespace DataStorageInterface { +namespace DataInterface { + +DataImporter::DataImporter(MySQL::MySQLClient* pMySQLClient, SQLite::SQLiteClient* pSQLiteClient) : + m_pMySQLClient(pMySQLClient), + m_pSQLiteClient(pSQLiteClient) +{ +} + +void DataImporter::ImportData() +{ + auto tables = GetTables(); + for (auto& table : tables) + { + auto devices = GetDevices(table); + for (auto& device : devices) + ImportData(table, device); + } +} + +std::vector DataImporter::GetTables() +{ + std::stringstream query; + query << "SELECT name FROM sqlite_master WHERE type ='table' AND name NOT LIKE 'sqlite_%';"; + auto resultSet = m_pSQLiteClient->ExecuteQuery(query.str()); + + std::vector tables; + while (resultSet.Next()) + tables.push_back(resultSet.String("name")); + + return tables; +} + +std::vector DataImporter::GetDevices(const std::string& table) +{ + std::stringstream query; + query << "SELECT DISTINCT device_id FROM " << table << ";"; + auto resultSet = m_pSQLiteClient->ExecuteQuery(query.str()); + + std::vector devices; + while (resultSet.Next()) + devices.push_back(resultSet.Int("device_id")); + + return devices; +} + +void DataImporter::ImportData(const std::string& table, int device) +{ + std::stringstream ss; + ss << "Table: " << table << " #" << device; + Logging::Log(Logging::Severity::Info, ss.str()); + + int maxMySQLPacketSize = GetMaximumMySQLPacketSize() - 2; + int maxTimestamp = GetMaximumTimestamp(table, device); + int dataId = DataStorage::DataId(table).Id(); + + std::stringstream queryPart; + + std::stringstream query; + query << "SELECT timestamp, value FROM " << table << " WHERE device_id = '" << device << "' AND timestamp > '" << maxTimestamp << "' ORDER BY timestamp ASC;"; + auto resultSet = m_pSQLiteClient->ExecuteQuery(query.str()); + + int writeCount = 0; + InitializeInsertQuery(query); + while (resultSet.Next()) + { + queryPart.str(""); + queryPart << "('" << device << "', '" << dataId << "', FROM_UNIXTIME(" << resultSet.Int("timestamp") << "), '" << resultSet.String("value") << "'),"; + ++writeCount; + + int packetSize = StringStreamSize(query) + StringStreamSize(queryPart); + if (packetSize > maxMySQLPacketSize) + { + ExecuteInsertQuery(query); + writeCount = 0; + InitializeInsertQuery(query); + } + + query << queryPart.str(); + } + + if (writeCount > 0) + ExecuteInsertQuery(query); +} + +int DataImporter::GetMaximumMySQLPacketSize() +{ + std::stringstream query; + query << "show variables like 'max_allowed_packet';"; + MySQL::MySQLResultSet resultSet = m_pMySQLClient->ExecuteQuery(query.str()); + + if (resultSet.RowsCount() != 1) + throw std::runtime_error("Unexpected reply when retrieving max_allowed_packet."); + + resultSet.Next(); + return resultSet.Int("Value"); +} + +int DataImporter::StringStreamSize(std::stringstream& ss) +{ + ss.seekp(0, std::ios_base::end); + return ss.tellp(); +} + +void DataImporter::InitializeInsertQuery(std::stringstream& query) +{ + query.str(""); + query << "INSERT INTO `datalog` (`device_id`, `data_id`, `timestamp`, `value`) VALUES "; +} + +void DataImporter::ExecuteInsertQuery(std::stringstream& query) +{ + query.seekp(-1, std::ios_base::end); + query << ";"; + m_pMySQLClient->Execute(query.str()); +} + +int DataImporter::GetMaximumTimestamp(const std::string& table, int device) +{ + std::stringstream query; + query << "SELECT MAX(UNIX_TIMESTAMP(`timestamp`)) AS timestamp FROM `datalog` WHERE `device_id` = '" << device <<"' AND `data_id` = '" << DataStorage::DataId(table).Id() << "';"; + auto resultSet = m_pMySQLClient->ExecuteQuery(query.str()); + if (resultSet.Next()) + return resultSet.Int("timestamp"); + + return 0; +} + +} // namespace DataInterface +} // namespace DataStorageInterface diff --git a/DataInterface/DataImporter.h b/DataInterface/DataImporter.h new file mode 100644 index 0000000..4bb6f56 --- /dev/null +++ b/DataInterface/DataImporter.h @@ -0,0 +1,49 @@ +#ifndef DATAINTERFACE_DATAIMPORTER_H +#define DATAINTERFACE_DATAIMPORTER_H + +#include +#include + + +namespace MySQL { + +class MySQLClient; + +} // namespace MySQL + +namespace SQLite { + +class SQLiteClient; + +} // namespace SQLite + +namespace DataStorageInterface { +namespace DataInterface { + +class DataImporter +{ +public: + DataImporter(MySQL::MySQLClient* pMySQLClient, SQLite::SQLiteClient* pSQLiteClient); + + void ImportData(); + +private: + std::vector GetTables(); + std::vector GetDevices(const std::string& table); + void ImportData(const std::string& table, int device); + + int GetMaximumMySQLPacketSize(); + int StringStreamSize(std::stringstream& ss); + void InitializeInsertQuery(std::stringstream& query); + void ExecuteInsertQuery(std::stringstream& query); + int GetMaximumTimestamp(const std::string& table, int device); + +private: + MySQL::MySQLClient* m_pMySQLClient; + SQLite::SQLiteClient* m_pSQLiteClient; +}; + +} // namespace DataInterface +} // namespace DataStorageInterface + +#endif // DATAINTERFACE_DATAIMPORTER_H diff --git a/DataInterface/DataType.cpp b/DataInterface/DataType.cpp new file mode 100644 index 0000000..7e6b984 --- /dev/null +++ b/DataInterface/DataType.cpp @@ -0,0 +1,45 @@ +#include "DataType.h" +#include + + +namespace DataStorageInterface { +namespace DataInterface { + +DataType::type GetDataType(const std::string& value) +{ + if (StringAlgorithm::is_numeric(value)) + return DataType::Float; + + return DataType::String; +} + +namespace Conversions { + +DataType::type DataType(const std::string& datatype) +{ + if (StringAlgorithm::iequals(datatype, "Float")) + return DataType::Float; + else if (StringAlgorithm::iequals(datatype, "String")) + return DataType::String; + else + return DataType::None; +} + +std::string DataType(DataType::type datatype) +{ + switch (datatype) + { + case DataType::None: + return "None"; + case DataType::Float: + return "Float"; + default: + case DataType::String: + return "String"; + } +} + +} // namespace Conversions + +} // namespace DataInterface +} // namespace DataStorageInterface diff --git a/DataInterface/DataType.h b/DataInterface/DataType.h new file mode 100644 index 0000000..bcb79b4 --- /dev/null +++ b/DataInterface/DataType.h @@ -0,0 +1,33 @@ +#ifndef DATAINTERFACE_DATATYPE_H +#define DATAINTERFACE_DATATYPE_H + +#include + + +namespace DataStorageInterface { +namespace DataInterface { + +class DataType +{ +public: + enum type + { + None, + Float, + String + }; +}; + +DataType::type GetDataType(const std::string& value); + +namespace Conversions { + +DataType::type DataType(const std::string& datatype); +std::string DataType(DataType::type datatype); + +} // namespace Conversions + +} // namespace DataInterface +} // namespace DataStorageInterface + +#endif // DATAINTERFACE_DATATYPE_H diff --git a/DataInterface/GraphClient.cpp b/DataInterface/GraphClient.cpp new file mode 100644 index 0000000..15c3dc1 --- /dev/null +++ b/DataInterface/GraphClient.cpp @@ -0,0 +1,117 @@ +#include "GraphClient.h" +#include "Util/Util.h" +#include +#include +#include + + +namespace DataStorageInterface { +namespace DataInterface { + +GraphClient::GraphClient(MySQL::MySQLClient* pMySQLClient) : + m_pMySQLClient(pMySQLClient) +{ +} + +nlohmann::json GraphClient::GetGraphHeader(const std::vector& dataIds, DataStorage::Timespan::type timespan) +{ + int currentTimestamp = Util::GetTimestamp() + Util::GetUTCOffset(); + return GetGraphHeader(timespan, currentTimestamp); +} + +nlohmann::json GraphClient::GetGraphData(int deviceId, const std::vector& dataIds, DataStorage::Timespan::type timespan) +{ + int currentTimestamp = Util::GetTimestamp() + Util::GetUTCOffset(); + nlohmann::json json = GetGraphHeader(timespan, currentTimestamp); + + std::string table; + switch (timespan) + { + case DataStorage::Timespan::Day: + table = "datalog"; // "datalog-daily" + break; + case DataStorage::Timespan::Week: + table = "datalog-weekly"; + break; + case DataStorage::Timespan::Month: + table = "datalog-monthly"; + break; + case DataStorage::Timespan::Year: + table = "datalog-yearly"; + break; + default: + case DataStorage::Timespan::Unknown: + return nlohmann::json(); + } + + nlohmann::json rootData = nlohmann::json::array(); + std::stringstream query; + + int startTimestamp = Util::GetTimestamp() - timespan; + for (auto dataId : dataIds) + { + nlohmann::json data; + nlohmann::json dataArray; + query.str(""); + + if (StringAlgorithm::iequals(table, "datalog")) + query << "SELECT UNIX_TIMESTAMP(`d`.`timestamp`) AS timestamp, `d`.`value` AS 'min-value', `d`.`value` AS 'mean-value', `d`.`value` AS 'max-value' "; + else + query << "SELECT UNIX_TIMESTAMP(`d`.`timestamp`) AS timestamp, `d`.`min-value`, `d`.`mean-value`, `d`.`max-value` "; + + query << "FROM `" << table << "` AS `d` "; + query << "WHERE `d`.`device_id` = '" << deviceId << "' "; + query << "AND `d`.`data_id` = '" << dataId << "' "; + query << "AND UNIX_TIMESTAMP(`d`.`timestamp`) > '" << startTimestamp << "';"; + + auto resultSet = m_pMySQLClient->ExecuteQuery(query.str()); + + int offset = Util::GetUTCOffset(); + int timestamp; + double minValue, meanValue, maxValue; + if (resultSet.RowsCount() > 0) + { + while (resultSet.Next()) + { + timestamp = resultSet.Int("timestamp") + offset; + minValue = resultSet.Double("min-value"); + meanValue = resultSet.Double("mean-value"); + maxValue = resultSet.Double("max-value"); + + nlohmann::json entry = nlohmann::json::array(); + entry.push_back(timestamp); + + std::stringstream entryValue; + if (StringAlgorithm::iequals(table, "datalog")) + entryValue << std::fixed << meanValue; + else + entryValue << std::fixed << minValue << ", " << std::fixed << maxValue; + entry.push_back(entryValue.str()); + + dataArray.push_back(entry); + } + + data["data"] = dataArray; + rootData.push_back(data); + } + } + + json["data"] = rootData; + + return json; +} + +nlohmann::json GraphClient::GetGraphHeader(DataStorage::Timespan::type timespan, std::time_t currentTimestamp) +{ + int startTimestamp = currentTimestamp - timespan; + + nlohmann::json json; + json["timespan"] = DataStorage::Conversions::Timespan(timespan); + json["start"] = startTimestamp; + json["end"] = currentTimestamp; + + return json; +} + +} // namespace DataInterface +} // namespace DataStorageInterface diff --git a/DataInterface/GraphClient.h b/DataInterface/GraphClient.h new file mode 100644 index 0000000..c66fd48 --- /dev/null +++ b/DataInterface/GraphClient.h @@ -0,0 +1,37 @@ +#ifndef DATAINTERFACE_GRAPHCLIENT_H +#define DATAINTERFACE_GRAPHCLIENT_H + +#include +#include +#include +#include + + +namespace MySQL { + +class MySQLClient; + +} // namespace MySQL + +namespace DataStorageInterface { +namespace DataInterface { + +class GraphClient +{ +public: + GraphClient(MySQL::MySQLClient* pMySQLClient); + + nlohmann::json GetGraphHeader(const std::vector& dataIds, DataStorage::Timespan::type timespan); + nlohmann::json GetGraphData(int deviceId, const std::vector& dataIds, DataStorage::Timespan::type timespan); + +private: + nlohmann::json GetGraphHeader(DataStorage::Timespan::type timespan, std::time_t currentTimestamp); + +private: + MySQL::MySQLClient* m_pMySQLClient; +}; + +} // namespace DataInterface +} // namespace DataStorageInterface + +#endif // DATAINTERFACE_GRAPHCLIENT_H diff --git a/DataInterface/Makefile b/DataInterface/Makefile new file mode 120000 index 0000000..d0b0e8e --- /dev/null +++ b/DataInterface/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/DataInterface/RRA.h b/DataInterface/RRA.h new file mode 100644 index 0000000..2cc2078 --- /dev/null +++ b/DataInterface/RRA.h @@ -0,0 +1,24 @@ +#ifndef DATAINTERFACE_RRA_H +#define DATAINTERFACE_RRA_H + + +namespace DataStorageInterface { +namespace DataInterface { + +class RRA +{ +public: + enum type + { + Raw = 1, + Daily = 300, + Weekly = 1800, + Monthly = 7200, + Yearly = 86400 + }; +}; + +} // namespace DataInterface +} // namespace DataStorageInterface + +#endif // DATAINTERFACE_RRA_H diff --git a/DataStorageAPI.ini b/DataStorageAPI.ini new file mode 100644 index 0000000..29cb2ad --- /dev/null +++ b/DataStorageAPI.ini @@ -0,0 +1,9 @@ +[DataStorageAPI] +Port = +Table = + +[MySQL] +Hostname = +Username = +Password = +Database = diff --git a/Libraries/DataStorage b/Libraries/DataStorage new file mode 120000 index 0000000..5af3ab5 --- /dev/null +++ b/Libraries/DataStorage @@ -0,0 +1 @@ +../../Libraries/DataStorage \ No newline at end of file diff --git a/Libraries/Http b/Libraries/Http new file mode 120000 index 0000000..5fe3319 --- /dev/null +++ b/Libraries/Http @@ -0,0 +1 @@ +../../Libraries/Http \ No newline at end of file diff --git a/Libraries/Logging b/Libraries/Logging new file mode 120000 index 0000000..2b4f659 --- /dev/null +++ b/Libraries/Logging @@ -0,0 +1 @@ +../../Libraries/Logging \ No newline at end of file diff --git a/Libraries/MySQL b/Libraries/MySQL new file mode 120000 index 0000000..5a4e864 --- /dev/null +++ b/Libraries/MySQL @@ -0,0 +1 @@ +../../Libraries/MySQL \ No newline at end of file diff --git a/Libraries/SQLite b/Libraries/SQLite new file mode 120000 index 0000000..5e0c157 --- /dev/null +++ b/Libraries/SQLite @@ -0,0 +1 @@ +../../Libraries/SQLite \ No newline at end of file diff --git a/Libraries/Utilities b/Libraries/Utilities new file mode 120000 index 0000000..88f16bc --- /dev/null +++ b/Libraries/Utilities @@ -0,0 +1 @@ +../../Libraries/Utilities \ No newline at end of file diff --git a/Libraries/clipp b/Libraries/clipp new file mode 120000 index 0000000..ffe3902 --- /dev/null +++ b/Libraries/clipp @@ -0,0 +1 @@ +../../Libraries/clipp \ No newline at end of file diff --git a/Libraries/inih b/Libraries/inih new file mode 120000 index 0000000..f48979f --- /dev/null +++ b/Libraries/inih @@ -0,0 +1 @@ +../../Libraries/inih \ No newline at end of file diff --git a/Libraries/json b/Libraries/json new file mode 120000 index 0000000..e497e2b --- /dev/null +++ b/Libraries/json @@ -0,0 +1 @@ +../../Libraries/json \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 120000 index 0000000..6aa5187 --- /dev/null +++ b/Makefile @@ -0,0 +1 @@ +Makefiles/Makefile \ No newline at end of file diff --git a/Makefile.conf b/Makefile.conf new file mode 100644 index 0000000..375adef --- /dev/null +++ b/Makefile.conf @@ -0,0 +1,35 @@ +# +# Makefile.conf +# + +LFLAGS += -lMySQL -lmysqlclient -lmysqlcppconn +LFLAGS += -L$(ROOTPATH)/Libraries/MySQL/lib/$(ARCH) +CFLAGS += -I$(ROOTPATH)/Libraries/MySQL/include + +LFLAGS += -lSQLite -lsqlite3 +LFLAGS += -L$(ROOTPATH)/Libraries/SQLite/lib/$(ARCH) +CFLAGS += -I$(ROOTPATH)/Libraries/SQLite/include + +LFLAGS += -lDataStorage +LFLAGS += -L$(ROOTPATH)/Libraries/DataStorage/lib/$(ARCH) +CFLAGS += -I$(ROOTPATH)/Libraries/DataStorage/include + +LFLAGS += -lHttp +LFLAGS += -L$(ROOTPATH)/Libraries/Http/lib/$(ARCH) +CFLAGS += -I$(ROOTPATH)/Libraries/Http/include + +LFLAGS += -lLogging +LFLAGS += -L$(ROOTPATH)/Libraries/Logging/lib/$(ARCH) +CFLAGS += -I$(ROOTPATH)/Libraries/Logging/include + +LFLAGS += -lUtilities +LFLAGS += -L$(ROOTPATH)/Libraries/Utilities/lib/$(ARCH) +CFLAGS += -I$(ROOTPATH)/Libraries/Utilities/include + +CFLAGS += -I$(ROOTPATH)/Libraries/clipp/include +CFLAGS += -I$(ROOTPATH)/Libraries/inih +CFLAGS += -I$(ROOTPATH)/Libraries/json/include/nlohmann -I$(ROOTPATH)/Libraries/json/include + +LFLAGS += -pthread -lm +CFLAGS += -I$(ROOTPATH) -I$(ROOTPATH)/Libraries +DEBUGDIR := .debug diff --git a/Makefile.target b/Makefile.target new file mode 100644 index 0000000..0b230ac --- /dev/null +++ b/Makefile.target @@ -0,0 +1,25 @@ +# +# Makefile.target +# + +DataStorageAPI.$(ARCH): $(OBJECTS) Application/DataStorageAPI.o.$(ARCH) + $(call build_target_arch,$@,$^) + +DataCondenser.$(ARCH): $(OBJECTS) Application/DataCondenser.o.$(ARCH) + $(call build_target_arch,$@,$^) + +DataImporter.$(ARCH): $(OBJECTS) Application/DataImporter.o.$(ARCH) + $(call build_target_arch,$@,$^) + +DataStorageAPI: + $(call build_target,$@) + +DataCondenser: + $(call build_target,$@) + +DataImporter: + $(call build_target,$@) + +.DEFAULT_GOAL := DataStorageAPI + +TARGETS += DataStorageAPI DataCondenser DataImporter diff --git a/Makefiles b/Makefiles new file mode 160000 index 0000000..5e11355 --- /dev/null +++ b/Makefiles @@ -0,0 +1 @@ +Subproject commit 5e113555ebd4e049b287b3c94c4ef33e151ea4e6 diff --git a/Util/Makefile b/Util/Makefile new file mode 120000 index 0000000..d0b0e8e --- /dev/null +++ b/Util/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/Util/Util.cpp b/Util/Util.cpp new file mode 100644 index 0000000..533b2bc --- /dev/null +++ b/Util/Util.cpp @@ -0,0 +1,23 @@ +#include "Util.h" +#include +#include + + +namespace DataStorageInterface { +namespace Util { + +int GetTimestamp() +{ + return std::time(nullptr); +} + +int GetUTCOffset() +{ + std::time_t current_time; + std::time(¤t_time); + struct std::tm *timeinfo = std::localtime(¤t_time); + return timeinfo->tm_gmtoff; +} + +} // namespace Util +} // namespace DataStorageInterface diff --git a/Util/Util.h b/Util/Util.h new file mode 100644 index 0000000..c4e5e21 --- /dev/null +++ b/Util/Util.h @@ -0,0 +1,16 @@ +#ifndef UTIL_UTIL_H +#define UTIL_UTIL_H + +#include + + +namespace DataStorageInterface { +namespace Util { + +int GetTimestamp(); +int GetUTCOffset(); + +} // namespace Util +} // namespace DataStorageInterface + +#endif // UTIL_UTIL_H