From daea5f93cce848fc81d5a746c2f8977377a4546d Mon Sep 17 00:00:00 2001 From: "de@itstall.de" <de@itstall.de> Date: Mon, 27 Jan 2020 14:37:14 +0100 Subject: [PATCH] Initial commit --- .gitignore | 5 + backend.cpp | 58 ++++ header/dbSqlite.h | 94 ++++++ header/influxdb.h | 320 ++++++++++++++++++++ header/json.h | 646 ++++++++++++++++++++++++++++++++++++++++ header/openweathermap.h | 196 ++++++++++++ 6 files changed, 1319 insertions(+) create mode 100644 .gitignore create mode 100644 backend.cpp create mode 100644 header/dbSqlite.h create mode 100644 header/influxdb.h create mode 100644 header/json.h create mode 100644 header/openweathermap.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2c31b73 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +################################################################################ +# Diese .gitignore-Datei wurde von Microsoft(R) Visual Studio automatisch erstellt. +################################################################################ + +/.vs diff --git a/backend.cpp b/backend.cpp new file mode 100644 index 0000000..fbb0aa9 --- /dev/null +++ b/backend.cpp @@ -0,0 +1,58 @@ +#include <iostream> +#include <thread> +#include "openweathermap.h" +#include "dbSqlite.h" +#include <vector> + +using namespace std; + +dbSqlite* db; + +void schedulerWeather(int time) { + std::cout << "Wetterstation::schedulerWeather" << std::endl; + while (1) { + openweathermap owmw; + + owmw.getWeather(db->getSettings().owm_plz, db->getSettings().owm_lngCode); + Sleep(time); + } +} + +void schedulerForecast(int time) { + std::cout << "Wetterstation::schedulerForecast" << std::endl; + while (1) { + openweathermap owmw; + + owmw.getForecast(db->getSettings().owm_plz, db->getSettings().owm_lngCode); + Sleep(time); + } +} + +int main() { + std::cout << "Wetterstation::main" << std::endl; + WSADATA wsaData; + + int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0) { + printf("WSAStartup failed: %d\n", iResult); + return 1; + } + + db = new dbSqlite(); + + std::thread thrWeather{ schedulerWeather, 60000 }; + Sleep(50); + std::thread thrForecast{ schedulerForecast, 900000 }; + + std::vector<WeatherConditions> cond = db->getConditions(); + + /*for (int i = 0; i < cond.size(); i++) { + cout << cond[i].condition_id << endl; + cout << cond[i].description << endl; + cout << "------------------------" << endl; + }*/ + + while (1) { + Sleep(1000); + } +} \ No newline at end of file diff --git a/header/dbSqlite.h b/header/dbSqlite.h new file mode 100644 index 0000000..696fefa --- /dev/null +++ b/header/dbSqlite.h @@ -0,0 +1,94 @@ +#pragma once +#include <iostream> +#include <string> +#include <vector> +#include "Poco/Data/Session.h" +#include "Poco/Data/SQLite/Connector.h" + +using namespace Poco::Data::Keywords; +using Poco::Data::Session; +using Poco::Data::Statement; +using std::string; +using std::cout; + +struct WeatherConditions { + int condition_id; + std::string main; + std::string description; +}; + +struct Settings { + std::string owm_appid; + std::string owm_plz; + std::string owm_lngCode; + std::string influx_host; + int influx_port; + std::string influx_db; + std::string influx_user; + std::string influx_pass; +}; + +class dbSqlite { +private: + std::vector<WeatherConditions> weatherConditions; + Settings settings; + +public: + string dbFile; + + dbSqlite() { + std::cout << "sbSqlite()" << std::endl; + Poco::Data::SQLite::Connector::registerConnector(); + this->dbFile = "weatherstation.db"; + this->weatherConditions = this->queryConditions(); + this->querySettings(); + }; + + Settings getSettings() { + std::cout << "dbSqlite::getSettings()" << std::endl; + std::cout << "owm_plz: " << this->settings.owm_plz << std::endl; + return this->settings; + } + + std::vector<WeatherConditions> getConditions() { + return this->weatherConditions; + } + + void querySettings() { + std::cout << "dbSqlite::querySettings()" << std::endl; + Session session("SQLite", this->dbFile); + + Statement select(session); + select << "SELECT * FROM settings LIMIT 1;", + into(this->settings.owm_appid), + into(this->settings.owm_plz), + into(this->settings.owm_lngCode), + into(this->settings.influx_host), + into(this->settings.influx_port), + into(this->settings.influx_db), + into(this->settings.influx_user), + into(this->settings.influx_pass), + range(0, 1); // iterate over result set one row at a time + select.execute(); + } + + std::vector<WeatherConditions> queryConditions() { + std::cout << "dbSqlite::queryConditions()" << std::endl; + Session session("SQLite", this->dbFile); + WeatherConditions wc; + + Statement select(session); + select << "SELECT * FROM weather_conditions;", + into(wc.condition_id), + into(wc.main), + into(wc.description), + range(0, 1); // iterate over result set one row at a time + + while (!select.done()) { + select.execute(); + weatherConditions.push_back(wc); + } + + return weatherConditions; + } +}; \ No newline at end of file diff --git a/header/influxdb.h b/header/influxdb.h new file mode 100644 index 0000000..9c0c57d --- /dev/null +++ b/header/influxdb.h @@ -0,0 +1,320 @@ +/* + influxdb-cpp -- 💜 C++ client for InfluxDB. + + Copyright (c) 2010-2018 <http://ez8.co> <orca.zhang@yahoo.com> + This library is released under the MIT License. + + Please see LICENSE file or visit https://github.com/orca-zhang/influxdb-cpp for details. + */ +#include <sstream> +#include <cstring> +#include <cstdio> +#include <cstdlib> + +#ifdef _WIN32 +#define NOMINMAX +#include <windows.h> +#include <algorithm> +#pragma comment(lib, "ws2_32") +#pragma warning(disable:4996) +typedef struct iovec { void* iov_base; size_t iov_len; } iovec; +inline __int64 writev(int sock, struct iovec* iov, int cnt) { + __int64 r = send(sock, (const char*)iov->iov_base, iov->iov_len, 0); + return (r < 0 || cnt == 1) ? r : r + writev(sock, iov + 1, cnt - 1); +} +#else +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <netinet/in.h> +#include <netdb.h> +#include <arpa/inet.h> +#define closesocket close +#endif + +namespace influxdb_cpp { + struct server_info { + std::string host_; + int port_; + std::string db_; + std::string usr_; + std::string pwd_; + std::string precision_; + server_info(const std::string& host, int port, const std::string& db = "", const std::string& usr = "", const std::string& pwd = "") { + port_ = port; + db_ = db; + usr_ = usr; + pwd_ = pwd; + + //convert hostname to ip-address + hostent* record = gethostbyname(host.c_str()); + if (record == NULL) { + printf("Cannot resolve IP address from hostname: %s is unavailable. Try to ping the host.\n", host.c_str()); + std::exit(-1); + } + in_addr* address = (in_addr*)record->h_addr; + std::string ip_address = inet_ntoa(*address); + + host_ = ip_address; + } + }; + namespace detail { + struct meas_caller; + struct tag_caller; + struct field_caller; + struct ts_caller; + struct inner { + static int http_request(const char*, const char*, const std::string&, const std::string&, const server_info&, std::string*); + static inline unsigned char to_hex(unsigned char x) { return x > 9 ? x + 55 : x + 48; } + static void url_encode(std::string& out, const std::string& src); + }; + } + + inline int query(std::string& resp, const std::string& query, const server_info& si) { + std::string qs("&q="); + detail::inner::url_encode(qs, query); + return detail::inner::http_request("GET", "query", qs, "", si, &resp); + } + inline int create_db(std::string& resp, const std::string& db_name, const server_info& si) { + std::string qs("&q=create+database+"); + detail::inner::url_encode(qs, db_name); + return detail::inner::http_request("POST", "query", qs, "", si, &resp); + } + + struct builder { + detail::tag_caller& meas(const std::string& m) { + lines_.imbue(std::locale("C")); + lines_.clear(); + return _m(m); + } + protected: + detail::tag_caller& _m(const std::string& m) { + _escape(m, ", "); + return (detail::tag_caller&) * this; + } + detail::tag_caller& _t(const std::string& k, const std::string& v) { + lines_ << ','; + _escape(k, ",= "); + lines_ << '='; + _escape(v, ",= "); + return (detail::tag_caller&) * this; + } + detail::field_caller& _f_s(char delim, const std::string& k, const std::string& v) { + lines_ << delim; + _escape(k, ",= "); + lines_ << "=\""; + _escape(v, "\""); + lines_ << '\"'; + return (detail::field_caller&) * this; + } + detail::field_caller& _f_i(char delim, const std::string& k, long long v) { + lines_ << delim; + _escape(k, ",= "); + lines_ << '='; + lines_ << v << 'i'; + return (detail::field_caller&) * this; + } + detail::field_caller& _f_f(char delim, const std::string& k, double v, int prec) { + lines_ << delim; + _escape(k, ",= "); + lines_.precision(prec); + lines_ << '=' << v; + return (detail::field_caller&) * this; + } + detail::field_caller& _f_b(char delim, const std::string& k, bool v) { + lines_ << delim; + _escape(k, ",= "); + lines_ << '=' << (v ? 't' : 'f'); + return (detail::field_caller&) * this; + } + detail::ts_caller& _ts(long long ts) { + lines_ << ' ' << ts; + return (detail::ts_caller&) * this; + } + int _post_http(const server_info& si, std::string* resp) { + return detail::inner::http_request("POST", "write", "", lines_.str(), si, resp); + } + int _send_udp(const std::string& host, int port) { + int sock, ret = 0; + struct sockaddr_in addr; + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + if ((addr.sin_addr.s_addr = inet_addr(host.c_str())) == INADDR_NONE) return -1; + + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return -2; + + lines_ << '\n'; + if (sendto(sock, &lines_.str()[0], lines_.str().length(), 0, (struct sockaddr*) & addr, sizeof(addr)) < (int)lines_.str().length()) + ret = -3; + + closesocket(sock); + return ret; + } + void _escape(const std::string& src, const char* escape_seq) { + size_t pos = 0, start = 0; + while ((pos = src.find_first_of(escape_seq, start)) != std::string::npos) { + lines_.write(src.c_str() + start, pos - start); + lines_ << '\\' << src[pos]; + start = ++pos; + } + lines_.write(src.c_str() + start, src.length() - start); + } + + std::stringstream lines_; + }; + + namespace detail { + struct tag_caller : public builder { + detail::tag_caller& tag(const std::string& k, const std::string& v) { return _t(k, v); } + detail::field_caller& field(const std::string& k, const std::string& v) { return _f_s(' ', k, v); } + detail::field_caller& field(const std::string& k, bool v) { return _f_b(' ', k, v); } + detail::field_caller& field(const std::string& k, short v) { return _f_i(' ', k, v); } + detail::field_caller& field(const std::string& k, int v) { return _f_i(' ', k, v); } + detail::field_caller& field(const std::string& k, long v) { return _f_i(' ', k, v); } + detail::field_caller& field(const std::string& k, long long v) { return _f_i(' ', k, v); } + detail::field_caller& field(const std::string& k, double v, int prec = 2) { return _f_f(' ', k, v, prec); } + private: + detail::tag_caller& meas(const std::string& m); + }; + struct ts_caller : public builder { + detail::tag_caller& meas(const std::string& m) { lines_ << '\n'; return _m(m); } + int post_http(const server_info& si, std::string* resp = NULL) { return _post_http(si, resp); } + int send_udp(const std::string& host, int port) { return _send_udp(host, port); } + }; + struct field_caller : public ts_caller { + detail::field_caller& field(const std::string& k, const std::string& v) { return _f_s(',', k, v); } + detail::field_caller& field(const std::string& k, bool v) { return _f_b(',', k, v); } + detail::field_caller& field(const std::string& k, short v) { return _f_i(',', k, v); } + detail::field_caller& field(const std::string& k, int v) { return _f_i(',', k, v); } + detail::field_caller& field(const std::string& k, long v) { return _f_i(',', k, v); } + detail::field_caller& field(const std::string& k, long long v) { return _f_i(',', k, v); } + detail::field_caller& field(const std::string& k, double v, int prec = 2) { return _f_f(',', k, v, prec); } + detail::ts_caller& timestamp(unsigned long long ts) { return _ts(ts); } + }; + inline void inner::url_encode(std::string& out, const std::string& src) { + size_t pos = 0, start = 0; + while ((pos = src.find_first_not_of("abcdefghijklmnopqrstuvwxyqABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~", start)) != std::string::npos) { + out.append(src.c_str() + start, pos - start); + if (src[pos] == ' ') + out += "+"; + else { + out += '%'; + out += to_hex((unsigned char)src[pos] >> 4); + out += to_hex((unsigned char)src[pos] & 0xF); + } + start = ++pos; + } + out.append(src.c_str() + start, src.length() - start); + } + inline int inner::http_request(const char* method, const char* uri, + const std::string& querystring, const std::string& body, const server_info& si, std::string* resp) { + std::string header; + struct iovec iv[2]; + struct sockaddr_in addr; + int sock, ret_code = 0, content_length = 0, len = 0; + char ch; + unsigned char chunked = 0; + + addr.sin_family = AF_INET; + addr.sin_port = htons(si.port_); + if ((addr.sin_addr.s_addr = inet_addr(si.host_.c_str())) == INADDR_NONE) return -1; + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) return -2; + + if (connect(sock, (struct sockaddr*)(&addr), sizeof(addr)) < 0) { + closesocket(sock); + return -3; + } + + header.resize(len = 0x100); + + for (;;) { + iv[0].iov_len = snprintf(&header[0], len, + "%s /%s?db=%s&u=%s&p=%s&precision=%s%s HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n", + method, uri, si.db_.c_str(), si.usr_.c_str(), si.pwd_.c_str(), si.precision_.c_str(), + querystring.c_str(), si.host_.c_str(), (int)body.length()); + if ((int)iv[0].iov_len >= len) + header.resize(len *= 2); + else + break; + } + iv[0].iov_base = &header[0]; + iv[1].iov_base = (void*)&body[0]; + iv[1].iov_len = body.length(); + + if (writev(sock, iv, 2) < (int)(iv[0].iov_len + iv[1].iov_len)) { + ret_code = -6; + goto END; + } + + iv[0].iov_len = len; + +#define _NO_MORE() (len >= (int)iv[0].iov_len && \ + (iv[0].iov_len = recv(sock, &header[0], header.length(), len = 0)) == size_t(-1)) +#define _GET_NEXT_CHAR() (ch = _NO_MORE() ? 0 : header[len++]) +#define _LOOP_NEXT(statement) for(;;) { if(!(_GET_NEXT_CHAR())) { ret_code = -7; goto END; } statement } +#define _UNTIL(c) _LOOP_NEXT( if(ch == c) break; ) +#define _GET_NUMBER(n) _LOOP_NEXT( if(ch >= '0' && ch <= '9') n = n * 10 + (ch - '0'); else break; ) +#define _GET_CHUNKED_LEN(n, c) _LOOP_NEXT( if(ch >= '0' && ch <= '9') n = n * 16 + (ch - '0'); \ + else if(ch >= 'A' && ch <= 'F') n = n * 16 + (ch - 'A') + 10; \ + else if(ch >= 'a' && ch <= 'f') n = n * 16 + (ch - 'a') + 10; else {if(ch != c) { ret_code = -8; goto END; } break;} ) +#define _(c) if((_GET_NEXT_CHAR()) != c) break; +#define __(c) if((_GET_NEXT_CHAR()) != c) { ret_code = -9; goto END; } + + if (resp) resp->clear(); + + _UNTIL(' ')_GET_NUMBER(ret_code) + for (;;) { + _UNTIL('\n') + switch (_GET_NEXT_CHAR()) { + case 'C':_('o')_('n')_('t')_('e')_('n')_('t')_('-') + _('L')_('e')_('n')_('g')_('t')_('h')_(':')_(' ') + _GET_NUMBER(content_length) + break; + case 'T':_('r')_('a')_('n')_('s')_('f')_('e')_('r')_('-') + _('E')_('n')_('c')_('o')_('d')_('i')_('n')_('g')_(':') + _(' ')_('c')_('h')_('u')_('n')_('k')_('e')_('d') + chunked = 1; + break; + case '\r':__('\n') + switch (chunked) { + do { + __('\r')__('\n') + case 1: + _GET_CHUNKED_LEN(content_length, '\r')__('\n') + if (!content_length) { + __('\r')__('\n') + goto END; + } + case 0: + while (content_length > 0 && !_NO_MORE()) { + content_length -= (iv[1].iov_len = (content_length < ((int)iv[0].iov_len - len) ? content_length : ((int)iv[0].iov_len - len))); + if (resp) resp->append(&header[len], iv[1].iov_len); + len += iv[1].iov_len; + } + } while (chunked); + } + goto END; + } + if (!ch) { + ret_code = -10; + goto END; + } + } + ret_code = -11; + END: + closesocket(sock); + return ret_code / 100 == 2 ? 0 : ret_code; +#undef _NO_MORE +#undef _GET_NEXT_CHAR +#undef _LOOP_NEXT +#undef _UNTIL +#undef _GET_NUMBER +#undef _GET_CHUNKED_LEN +#undef _ +#undef __ + } + } +} \ No newline at end of file diff --git a/header/json.h b/header/json.h new file mode 100644 index 0000000..8676bd7 --- /dev/null +++ b/header/json.h @@ -0,0 +1,646 @@ +#pragma once + +#include <cstdint> +#include <cmath> +#include <cctype> +#include <string> +#include <deque> +#include <map> +#include <type_traits> +#include <initializer_list> +#include <ostream> +#include <iostream> + +namespace json { + + using std::map; + using std::deque; + using std::string; + using std::enable_if; + using std::initializer_list; + using std::is_same; + using std::is_convertible; + using std::is_integral; + using std::is_floating_point; + + namespace { + string json_escape(const string& str) { + string output; + for (unsigned i = 0; i < str.length(); ++i) + switch (str[i]) { + case '\"': output += "\\\""; break; + case '\\': output += "\\\\"; break; + case '\b': output += "\\b"; break; + case '\f': output += "\\f"; break; + case '\n': output += "\\n"; break; + case '\r': output += "\\r"; break; + case '\t': output += "\\t"; break; + default: output += str[i]; break; + } + return std::move(output); + } + } + + class JSON { + union BackingData { + BackingData(double d) : Float(d) {} + BackingData(long l) : Int(l) {} + BackingData(bool b) : Bool(b) {} + BackingData(string s) : String(new string(s)) {} + BackingData() : Int(0) {} + + deque<JSON>* List; + map<string, JSON>* Map; + string* String; + double Float; + long Int; + bool Bool; + } Internal; + + public: + enum class Class { + Null, + Object, + Array, + String, + Floating, + Integral, + Boolean + }; + + template <typename Container> + class JSONWrapper { + Container* object; + + public: + JSONWrapper(Container* val) : object(val) {} + JSONWrapper(std::nullptr_t) : object(nullptr) {} + + typename Container::iterator begin() { return object ? object->begin() : typename Container::iterator(); } + typename Container::iterator end() { return object ? object->end() : typename Container::iterator(); } + typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::iterator(); } + typename Container::const_iterator end() const { return object ? object->end() : typename Container::iterator(); } + }; + + template <typename Container> + class JSONConstWrapper { + const Container* object; + + public: + JSONConstWrapper(const Container* val) : object(val) {} + JSONConstWrapper(std::nullptr_t) : object(nullptr) {} + + typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::const_iterator(); } + typename Container::const_iterator end() const { return object ? object->end() : typename Container::const_iterator(); } + }; + + JSON() : Internal(), Type(Class::Null) {} + + JSON(initializer_list<JSON> list) + : JSON() { + SetType(Class::Object); + for (auto i = list.begin(), e = list.end(); i != e; ++i, ++i) + operator[](i->ToString()) = *std::next(i); + } + + JSON(JSON&& other) + : Internal(other.Internal) + , Type(other.Type) { + other.Type = Class::Null; other.Internal.Map = nullptr; + } + + JSON& operator=(JSON&& other) { + ClearInternal(); + Internal = other.Internal; + Type = other.Type; + other.Internal.Map = nullptr; + other.Type = Class::Null; + return *this; + } + + JSON(const JSON& other) { + switch (other.Type) { + case Class::Object: + Internal.Map = + new map<string, JSON>(other.Internal.Map->begin(), + other.Internal.Map->end()); + break; + case Class::Array: + Internal.List = + new deque<JSON>(other.Internal.List->begin(), + other.Internal.List->end()); + break; + case Class::String: + Internal.String = + new string(*other.Internal.String); + break; + default: + Internal = other.Internal; + } + Type = other.Type; + } + + JSON& operator=(const JSON& other) { + ClearInternal(); + switch (other.Type) { + case Class::Object: + Internal.Map = + new map<string, JSON>(other.Internal.Map->begin(), + other.Internal.Map->end()); + break; + case Class::Array: + Internal.List = + new deque<JSON>(other.Internal.List->begin(), + other.Internal.List->end()); + break; + case Class::String: + Internal.String = + new string(*other.Internal.String); + break; + default: + Internal = other.Internal; + } + Type = other.Type; + return *this; + } + + ~JSON() { + switch (Type) { + case Class::Array: + delete Internal.List; + break; + case Class::Object: + delete Internal.Map; + break; + case Class::String: + delete Internal.String; + break; + default:; + } + } + + template <typename T> + JSON(T b, typename enable_if<is_same<T, bool>::value>::type* = 0) : Internal(b), Type(Class::Boolean) {} + + template <typename T> + JSON(T i, typename enable_if<is_integral<T>::value && !is_same<T, bool>::value>::type* = 0) : Internal((long)i), Type(Class::Integral) {} + + template <typename T> + JSON(T f, typename enable_if<is_floating_point<T>::value>::type* = 0) : Internal((double)f), Type(Class::Floating) {} + + template <typename T> + JSON(T s, typename enable_if<is_convertible<T, string>::value>::type* = 0) : Internal(string(s)), Type(Class::String) {} + + JSON(std::nullptr_t) : Internal(), Type(Class::Null) {} + + static JSON Make(Class type) { + JSON ret; ret.SetType(type); + return ret; + } + + static JSON Load(const string&); + + template <typename T> + void append(T arg) { + SetType(Class::Array); Internal.List->emplace_back(arg); + } + + template <typename T, typename... U> + void append(T arg, U... args) { + append(arg); append(args...); + } + + template <typename T> + typename enable_if<is_same<T, bool>::value, JSON&>::type operator=(T b) { + SetType(Class::Boolean); Internal.Bool = b; return *this; + } + + template <typename T> + typename enable_if<is_integral<T>::value && !is_same<T, bool>::value, JSON&>::type operator=(T i) { + SetType(Class::Integral); Internal.Int = i; return *this; + } + + template <typename T> + typename enable_if<is_floating_point<T>::value, JSON&>::type operator=(T f) { + SetType(Class::Floating); Internal.Float = f; return *this; + } + + template <typename T> + typename enable_if<is_convertible<T, string>::value, JSON&>::type operator=(T s) { + SetType(Class::String); *Internal.String = string(s); return *this; + } + + JSON& operator[](const string& key) { + SetType(Class::Object); return Internal.Map->operator[](key); + } + + JSON& operator[](unsigned index) { + SetType(Class::Array); + if (index >= Internal.List->size()) Internal.List->resize(index + 1); + return Internal.List->operator[](index); + } + + JSON& at(const string& key) { + return operator[](key); + } + + const JSON& at(const string& key) const { + return Internal.Map->at(key); + } + + JSON& at(unsigned index) { + return operator[](index); + } + + const JSON& at(unsigned index) const { + return Internal.List->at(index); + } + + int length() const { + if (Type == Class::Array) + return Internal.List->size(); + else + return -1; + } + + bool hasKey(const string& key) const { + if (Type == Class::Object) + return Internal.Map->find(key) != Internal.Map->end(); + return false; + } + + int size() const { + if (Type == Class::Object) + return Internal.Map->size(); + else if (Type == Class::Array) + return Internal.List->size(); + else + return -1; + } + + Class JSONType() const { return Type; } + + /// Functions for getting primitives from the JSON object. + bool IsNull() const { return Type == Class::Null; } + + string ToString() const { bool b; return std::move(ToString(b)); } + string ToString(bool& ok) const { + ok = (Type == Class::String); + return ok ? std::move(json_escape(*Internal.String)) : string(""); + } + + double ToFloat() const { bool b; return ToFloat(b); } + double ToFloat(bool& ok) const { + ok = (Type == Class::Floating); + return ok ? Internal.Float : 0.0; + } + + long ToInt() const { bool b; return ToInt(b); } + long ToInt(bool& ok) const { + ok = (Type == Class::Integral); + return ok ? Internal.Int : 0; + } + + bool ToBool() const { bool b; return ToBool(b); } + bool ToBool(bool& ok) const { + ok = (Type == Class::Boolean); + return ok ? Internal.Bool : false; + } + + JSONWrapper<map<string, JSON>> ObjectRange() { + if (Type == Class::Object) + return JSONWrapper<map<string, JSON>>(Internal.Map); + return JSONWrapper<map<string, JSON>>(nullptr); + } + + JSONWrapper<deque<JSON>> ArrayRange() { + if (Type == Class::Array) + return JSONWrapper<deque<JSON>>(Internal.List); + return JSONWrapper<deque<JSON>>(nullptr); + } + + JSONConstWrapper<map<string, JSON>> ObjectRange() const { + if (Type == Class::Object) + return JSONConstWrapper<map<string, JSON>>(Internal.Map); + return JSONConstWrapper<map<string, JSON>>(nullptr); + } + + + JSONConstWrapper<deque<JSON>> ArrayRange() const { + if (Type == Class::Array) + return JSONConstWrapper<deque<JSON>>(Internal.List); + return JSONConstWrapper<deque<JSON>>(nullptr); + } + + string dump(int depth = 1, string tab = " ") const { + string pad = ""; + for (int i = 0; i < depth; ++i, pad += tab); + + switch (Type) { + case Class::Null: + return "null"; + case Class::Object: { + string s = "{\n"; + bool skip = true; + for (auto& p : *Internal.Map) { + if (!skip) s += ",\n"; + s += (pad + "\"" + p.first + "\" : " + p.second.dump(depth + 1, tab)); + skip = false; + } + s += ("\n" + pad.erase(0, 2) + "}"); + return s; + } + case Class::Array: { + string s = "["; + bool skip = true; + for (auto& p : *Internal.List) { + if (!skip) s += ", "; + s += p.dump(depth + 1, tab); + skip = false; + } + s += "]"; + return s; + } + case Class::String: + return "\"" + json_escape(*Internal.String) + "\""; + case Class::Floating: + return std::to_string(Internal.Float); + case Class::Integral: + return std::to_string(Internal.Int); + case Class::Boolean: + return Internal.Bool ? "true" : "false"; + default: + return ""; + } + return ""; + } + + friend std::ostream& operator<<(std::ostream&, const JSON&); + + private: + void SetType(Class type) { + if (type == Type) + return; + + ClearInternal(); + + switch (type) { + case Class::Null: Internal.Map = nullptr; break; + case Class::Object: Internal.Map = new map<string, JSON>(); break; + case Class::Array: Internal.List = new deque<JSON>(); break; + case Class::String: Internal.String = new string(); break; + case Class::Floating: Internal.Float = 0.0; break; + case Class::Integral: Internal.Int = 0; break; + case Class::Boolean: Internal.Bool = false; break; + } + + Type = type; + } + + private: + /* beware: only call if YOU know that Internal is allocated. No checks performed here. + This function should be called in a constructed JSON just before you are going to + overwrite Internal... + */ + void ClearInternal() { + switch (Type) { + case Class::Object: delete Internal.Map; break; + case Class::Array: delete Internal.List; break; + case Class::String: delete Internal.String; break; + default:; + } + } + + private: + + Class Type = Class::Null; + }; + + JSON Array() { + return std::move(JSON::Make(JSON::Class::Array)); + } + + template <typename... T> + JSON Array(T... args) { + JSON arr = JSON::Make(JSON::Class::Array); + arr.append(args...); + return std::move(arr); + } + + JSON Object() { + return std::move(JSON::Make(JSON::Class::Object)); + } + + std::ostream& operator<<(std::ostream& os, const JSON& json) { + os << json.dump(); + return os; + } + + namespace { + JSON parse_next(const string&, size_t&); + + void consume_ws(const string& str, size_t& offset) { + while (isspace(str[offset])) ++offset; + } + + JSON parse_object(const string& str, size_t& offset) { + JSON Object = JSON::Make(JSON::Class::Object); + + ++offset; + consume_ws(str, offset); + if (str[offset] == '}') { + ++offset; return std::move(Object); + } + + while (true) { + JSON Key = parse_next(str, offset); + consume_ws(str, offset); + if (str[offset] != ':') { + std::cerr << "Error: Object: Expected colon, found '" << str[offset] << "'\n"; + break; + } + consume_ws(str, ++offset); + JSON Value = parse_next(str, offset); + Object[Key.ToString()] = Value; + + consume_ws(str, offset); + if (str[offset] == ',') { + ++offset; continue; + } + else if (str[offset] == '}') { + ++offset; break; + } + else { + std::cerr << "ERROR: Object: Expected comma, found '" << str[offset] << "'\n"; + break; + } + } + + return std::move(Object); + } + + JSON parse_array(const string& str, size_t& offset) { + JSON Array = JSON::Make(JSON::Class::Array); + unsigned index = 0; + + ++offset; + consume_ws(str, offset); + if (str[offset] == ']') { + ++offset; return std::move(Array); + } + + while (true) { + Array[index++] = parse_next(str, offset); + consume_ws(str, offset); + + if (str[offset] == ',') { + ++offset; continue; + } + else if (str[offset] == ']') { + ++offset; break; + } + else { + std::cerr << "ERROR: Array: Expected ',' or ']', found '" << str[offset] << "'\n"; + return std::move(JSON::Make(JSON::Class::Array)); + } + } + + return std::move(Array); + } + + JSON parse_string(const string& str, size_t& offset) { + JSON String; + string val; + for (char c = str[++offset]; c != '\"'; c = str[++offset]) { + if (c == '\\') { + switch (str[++offset]) { + case '\"': val += '\"'; break; + case '\\': val += '\\'; break; + case '/': val += '/'; break; + case 'b': val += '\b'; break; + case 'f': val += '\f'; break; + case 'n': val += '\n'; break; + case 'r': val += '\r'; break; + case 't': val += '\t'; break; + case 'u': { + val += "\\u"; + for (unsigned i = 1; i <= 4; ++i) { + c = str[offset + i]; + if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) + val += c; + else { + std::cerr << "ERROR: String: Expected hex character in unicode escape, found '" << c << "'\n"; + return std::move(JSON::Make(JSON::Class::String)); + } + } + offset += 4; + } break; + default: val += '\\'; break; + } + } + else + val += c; + } + ++offset; + String = val; + return std::move(String); + } + + JSON parse_number(const string& str, size_t& offset) { + JSON Number; + string val, exp_str; + char c; + bool isDouble = false; + long exp = 0; + while (true) { + c = str[offset++]; + if ((c == '-') || (c >= '0' && c <= '9')) + val += c; + else if (c == '.') { + val += c; + isDouble = true; + } + else + break; + } + if (c == 'E' || c == 'e') { + c = str[offset++]; + if (c == '-') { ++offset; exp_str += '-'; } + while (true) { + c = str[offset++]; + if (c >= '0' && c <= '9') + exp_str += c; + else if (!isspace(c) && c != ',' && c != ']' && c != '}') { + std::cerr << "ERROR: Number: Expected a number for exponent, found '" << c << "'\n"; + return std::move(JSON::Make(JSON::Class::Null)); + } + else + break; + } + exp = std::stol(exp_str); + } + else if (!isspace(c) && c != ',' && c != ']' && c != '}') { + std::cerr << "ERROR: Number: unexpected character '" << c << "'\n"; + return std::move(JSON::Make(JSON::Class::Null)); + } + --offset; + + if (isDouble) + Number = std::stod(val) * std::pow(10, exp); + else { + if (!exp_str.empty()) + Number = std::stol(val) * std::pow(10, exp); + else + Number = std::stol(val); + } + return std::move(Number); + } + + JSON parse_bool(const string& str, size_t& offset) { + JSON Bool; + if (str.substr(offset, 4) == "true") + Bool = true; + else if (str.substr(offset, 5) == "false") + Bool = false; + else { + std::cerr << "ERROR: Bool: Expected 'true' or 'false', found '" << str.substr(offset, 5) << "'\n"; + return std::move(JSON::Make(JSON::Class::Null)); + } + offset += (Bool.ToBool() ? 4 : 5); + return std::move(Bool); + } + + JSON parse_null(const string& str, size_t& offset) { + JSON Null; + if (str.substr(offset, 4) != "null") { + std::cerr << "ERROR: Null: Expected 'null', found '" << str.substr(offset, 4) << "'\n"; + return std::move(JSON::Make(JSON::Class::Null)); + } + offset += 4; + return std::move(Null); + } + + JSON parse_next(const string& str, size_t& offset) { + char value; + consume_ws(str, offset); + value = str[offset]; + switch (value) { + case '[': return std::move(parse_array(str, offset)); + case '{': return std::move(parse_object(str, offset)); + case '\"': return std::move(parse_string(str, offset)); + case 't': + case 'f': return std::move(parse_bool(str, offset)); + case 'n': return std::move(parse_null(str, offset)); + default: if ((value <= '9' && value >= '0') || value == '-') + return std::move(parse_number(str, offset)); + } + std::cerr << "ERROR: Parse: Unknown starting character '" << value << "'\n"; + return JSON(); + } + } + + JSON JSON::Load(const string& str) { + size_t offset = 0; + return std::move(parse_next(str, offset)); + } +} \ No newline at end of file diff --git a/header/openweathermap.h b/header/openweathermap.h new file mode 100644 index 0000000..47155ed --- /dev/null +++ b/header/openweathermap.h @@ -0,0 +1,196 @@ +#pragma once +#include <iostream> +#include <sstream> +#include <vector> + +#include "Poco/Data/Session.h" +#include "Poco/Data/SQLite/Connector.h" +#include <Poco/JSON/JSON.h> +#include <Poco/JSON/Parser.h> +#include <Poco/Dynamic/Var.h> +#include <Poco/URI.h> +#include <Poco/Net/HTTPClientSession.h> +#include <Poco/Net/HTTPRequest.h> +#include <Poco/Net/HTTPResponse.h> +#include <Poco/StreamCopier.h> +#include <Poco/Path.h> +#include <Poco/Exception.h> +#include "influxdb.h" +#include "json.h" +#include "dbSqlite.h" + +struct weatherData { + std::string plz; + std::string lngCode; + int sunrise; + int sunset; + int visibility; + double temp; + double tempFeelsLike; + double tempMin; + double tempMax; + int humidity; + int pressure; + double windSpeed; + int windDeg; + int clouds; + double rain1h; + double rain3h; + double snow1h; + double snow3h; + std::string icons; + std::string from; +}; + +class openweathermap { +private: + std::string appid; + std::string plz; + std::string lngCode; + weatherData sWeather; + std::vector<weatherData> vecForecast; + dbSqlite* db; +public: + openweathermap() { + std::cout << "openweathermap::openweathermap()" << std::endl; + db = new dbSqlite(); + } + + std::string str_tolower(std::string s) { + std::cout << "openweathermap::str_tolower()" << std::endl; + std::transform(s.begin(), s.end(), s.begin(), + [](unsigned char c) { return std::tolower(c); } + ); + return s; + } + + weatherData getSWeather() { + std::cout << "openweathermap::getSWeather()" << std::endl; + return this->sWeather; + } + + std::vector<weatherData> getSForecast() { + std::cout << "openweathermap::getSForecast()" << std::endl; + return this->vecForecast; + } + + void setSWeather(json::JSON jObj, std::string plz, std::string lngCode) { + std::cout << "openweathermap::setSWeather()" << std::endl; + this->sWeather.plz = plz; + this->sWeather.lngCode = lngCode; + this->sWeather.visibility = jObj["visibility"].ToInt(); + this->sWeather.sunrise = jObj["sys"]["sunrise"].ToInt(); + this->sWeather.sunset = jObj["sys"]["sunset"].ToInt(); + this->sWeather.temp = jObj["main"]["temp"].ToFloat(); + this->sWeather.tempFeelsLike = jObj["main"]["feels_like"].ToFloat(); + this->sWeather.tempMin = jObj["main"]["temp_min"].ToFloat(); + this->sWeather.tempMax = jObj["main"]["temp_max"].ToFloat(); + this->sWeather.humidity = jObj["main"]["humidity"].ToInt(); + this->sWeather.pressure = jObj["main"]["pressure"].ToInt(); + this->sWeather.windSpeed = jObj["wind"]["speed"].ToFloat(); + this->sWeather.windDeg = jObj["wind"]["deg"].ToInt(); + this->sWeather.clouds = jObj["clouds"]["all"].ToInt(); + this->sWeather.rain1h = jObj["rain"]["1h"].ToFloat(); + this->sWeather.rain3h = jObj["rain"]["3h"].ToFloat(); + this->sWeather.snow1h = jObj["snow"]["1h"].ToFloat(); + this->sWeather.snow3h = jObj["snow"]["3h"].ToFloat(); + this->sWeather.icons = jObj["weather"].dump(); + } + + void setSForecast(json::JSON jObj, std::string plz, std::string lngCode) { + std::cout << "openweathermap::setSForecast()" << std::endl; + for (int i = 0; i < jObj["cnt"].ToInt(); i++) { + weatherData item; + item.plz = plz; + item.lngCode = lngCode; + item.sunrise = jObj["city"]["sunrise"].ToInt(); + item.sunset = jObj["city"]["sunset"].ToInt(); + item.temp = jObj["list"][i]["main"]["temp"].ToFloat(); + item.tempFeelsLike = jObj["list"][i]["main"]["feels_like"].ToFloat(); + item.tempMin = jObj["list"][i]["main"]["temp_min"].ToFloat(); + item.tempMax = jObj["list"][i]["main"]["temp_max"].ToFloat(); + item.humidity = jObj["list"][i]["main"]["humidity"].ToInt(); + item.pressure = jObj["list"][i]["main"]["pressure"].ToInt(); + item.windSpeed = jObj["list"][i]["wind"]["speed"].ToFloat(); + item.windDeg = jObj["list"][i]["wind"]["deg"].ToInt(); + item.clouds = jObj["list"][i]["clouds"]["all"].ToInt(); + item.icons = jObj["list"][i]["weather"].dump(); + item.from = jObj["list"][i]["dt_txt"].ToString(); + if (vecForecast.size() != jObj["cnt"].ToInt()) { + this->vecForecast.push_back(item); + } + else { + this->vecForecast[i] = item; + } + } + } + + std::string getResponse(std::string url) { + std::cout << "openweathermap::getResponse()" << std::endl; + try { + // prepare session + Poco::URI uri(url); + Poco::Net::HTTPClientSession session(uri.getHost(), uri.getPort()); + + // prepare path + std::string path(uri.getPathAndQuery()); + if (path.empty()) path = "/"; + + // send request + Poco::Net::HTTPRequest req(Poco::Net::HTTPRequest::HTTP_GET, path, Poco::Net::HTTPMessage::HTTP_1_1); + session.sendRequest(req); + + // get response + Poco::Net::HTTPResponse res; + std::cout << res.getStatus() << " " << res.getReason() << std::endl; + + // copy response to output string stream + std::istream& is = session.receiveResponse(res); + std::ostringstream oss; + Poco::StreamCopier::copyStream(is, oss); + + return oss.str(); + } + catch (Poco::Exception & ex) { + std::cerr << ex.displayText() << std::endl; + } + } + + void getWeather(std::string plz, std::string lngCode) { + std::cout << "openweathermap::getWeather" << std::endl; + lngCode = str_tolower(lngCode); + this->setSWeather(json::JSON::Load(this->getResponse("http://api.openweathermap.org/data/2.5/weather?zip=" + plz + "," + lngCode + "&appid=" + db->getSettings().owm_appid + "&mode=json&units=metric&lang=de")), plz, lngCode); + + std::cout << "owm_appid: " << db->getSettings().owm_appid << std::endl; + + influxdb_cpp::server_info si(db->getSettings().influx_host, db->getSettings().influx_port, db->getSettings().influx_db, db->getSettings().influx_user, db->getSettings().influx_pass); + std::cout << "Influx Result: " << influxdb_cpp::builder() + .meas("Weather") + .tag("plz", this->sWeather.plz) + .tag("lngCode", this->sWeather.lngCode) + .field("sunrise", this->sWeather.sunrise) + .field("sunset", this->sWeather.sunset) + .field("visibility", this->sWeather.visibility) + .field("temp", this->sWeather.temp) + .field("temp_feels_like", this->sWeather.tempFeelsLike) + .field("temp_min", this->sWeather.tempMin) + .field("temp_max", this->sWeather.tempMax) + .field("humidity", this->sWeather.humidity) + .field("pressure", this->sWeather.pressure) + .field("wind_speed", this->sWeather.windSpeed) + .field("wind_deg", this->sWeather.windDeg) + .field("rain1h", this->sWeather.rain1h) + .field("rain3h", this->sWeather.rain3h) + .field("snow1h", this->sWeather.snow1h) + .field("snow3h", this->sWeather.snow3h) + .field("clouds", this->sWeather.clouds) + .field("icons", this->sWeather.icons) + .post_http(si); + } + + void getForecast(std::string plz, std::string lngCode) { + std::cout << "openweathermap::getForecast" << std::endl; + lngCode = str_tolower(lngCode); + this->setSForecast(json::JSON::Load(this->getResponse("http://api.openweathermap.org/data/2.5/forecase?zip=" + plz + "," + lngCode + "&appid=" + db->getSettings().owm_appid + "&mode=json&units=metric&lang=de")), plz, lngCode); + } +}; \ No newline at end of file -- GitLab