From e4869d23bfc552a320e06664c148c5367c2e4184 Mon Sep 17 00:00:00 2001 From: stephb9959 Date: Sun, 15 May 2022 10:39:42 -0700 Subject: [PATCH] Adding WifiClientHistory. --- CMakeLists.txt | 2 +- build | 2 +- openapi/owanalytics.yaml | 140 +++++++++++++++- src/APStats.cpp | 81 ++++++++- .../RESTAPI_wificlienthistory_handler.cpp | 70 ++++++++ .../RESTAPI_wificlienthistory_handler.h | 33 ++++ src/RESTObjects/RESTAPI_AnalyticsObjects.cpp | 109 +++++++++++- src/RESTObjects/RESTAPI_AnalyticsObjects.h | 57 ++++++- src/StorageService.cpp | 6 +- src/StorageService.h | 7 +- src/storage/storage_wificlients.cpp | 156 ++++++++++++++++++ src/storage/storage_wificlients.h | 57 +++++++ 12 files changed, 705 insertions(+), 15 deletions(-) create mode 100644 src/RESTAPI/RESTAPI_wificlienthistory_handler.cpp create mode 100644 src/RESTAPI/RESTAPI_wificlienthistory_handler.h create mode 100644 src/storage/storage_wificlients.cpp create mode 100644 src/storage/storage_wificlients.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c4fdf06..c29ba11 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,7 +100,7 @@ add_executable(owanalytics src/HealthReceiver.cpp src/HealthReceiver.h src/StatFunc.h src/RESTAPI/RESTAPI_board_timepoint_handler.cpp src/RESTAPI/RESTAPI_board_timepoint_handler.h - src/storage/storage_timepoints.cpp src/storage/storage_timepoints.h) + src/storage/storage_timepoints.cpp src/storage/storage_timepoints.h src/storage/storage_wificlients.cpp src/storage/storage_wificlients.h src/RESTAPI/RESTAPI_wificlienthistory_handler.cpp src/RESTAPI/RESTAPI_wificlienthistory_handler.h) target_link_libraries(owanalytics PUBLIC ${Poco_LIBRARIES} diff --git a/build b/build index fc9afb4..4e9e288 100644 --- a/build +++ b/build @@ -1 +1 @@ -59 \ No newline at end of file +63 \ No newline at end of file diff --git a/openapi/owanalytics.yaml b/openapi/owanalytics.yaml index ed5966e..0865f2d 100644 --- a/openapi/owanalytics.yaml +++ b/openapi/owanalytics.yaml @@ -498,7 +498,7 @@ components: format: uuid boardId: type: string - format: + format: uuid serialNumber: type: string timestamp: @@ -577,6 +577,90 @@ components: type: integer format: int64 + WifiClientHistory: + type: object + properties: + timestamp: + type: integer + stationId: + type: string + bssId: + type: string + ssid: + type: string + rssi: + type: integer + rx_bitrate: + type: integer + rx_chwidth: + type: integer + rx_mcs: + type: integer + rx_nss: + type: integer + rx_vht: + type: boolean + tx_bitrate: + type: integer + tx_chwidth: + type: integer + tx_mcs: + type: integer + tx_nss: + type: integer + tx_vht: + type: boolean + rx_bytes: + type: integer + tx_bytes: + type: integer + rx_duration: + type: integer + tx_duration: + type: integer + rx_packets: + type: integer + tx_packets: + type: integer + ipv4: + type: string + ipv6: + type: string + channel_width: + type: integer + noise: + type: integer + tx_power: + type: integer + channel: + type: integer + active_ms: + type: integer + busy_ms: + type: integer + receive_ms: + type: integer + mode: + type: string + ack_signal: + type: integer + ack_signal_avg: + type: integer + connected: + type: integer + inactive: + type: integer + tx_retries: + type: integer + + WifiClientHistoryList: + type: object + properties: + entries: + type: array + items: + $ref: '#/components/schemas/WifiClientHistory' + ######################################################################################### ## ## These are endpoints that all services in the OPenWiFI stack must provide @@ -998,6 +1082,16 @@ paths: default: false required: false + responses: + 200: + $ref: '#/components/schemas/DeviceTimePointList' + 400: + $ref: '#/components/responses/BadRequest' + 403: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + delete: tags: - Board data @@ -1066,6 +1160,50 @@ paths: 404: $ref: '#/components/responses/NotFound' + /wifiClientHistory/{client}: + get: + tags: + - WiFiClientHistory + operationId: getWifiClientHistory + summary: Retrieve WiFi client history for debugging purpose + parameters: + - in: path + name: client + schema: + type: string + example: + "112233aabbcc" + required: true + - in: query + name: fromDate + schema: + type: integer + required: false + - in: query + name: endDate + schema: + type: integer + required: false + - in: query + description: Pagination start (starts at 1. If not specified, 1 is assumed) + name: offset + schema: + type: integer + required: false + - in: query + description: Maximum number of entries to return (if absent, no limit is assumed) + name: limit + schema: + type: integer + required: false + responses: + 200: + $ref: '#/components/schemas/WifiClientHistoryList' + 403: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + ######################################################################################### ## ## These are endpoints that all services in the OpenWiFi stack must provide diff --git a/src/APStats.cpp b/src/APStats.cpp index f7cc943..e830f13 100644 --- a/src/APStats.cpp +++ b/src/APStats.cpp @@ -8,12 +8,31 @@ namespace OpenWifi { + static std::string mac_filter(const std::string &m) { + std::string r; + for(const auto &c:m) + if(c!=':' && c!='-') r += c; + return r; + } + template void GetJSON(const char *field, const nlohmann::json & doc, T & v , const T & def ) { - if(doc.contains(field) && !doc[field].is_null()) { + try { v = doc[field].get(); - } else { - v = def; + return; + } catch (...) { + } + v = def; + } + + template void GetJSON(const char *field1, const char *field2, const nlohmann::json & doc, T & v , const T & def ) { + try { + v = doc[field1][field2].get(); + return; + } catch (...) { + + } + v = def; } inline double safe_div(uint64_t a , uint64_t b) { @@ -115,6 +134,7 @@ namespace OpenWifi { GetJSON("active_ms", radio, RTP.active_ms, (uint64_t) 0); GetJSON("channel", radio, RTP.channel, (uint64_t) 0); GetJSON("temperature", radio, RTP.temperature, (int64_t) 20); + GetJSON("channel_width", radio, RTP.channel_width, (uint64_t) 20); if(RTP.temperature==0) RTP.temperature = 20; GetJSON("noise", radio, RTP.noise, (int64_t) -90); @@ -168,9 +188,9 @@ namespace OpenWifi { GetJSON("ssid",ssid,SSIDTP.ssid, std::string{""} ); if (ssid.contains("associations") && ssid["associations"].is_array()) { auto associations = ssid["associations"]; - auto it = radio_map.find(radio_location); - if(it!=radio_map.end()) { - auto the_radio = it->second.first; + auto radio_it = radio_map.find(radio_location); + if(radio_it!=radio_map.end()) { + auto the_radio = radio_it->second.first; if (the_radio == 2) DI_.associations_2g += associations.size(); else if (the_radio == 5) @@ -179,6 +199,7 @@ namespace OpenWifi { DI_.associations_6g += associations.size(); } for(const auto &association:associations) { + AnalyticsObjects::UETimePoint TP; GetJSON("station",association,TP.station, std::string{} ); GetJSON("rssi",association,TP.rssi, (int64_t)0 ); @@ -192,6 +213,54 @@ namespace OpenWifi { GetJSON("connected",association,TP.connected, (uint64_t)0 ); GetJSON("inactive",association,TP.inactive, (uint64_t)0 ); + AnalyticsObjects::WifiClientHistory WFH; + + WFH.stationId = mac_filter(TP.station); + WFH.bssId = mac_filter(SSIDTP.bssid); + WFH.ssid = SSIDTP.ssid; + WFH.rssi = TP.rssi; + GetJSON("rx_rate","bitrate",association,WFH.rx_bitrate,(uint32_t)0); + GetJSON("rx_rate","chwidth",association,WFH.rx_chwidth,(uint32_t)0); + GetJSON("rx_rate","mcs",association,WFH.rx_mcs,(uint16_t)0); + GetJSON("rx_rate","nss",association,WFH.rx_nss,(uint16_t)0); + GetJSON("rx_rate","vht",association,WFH.rx_vht,false); + GetJSON("tx_rate","bitrate",association,WFH.tx_bitrate,(uint32_t)0); + GetJSON("tx_rate","chwidth",association,WFH.tx_chwidth,(uint32_t)0); + GetJSON("tx_rate","mcs",association,WFH.tx_mcs,(uint16_t)0); + GetJSON("tx_rate","nss",association,WFH.tx_nss,(uint16_t)0); + GetJSON("tx_rate","vht",association,WFH.tx_vht,false); + GetJSON("rx_bytes",association,WFH.rx_bytes,(uint64_t)0); + GetJSON("tx_bytes",association,WFH.tx_bytes,(uint64_t)0); + GetJSON("rx_duration",association,WFH.rx_duration,(uint64_t)0); + GetJSON("tx_duration",association,WFH.tx_duration,(uint64_t)0); + GetJSON("rx_packets",association,WFH.rx_packets,(uint64_t)0); + GetJSON("tx_packets",association,WFH.tx_packets,(uint64_t)0); + + WFH.ipv4 = "---"; + WFH.ipv6 = "----"; + + for(const auto &rd:DTP.radio_data) { + if(rd.band == SSIDTP.band) { + WFH.channel_width = rd.channel_width; + WFH.noise = rd.noise; + WFH.tx_power = rd.tx_power; + WFH.channel = rd.channel; + WFH.active_ms = rd.active_ms; + WFH.busy_ms = rd.busy_ms; + WFH.receive_ms = rd.receive_ms; + break; + } + } + + WFH.mode = SSIDTP.mode; + GetJSON("ack_signal",association,WFH.ack_signal,(int64_t)0); + GetJSON("ack_signal_avg",association,WFH.ack_signal_avg,(int64_t)0); + GetJSON("connected",association,WFH.connected,(uint64_t)0); + GetJSON("inactive",association,WFH.inactive,(uint64_t)0); + GetJSON("tx_retries",association,WFH.tx_retries,(uint64_t)0); + + StorageService()->WifiClientHistoryDB().CreateRecord(WFH); + if(association.contains("tid_stats") && association["tid_stats"].is_array()) { auto tid_stats = association["tid_stats"]; for(const auto &tid_stat:tid_stats) { diff --git a/src/RESTAPI/RESTAPI_wificlienthistory_handler.cpp b/src/RESTAPI/RESTAPI_wificlienthistory_handler.cpp new file mode 100644 index 0000000..ee6b479 --- /dev/null +++ b/src/RESTAPI/RESTAPI_wificlienthistory_handler.cpp @@ -0,0 +1,70 @@ +// +// Created by stephane bourque on 2022-05-15. +// + +#include "RESTAPI_wificlienthistory_handler.h" +#include "RESTAPI/RESTAPI_analytics_db_helpers.h" + +namespace OpenWifi { + + void RESTAPI_wificlienthistory_handler::DoGet() { + + auto stationId = GetBinding("client"); + if(!Utils::ValidSerialNumber(stationId)) { + return BadRequest(RESTAPI::Errors::InvalidSerialNumber); + } + + auto fromDate = GetParameter("fromDate",0); + auto endDate = GetParameter("endDate",0); + + WifiClientHistoryDB::RecordVec Results; + std::string Where; + if(fromDate && endDate) + Where = fmt::format(" stationId='{}' and timestamp>={} and timestamp<={} ", stationId, fromDate, endDate); + else if(fromDate && !endDate) + Where = fmt::format(" stationId='{}' and timestamp>={} ", stationId, fromDate); + else if(!fromDate && endDate) + Where = fmt::format(" stationId='{}' and timestamp<={} ", stationId, endDate); + else + Where = fmt::format(" stationId='{}' ", stationId); + + if(StorageService()->WifiClientHistoryDB().GetRecords(QB_.Offset,QB_.Limit, Results, Where)) { + return ReturnObject("entries",Results); + } + + return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); + } + + void RESTAPI_wificlienthistory_handler::DoDelete() { + + if(UserInfo_.userinfo.userRole!=SecurityObjects::ADMIN && UserInfo_.userinfo.userRole!=SecurityObjects::ROOT) { + return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); + } + + auto stationId = GetBinding("client"); + if(!Utils::ValidSerialNumber(stationId)) { + return BadRequest(RESTAPI::Errors::InvalidSerialNumber); + } + + auto fromDate = GetParameter("fromDate",0); + auto endDate = GetParameter("endDate",0); + + WifiClientHistoryDB::RecordVec Results; + std::string Where; + if(fromDate && endDate) + Where = fmt::format(" stationId='{}' and timestamp>={} and timestamp<={} ", stationId, fromDate, endDate); + else if(fromDate && !endDate) + Where = fmt::format(" stationId='{}' and timestamp>={} ", stationId, fromDate); + else if(!fromDate && endDate) + Where = fmt::format(" stationId='{}' and timestamp<={} ", stationId, endDate); + else + Where = fmt::format(" stationId='{}' ", stationId); + + if(StorageService()->WifiClientHistoryDB().DeleteRecords(Where)) { + return OK(); + } + + return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); + } + +} \ No newline at end of file diff --git a/src/RESTAPI/RESTAPI_wificlienthistory_handler.h b/src/RESTAPI/RESTAPI_wificlienthistory_handler.h new file mode 100644 index 0000000..f9d5ce3 --- /dev/null +++ b/src/RESTAPI/RESTAPI_wificlienthistory_handler.h @@ -0,0 +1,33 @@ +// +// Created by stephane bourque on 2022-05-15. +// + +#pragma once + +#include "framework/MicroService.h" +#include "StorageService.h" + +namespace OpenWifi { + + class RESTAPI_wificlienthistory_handler : public RESTAPIHandler { + public: + RESTAPI_wificlienthistory_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + : RESTAPIHandler(bindings, L, + std::vector{ + Poco::Net::HTTPRequest::HTTP_GET, + Poco::Net::HTTPRequest::HTTP_DELETE, + Poco::Net::HTTPRequest::HTTP_OPTIONS}, + Server, + TransactionId, + Internal){} + + static auto PathName() { return std::list{"/api/v1//wifiClientHistory/{client}"}; }; + + private: + OpenWifi::WifiClientHistoryDB & DB_=StorageService()->WifiClientHistoryDB(); + void DoGet() final; + void DoPost() final {}; + void DoPut() final {}; + void DoDelete() final; + }; +} \ No newline at end of file diff --git a/src/RESTObjects/RESTAPI_AnalyticsObjects.cpp b/src/RESTObjects/RESTAPI_AnalyticsObjects.cpp index 2dc57e6..aebe317 100644 --- a/src/RESTObjects/RESTAPI_AnalyticsObjects.cpp +++ b/src/RESTObjects/RESTAPI_AnalyticsObjects.cpp @@ -316,7 +316,7 @@ namespace OpenWifi::AnalyticsObjects { void RadioTimePoint::to_json(Poco::JSON::Object &Obj) const { field_to_json(Obj,"band",band); - field_to_json(Obj,"radio_channel",radio_channel); + field_to_json(Obj,"channel_width",channel_width); field_to_json(Obj,"active_ms",active_ms); field_to_json(Obj,"busy_ms",busy_ms); field_to_json(Obj,"receive_ms",receive_ms); @@ -334,7 +334,7 @@ namespace OpenWifi::AnalyticsObjects { bool RadioTimePoint::from_json(const Poco::JSON::Object::Ptr &Obj) { try { field_from_json(Obj,"band",band); - field_from_json(Obj,"radio_channel",radio_channel); + field_from_json(Obj,"channel_width",channel_width); field_from_json(Obj,"active_ms",active_ms); field_from_json(Obj,"busy_ms",busy_ms); field_from_json(Obj,"receive_ms",receive_ms); @@ -514,4 +514,109 @@ namespace OpenWifi::AnalyticsObjects { return false; } + void WifiClientRate::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"bitrate",bitrate); + field_to_json(Obj,"chwidth",chwidth); + field_to_json(Obj,"mcs",mcs); + field_to_json(Obj,"nss",nss); + field_to_json(Obj,"vht",vht); + } + + bool WifiClientRate::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj,"bitrate",bitrate); + field_from_json(Obj,"chwidth",chwidth); + field_from_json(Obj,"mcs",mcs); + field_from_json(Obj,"nss",nss); + field_from_json(Obj,"vht",vht); + return true; + } catch(...) { + + } + return false; + } + + void WifiClientHistory::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"timestamp",timestamp); + field_to_json(Obj,"stationId",stationId); + field_to_json(Obj,"bssId",bssId); + field_to_json(Obj,"ssid",ssid); + field_to_json(Obj,"rssi",rssi); + field_to_json(Obj,"rx_bitrate",rx_bitrate); + field_to_json(Obj,"rx_chwidth",rx_chwidth); + field_to_json(Obj,"rx_mcs",rx_mcs); + field_to_json(Obj,"rx_nss",rx_nss); + field_to_json(Obj,"rx_vht",rx_vht); + field_to_json(Obj,"tx_bitrate",tx_bitrate); + field_to_json(Obj,"tx_chwidth",tx_chwidth); + field_to_json(Obj,"tx_mcs",tx_mcs); + field_to_json(Obj,"tx_nss",tx_nss); + field_to_json(Obj,"tx_vht",tx_vht); + field_to_json(Obj,"rx_bytes",rx_bytes); + field_to_json(Obj,"tx_bytes",tx_bytes); + field_to_json(Obj,"rx_duration",rx_duration); + field_to_json(Obj,"tx_duration",tx_duration); + field_to_json(Obj,"rx_packets",rx_packets); + field_to_json(Obj,"tx_packets",tx_packets); + field_to_json(Obj,"ipv4",ipv4); + field_to_json(Obj,"ipv6",ipv6); + field_to_json(Obj,"channel_width",channel_width); + field_to_json(Obj,"noise",noise); + field_to_json(Obj,"tx_power",tx_power); + field_to_json(Obj,"channel",channel); + field_to_json(Obj,"active_ms",active_ms); + field_to_json(Obj,"busy_ms",busy_ms); + field_to_json(Obj,"receive_ms",receive_ms); + field_to_json(Obj,"mode",mode); + field_to_json(Obj,"ack_signal",ack_signal); + field_to_json(Obj,"ack_signal_avg",ack_signal_avg); + field_to_json(Obj,"connected",connected); + field_to_json(Obj,"inactive",inactive); + field_to_json(Obj,"tx_retries",tx_retries); + } + + bool WifiClientHistory::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj,"timestamp",timestamp); + field_from_json(Obj,"stationId",stationId); + field_from_json(Obj,"bssId",bssId); + field_from_json(Obj,"ssid",ssid); + field_from_json(Obj,"rssi",rssi); + field_from_json(Obj,"rx_bitrate",rx_bitrate); + field_from_json(Obj,"rx_chwidth",rx_chwidth); + field_from_json(Obj,"rx_mcs",rx_mcs); + field_from_json(Obj,"rx_nss",rx_nss); + field_from_json(Obj,"rx_vht",rx_vht); + field_from_json(Obj,"tx_bitrate",tx_bitrate); + field_from_json(Obj,"tx_chwidth",tx_chwidth); + field_from_json(Obj,"tx_mcs",tx_mcs); + field_from_json(Obj,"tx_nss",tx_nss); + field_from_json(Obj,"tx_vht",tx_vht); + field_from_json(Obj,"rx_bytes",rx_bytes); + field_from_json(Obj,"tx_bytes",tx_bytes); + field_from_json(Obj,"rx_duration",rx_duration); + field_from_json(Obj,"tx_duration",tx_duration); + field_from_json(Obj,"rx_packets",rx_packets); + field_from_json(Obj,"tx_packets",tx_packets); + field_from_json(Obj,"ipv4",ipv4); + field_from_json(Obj,"ipv6",ipv6); + field_from_json(Obj,"channel_width",channel_width); + field_from_json(Obj,"noise",noise); + field_from_json(Obj,"tx_power",tx_power); + field_from_json(Obj,"channel",channel); + field_from_json(Obj,"active_ms",active_ms); + field_from_json(Obj,"busy_ms",busy_ms); + field_from_json(Obj,"receive_ms",receive_ms); + field_from_json(Obj,"mode",mode); + field_from_json(Obj,"ack_signal",ack_signal); + field_from_json(Obj,"ack_signal_avg",ack_signal_avg); + field_from_json(Obj,"connected",connected); + field_from_json(Obj,"inactive",inactive); + field_from_json(Obj,"tx_retries",tx_retries); + return true; + } catch(...) { + + } + return false; + } } \ No newline at end of file diff --git a/src/RESTObjects/RESTAPI_AnalyticsObjects.h b/src/RESTObjects/RESTAPI_AnalyticsObjects.h index 928bd7d..6573aca 100644 --- a/src/RESTObjects/RESTAPI_AnalyticsObjects.h +++ b/src/RESTObjects/RESTAPI_AnalyticsObjects.h @@ -237,7 +237,7 @@ namespace OpenWifi { struct RadioTimePoint { uint64_t band = 0, - radio_channel = 0; + channel_width = 0; uint64_t active_ms = 0, busy_ms = 0, receive_ms = 0, @@ -362,5 +362,60 @@ namespace OpenWifi { void to_json(Poco::JSON::Object &Obj) const; bool from_json(const Poco::JSON::Object::Ptr &Obj); }; + + struct WifiClientRate { + uint32_t bitrate=0; + uint32_t chwidth=0; + uint16_t mcs=0; + uint16_t nss=0; + bool vht=false; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct WifiClientHistory { + uint64_t timestamp=OpenWifi::Now(); + std::string stationId; + std::string bssId; + std::string ssid; + int64_t rssi=0; + uint32_t rx_bitrate=0; + uint32_t rx_chwidth=0; + uint16_t rx_mcs=0; + uint16_t rx_nss=0; + bool rx_vht=false; + uint32_t tx_bitrate=0; + uint32_t tx_chwidth=0; + uint16_t tx_mcs=0; + uint16_t tx_nss=0; + bool tx_vht=false; + uint64_t rx_bytes=0; + uint64_t tx_bytes=0; + uint64_t rx_duration=0; + uint64_t tx_duration=0; + uint64_t rx_packets=0; + uint64_t tx_packets=0; + std::string ipv4; + std::string ipv6; + uint64_t channel_width=0; + int64_t noise=0; + uint64_t tx_power=0; + uint64_t channel=0; + uint64_t active_ms=0; + uint64_t busy_ms=0; + uint64_t receive_ms=0; + std::string mode; + int64_t ack_signal=0; + int64_t ack_signal_avg=0; + uint64_t connected=0; + uint64_t inactive=0; + uint64_t tx_retries=0; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + } + } \ No newline at end of file diff --git a/src/StorageService.cpp b/src/StorageService.cpp index c94ba45..f75a7b8 100644 --- a/src/StorageService.cpp +++ b/src/StorageService.cpp @@ -17,9 +17,13 @@ namespace OpenWifi { StorageClass::Start(); BoardsDB_ = std::make_unique(dbType_,*Pool_, Logger()); - BoardsDB_->Create(); TimePointsDB_ = std::make_unique(dbType_,*Pool_, Logger()); + WifiClientHistoryDB_ = std::make_unique(dbType_,*Pool_, Logger()); + TimePointsDB_->Create(); + BoardsDB_->Create(); + WifiClientHistoryDB_->Create(); + PeriodicCleanup_ = MicroService::instance().ConfigGetInt("storage.cleanup.interval", 6*60*60); if(PeriodicCleanup_<1*60*60) PeriodicCleanup_ = 1*60*60; diff --git a/src/StorageService.h b/src/StorageService.h index 2817926..42ed459 100644 --- a/src/StorageService.h +++ b/src/StorageService.h @@ -12,6 +12,7 @@ #include "framework/StorageClass.h" #include "storage/storage_boards.h" #include "storage/storage_timepoints.h" +#include "storage/storage_wificlients.h" namespace OpenWifi { class Storage : public StorageClass, Poco::Runnable { @@ -25,13 +26,15 @@ namespace OpenWifi { void Stop() override; void run() final; - OpenWifi::BoardsDB & BoardsDB() { return *BoardsDB_; }; - OpenWifi::TimePointDB & TimePointsDB() { return *TimePointsDB_; }; + auto & BoardsDB() { return *BoardsDB_; }; + auto & TimePointsDB() { return *TimePointsDB_; }; + auto & WifiClientHistoryDB() { return *WifiClientHistoryDB_; }; void onTimer(Poco::Timer & timer); private: std::unique_ptr BoardsDB_; std::unique_ptr TimePointsDB_; + std::unique_ptr WifiClientHistoryDB_; Poco::Thread Updater_; std::atomic_bool Running_=false; Poco::Timer Timer_; diff --git a/src/storage/storage_wificlients.cpp b/src/storage/storage_wificlients.cpp new file mode 100644 index 0000000..0f64e6e --- /dev/null +++ b/src/storage/storage_wificlients.cpp @@ -0,0 +1,156 @@ +// +// License type: BSD 3-Clause License +// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE +// +// Created by Stephane Bourque on 2021-03-04. +// Arilia Wireless Inc. +// + +#include "framework/MicroService.h" +#include "storage_wificlients.h" +#include "framework/OpenWifiTypes.h" + +namespace OpenWifi { + + static ORM::FieldVec Boards_Fields{ + ORM::Field{"timestamp",ORM::FieldType::FT_BIGINT}, + ORM::Field{"stationId",ORM::FieldType::FT_TEXT}, + ORM::Field{"bssId",ORM::FieldType::FT_TEXT}, + ORM::Field{"ssid",ORM::FieldType::FT_TEXT}, + ORM::Field{"rssi",ORM::FieldType::FT_BIGINT}, + ORM::Field{"rx_bitrate",ORM::FieldType::FT_BIGINT}, + ORM::Field{"rx_chwidth",ORM::FieldType::FT_BIGINT}, + ORM::Field{"rx_mcs",ORM::FieldType::FT_BIGINT}, + ORM::Field{"rx_nss",ORM::FieldType::FT_BIGINT}, + ORM::Field{"rx_vht",ORM::FieldType::FT_BOOLEAN}, + ORM::Field{"tx_bitrate",ORM::FieldType::FT_BIGINT}, + ORM::Field{"tx_chwidth",ORM::FieldType::FT_BIGINT}, + ORM::Field{"tx_mcs",ORM::FieldType::FT_BIGINT}, + ORM::Field{"tx_nss",ORM::FieldType::FT_BIGINT}, + ORM::Field{"tx_vht",ORM::FieldType::FT_BOOLEAN}, + ORM::Field{"rx_bytes",ORM::FieldType::FT_BIGINT}, + ORM::Field{"tx_bytes",ORM::FieldType::FT_BIGINT}, + ORM::Field{"rx_duration",ORM::FieldType::FT_BIGINT}, + ORM::Field{"tx_duration",ORM::FieldType::FT_BIGINT}, + ORM::Field{"rx_packets",ORM::FieldType::FT_BIGINT}, + ORM::Field{"tx_packets",ORM::FieldType::FT_BIGINT}, + ORM::Field{"ipv4",ORM::FieldType::FT_TEXT}, + ORM::Field{"ipv6",ORM::FieldType::FT_TEXT}, + ORM::Field{"channel_width",ORM::FieldType::FT_BIGINT}, + ORM::Field{"noise",ORM::FieldType::FT_BIGINT}, + ORM::Field{"tx_power",ORM::FieldType::FT_BIGINT}, + ORM::Field{"channel",ORM::FieldType::FT_BIGINT}, + ORM::Field{"active_ms",ORM::FieldType::FT_BIGINT}, + ORM::Field{"busy_ms",ORM::FieldType::FT_BIGINT}, + ORM::Field{"receive_ms",ORM::FieldType::FT_BIGINT}, + ORM::Field{"mode",ORM::FieldType::FT_TEXT}, + ORM::Field{"ack_signal",ORM::FieldType::FT_BIGINT}, + ORM::Field{"ack_signal_avg",ORM::FieldType::FT_BIGINT}, + ORM::Field{"connected",ORM::FieldType::FT_BIGINT}, + ORM::Field{"inactive",ORM::FieldType::FT_BIGINT}, + ORM::Field{"tx_retries",ORM::FieldType::FT_BIGINT} + }; + + static ORM::IndexVec BoardsDB_Indexes{ + { std::string("stationid_name_index"), + ORM::IndexEntryVec{ + {std::string("stationId"), + ORM::Indextype::ASC} } }, + { std::string("stationtsid_name_index"), + ORM::IndexEntryVec{ + {std::string("stationId"), + ORM::Indextype::ASC} , + {std::string("timestamp"), + ORM::Indextype::ASC}} } + }; + + WifiClientHistoryDB::WifiClientHistoryDB( OpenWifi::DBType T, Poco::Data::SessionPool & P, Poco::Logger &L) : + DB(T, "wificlienthistory", Boards_Fields, BoardsDB_Indexes, P, L, "wfh") {} + + bool WifiClientHistoryDB::Upgrade([[maybe_unused]] uint32_t from, uint32_t &to) { + std::vector Statements{ + }; + RunScript(Statements); + to = 2; + return true; + } +} + +template<> void ORM::DB::Convert(const OpenWifi::WifiClientHistoryDBRecordType &In, OpenWifi::AnalyticsObjects::WifiClientHistory &Out) { + Out.timestamp = In.get<0>(); + Out.stationId = In.get<1>(); + Out.bssId= In.get<2>(); + Out.ssid= In.get<3>(); + Out.rssi= In.get<4>(); + Out.rx_bitrate= In.get<5>(); + Out.rx_chwidth= In.get<6>(); + Out.rx_mcs= In.get<7>(); + Out.rx_nss= In.get<8>(); + Out.rx_vht= In.get<9>(); + Out.tx_bitrate= In.get<10>(); + Out.tx_chwidth= In.get<11>(); + Out.tx_mcs= In.get<12>(); + Out.tx_nss= In.get<13>(); + Out.tx_vht= In.get<14>(); + Out.rx_bytes= In.get<15>(); + Out.tx_bytes= In.get<16>(); + Out.rx_duration= In.get<17>(); + Out.tx_duration= In.get<18>(); + Out.rx_packets= In.get<19>(); + Out.tx_packets= In.get<20>(); + Out.ipv4= In.get<21>(); + Out.ipv6= In.get<22>(); + Out.channel_width= In.get<23>(); + Out.noise= In.get<24>(); + Out.tx_power= In.get<25>(); + Out.channel= In.get<26>(); + Out.active_ms= In.get<27>(); + Out.busy_ms= In.get<28>(); + Out.receive_ms= In.get<29>(); + Out.mode= In.get<30>(); + Out.ack_signal= In.get<31>(); + Out.ack_signal_avg= In.get<32>(); + Out.connected= In.get<33>(); + Out.inactive= In.get<34>(); + Out.tx_retries= In.get<35>(); +} + +template<> void ORM::DB::Convert(const OpenWifi::AnalyticsObjects::WifiClientHistory &In, OpenWifi::WifiClientHistoryDBRecordType &Out) { + Out.set<0>(In.timestamp); + Out.set<1>(In.stationId); + Out.set<2>(In.bssId); + Out.set<3>(In.ssid); + Out.set<4>(In.rssi); + Out.set<5>(In.rx_bitrate); + Out.set<6>(In.rx_chwidth); + Out.set<7>(In.rx_mcs); + Out.set<8>(In.rx_nss); + Out.set<9>(In.rx_vht); + Out.set<10>(In.tx_bitrate); + Out.set<11>(In.tx_chwidth); + Out.set<12>(In.tx_mcs); + Out.set<13>(In.tx_nss); + Out.set<14>(In.tx_vht); + Out.set<15>(In.rx_bytes); + Out.set<16>(In.tx_bytes); + Out.set<17>(In.rx_duration); + Out.set<18>(In.tx_duration); + Out.set<19>(In.rx_packets); + Out.set<20>(In.tx_packets); + Out.set<21>(In.ipv4); + Out.set<22>(In.ipv6); + Out.set<23>(In.channel_width); + Out.set<24>(In.noise); + Out.set<25>(In.tx_power); + Out.set<26>(In.channel); + Out.set<27>(In.active_ms); + Out.set<28>(In.busy_ms); + Out.set<29>(In.receive_ms); + Out.set<30>(In.mode); + Out.set<31>(In.ack_signal); + Out.set<32>(In.ack_signal_avg); + Out.set<33>(In.connected); + Out.set<34>(In.inactive); + Out.set<35>(In.tx_retries); + +} diff --git a/src/storage/storage_wificlients.h b/src/storage/storage_wificlients.h new file mode 100644 index 0000000..aba8300 --- /dev/null +++ b/src/storage/storage_wificlients.h @@ -0,0 +1,57 @@ +// +// Created by stephane bourque on 2022-05-15. +// + +#pragma once + +#include "framework/orm.h" +#include "RESTObjects/RESTAPI_AnalyticsObjects.h" + +namespace OpenWifi { + typedef Poco::Tuple< + uint64_t, // timestamp=OpenWifi::Now(); + std::string, // stationId; + std::string, // bssId; + std::string, // ssid; + int64_t, // rssi=0; + uint32_t, // rx_bitrate=0; + uint32_t, // rx_chwidth=0; + uint16_t, // rx_mcs=0; + uint16_t, // rx_nss=0; + bool, // rx_vht=false; + uint32_t, // tx_bitrate=0; + uint32_t, // tx_chwidth=0; + uint16_t, // tx_mcs=0; + uint16_t, // tx_nss=0; + bool, // tx_vht=false; + uint64_t, // rx_bytes=0, + uint64_t, // tx_bytes=0; + int64_t, // rx_duration=0, + uint64_t, // tx_duration=0; + uint64_t, // rx_packets=0, + uint64_t, // tx_packets=0; + std::string, // ipv4; + std::string, // ipv6; + uint64_t, // channel_width=0; + int64_t, // noise=0; + uint64_t, // tx_power=0; + uint64_t, // channel=0; + uint64_t, // active_ms=0, + uint64_t, // busy_ms=0, + uint64_t, // receive_ms=0; + std::string, // mode; + int64_t, // ack_signal=0; + int64_t, // ack_signal_avg=0; + int64_t, // connected=0; + int64_t, // inactive=0; + int64_t // tx_retries=0; + > WifiClientHistoryDBRecordType; + + class WifiClientHistoryDB : public ORM::DB { + public: + WifiClientHistoryDB( OpenWifi::DBType T, Poco::Data::SessionPool & P, Poco::Logger &L); + virtual ~WifiClientHistoryDB() {}; + private: + bool Upgrade(uint32_t from, uint32_t &to) override; + }; +}