From 1286e90511dd6308a3c30010c464eb434d5cec7e Mon Sep 17 00:00:00 2001 From: stephb9959 Date: Thu, 10 Mar 2022 22:51:41 -0800 Subject: [PATCH] Initial commit --- CMakeLists.txt | 2 +- build | 2 +- openapi/owanalytics.yaml | 586 +++++++++++++++++++++++++ src/Daemon.cpp | 4 +- src/RESTObjects/RESTAPI_SubObjects.cpp | 565 ++++++++++++++++++++++++ src/RESTObjects/RESTAPI_SubObjects.h | 303 +++++++++++++ src/StateReceiver.cpp | 90 ++++ src/StateReceiver.h | 54 +++ src/VenueCoordinator.cpp | 47 ++ src/VenueCoordinator.h | 35 ++ src/VenueWatcher.cpp | 66 +++ src/VenueWatcher.h | 57 +++ src/framework/MicroService.h | 8 +- 13 files changed, 1811 insertions(+), 8 deletions(-) create mode 100644 openapi/owanalytics.yaml create mode 100644 src/RESTObjects/RESTAPI_SubObjects.cpp create mode 100644 src/RESTObjects/RESTAPI_SubObjects.h create mode 100644 src/StateReceiver.cpp create mode 100644 src/StateReceiver.h create mode 100644 src/VenueCoordinator.cpp create mode 100644 src/VenueCoordinator.h create mode 100644 src/VenueWatcher.cpp create mode 100644 src/VenueWatcher.h diff --git a/CMakeLists.txt b/CMakeLists.txt index af0c63f..0193190 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,7 +83,7 @@ add_executable(owanalytics src/RESTAPI/RESTAPI_routers.cpp src/Daemon.cpp src/Daemon.h src/Dashboard.h src/Dashboard.cpp - src/StorageService.cpp src/StorageService.h src/RESTObjects/RESTAPI_AnalyticsObjects.cpp src/RESTObjects/RESTAPI_AnalyticsObjects.h) + src/StorageService.cpp src/StorageService.h src/RESTObjects/RESTAPI_AnalyticsObjects.cpp src/RESTObjects/RESTAPI_AnalyticsObjects.h src/StateReceiver.cpp src/StateReceiver.h src/VenueWatcher.cpp src/VenueWatcher.h src/VenueCoordinator.cpp src/VenueCoordinator.h) target_link_libraries(owanalytics PUBLIC ${Poco_LIBRARIES} ${MySQL_LIBRARIES} diff --git a/build b/build index e440e5c..62f9457 100644 --- a/build +++ b/build @@ -1 +1 @@ -3 \ No newline at end of file +6 \ No newline at end of file diff --git a/openapi/owanalytics.yaml b/openapi/owanalytics.yaml new file mode 100644 index 0000000..13d686c --- /dev/null +++ b/openapi/owanalytics.yaml @@ -0,0 +1,586 @@ +openapi: 3.0.1 +info: + title: OpenWiFi Analytics Service + description: Definitions and APIs to analyze OpenWiFi network. + version: 2.6.0 + license: + name: BSD3 + url: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE + +servers: + - url: 'https://localhost:16009/api/v1' + +security: + - bearerAuth: [] + - ApiKeyAuth: [] + +components: + securitySchemes: + ApiKeyAuth: + type: apiKey + in: header + name: X-API-KEY + bearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + + responses: + NotFound: + description: The specified resource was not found. + content: + application/json: + schema: + properties: + ErrorCode: + type: integer + ErrorDetails: + type: string + ErrorDescription: + type: string + + Unauthorized: + description: The requested does not have sufficient rights to perform the operation. + content: + application/json: + schema: + properties: + ErrorCode: + type: integer + enum: + - 0 # Success + - 1 # PASSWORD_CHANGE_REQUIRED, + - 2 # INVALID_CREDENTIALS, + - 3 # PASSWORD_ALREADY_USED, + - 4 # USERNAME_PENDING_VERIFICATION, + - 5 # PASSWORD_INVALID, + - 6 # INTERNAL_ERROR, + - 7 # ACCESS_DENIED, + - 8 # INVALID_TOKEN + - 9 # EXPIRED_TOKEN + - 10 # RATE_LIMIT_EXCEEDED + - 11 # BAD_MFA_TRANSACTION + - 12 # MFA_FAILURE + - 13 # SECURITY_SERVICE_UNREACHABLE + ErrorDetails: + type: string + ErrorDescription: + type: string + + Success: + description: The requested operation was performed. + content: + application/json: + schema: + properties: + Operation: + type: string + Details: + type: string + Code: + type: integer + + BadRequest: + description: The requested operation failed. + content: + application/json: + schema: + properties: + ErrorCode: + type: integer + ErrorDetails: + type: string + ErrorDescription: + type: integer + + schemas: + ObjectInfo: + type: object + properties: + id: + type: string + format: uuid + name: + type: string + description: + type: string + notes: + type: array + items: + $ref: '#/components/schemas/NoteInfo' + created: + type: integer + format: int64 + modified: + type: integer + format: int64 + tags: + type: array + items: + type: integer + format: int64 + + VenueInfo: + type: object + properties: + id: + type: string + format: uuid + name: + type: string + description: + type: string + retention: + type: integer + interval: + type: integer + monitorSubVenues: + type: boolean + + BoardInfo: + type: object + properties: + allOf: + $ref: '#/components/schemas/ObjectInfo' + venueList: + type: array + items: + $ref: '#/components/schemas/VenueInfo' + + BoardInfoList: + type: object + properties: + boards: + type: array + items: + $ref: '#/components/schemas/BoardInfo' + + ######################################################################################### + ## + ## These are endpoints that all services in the OPenWiFI stack must provide + ## + ######################################################################################### + AnyPayload: + type: object + properties: + Document: + type: string + + StringList: + type: object + properties: + list: + type: array + items: + type: string + + TagValuePair: + type: object + properties: + tag: + type: string + value: + type: string + + TagValuePairList: + type: object + properties: + tagList: + type: array + items: + $ref: '#/components/schemas/TagValuePair' + + TagIntPair: + type: object + properties: + tag: + type: string + value: + type: integer + format: int64 + + TagIntPairList: + type: object + properties: + tagList: + type: array + items: + $ref: '#/components/schemas/TagIntPair' + + SystemCommandDetails: + type: object + properties: + command: + type: string + enum: + - setloglevels + - getloglevels + - getSubSystemNames + - getLogLevelNames + - stats + parameters: + oneOf: + - $ref: '#/components/schemas/StringList' + - $ref: '#/components/schemas/TagValuePairList' + + SystemCommandResults: + type: object + oneOf: + - $ref: '#/components/schemas/StringList' + - $ref: '#/components/schemas/TagValuePairList' + + NoteInfo: + type: object + properties: + created: + type: integer + format: int64 + createdBy: + type: string + note: + type: string + + SystemInfoResults: + type: object + properties: + version: + type: string + uptime: + type: integer + format: integer64 + start: + type: integer + format: integer64 + os: + type: string + processors: + type: integer + hostname: + type: string + certificates: + type: array + items: + type: object + properties: + filename: + type: string + expires: + type: integer + format: int64 + + Dashboard: + type: object + properties: + snapshot: + type: integer + format: int64 + tenants: + $ref: '#/components/schemas/TagIntPairList' + + SystemCommandSetLogLevel: + type: object + properties: + command: + type: string + enum: + - setloglevel + subsystems: + type: array + items: + $ref: '#/components/schemas/TagValuePair' + + SystemCommandReload: + type: object + properties: + command: + type: string + enum: + - reload + subsystems: + type: array + items: + type: string + example: these are the SubSystems names retrieve with the GetSubSystemsNamesResult. + + SystemCommandGetLogLevels: + type: object + properties: + command: + type: string + enum: + - getloglevels + + SystemGetLogLevelsResult: + type: object + properties: + taglist: + type: array + items: + $ref: '#/components/schemas/TagValuePair' + + SystemCommandGetLogLevelNames: + type: object + properties: + command: + type: string + enum: + - getloglevelnames + + SystemCommandGetSubsystemNames: + type: object + properties: + command: + type: string + enum: + - getsubsystemnames + + SystemCommandGetLogLevelNamesResult: + type: object + properties: + list: + type: array + items: + type: string + + SystemGetSubSystemNamesResult: + type: object + properties: + taglist: + type: array + items: + $ref: '#/components/schemas/TagValuePair' + +paths: + /boards: + get: + tags: + - Boards + operationId: getBoards + summary: Retrieve a list of boards. + parameters: + - 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 + - in: query + description: Filter the results + name: filter + schema: + type: string + required: false + - in: query + description: return the number of boards + name: countOnly + schema: + type: boolean + required: false + + responses: + 200: + description: Return a list of boards + content: + application/json: + schema: + $ref: '#/components/schemas/BoardInfoList' + 403: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + + /board/{id}: + get: + tags: + - Boards + operationId: getBoard + summary: Retrieve a board + parameters: + - in: path + name: id + schema: + type: string + format: uuid + required: true + responses: + 200: + description: Return a list of boards + content: + application/json: + schema: + $ref: '#/components/schemas/BoardInfo' + 403: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + delete: + tags: + - Boards + operationId: deleteBoard + summary: Remove a board + parameters: + - in: path + name: id + schema: + type: string + format: uuid + required: true + responses: + 200: + $ref: '#/components/responses/Success' + 403: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + post: + tags: + - Boards + operationId: createBoard + summary: Create a board + parameters: + - in: path + name: id + schema: + type: string + format: uuid + required: true + example: value should be 0 for a post + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/BoardInfo' + responses: + 200: + $ref: '#/components/schemas/BoardInfo' + 403: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + put: + tags: + - Boards + operationId: modifyBoard + summary: Modify a board + parameters: + - in: path + name: id + schema: + type: string + format: uuid + required: true + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/BoardInfo' + responses: + 200: + $ref: '#/components/schemas/BoardInfo' + 403: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + + /iptocountry: + get: + tags: + - Utility + summary: Get the country code for an IP address + operationId: getIpToCountry + parameters: + - in: query + name: iplist + schema: + type: string + example: + 10.2.2.2,10.3.4.3 + required: true + responses: + 200: + description: List of country codes. + content: + application/json: + schema: + type: object + properties: + enabled: + type: boolean + countryCodes: + type: array + items: + type: string + 403: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + + ######################################################################################### + ## + ## These are endpoints that all services in the OpenWiFi stack must provide + ## + ######################################################################################### + /system: + post: + tags: + - System Commands + summary: Perform some system wide commands. + operationId: systemCommand + requestBody: + description: Command details + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/SystemCommandSetLogLevel' + - $ref: '#/components/schemas/SystemCommandReload' + - $ref: '#/components/schemas/SystemCommandGetLogLevels' + - $ref: '#/components/schemas/SystemCommandGetLogLevelNames' + - $ref: '#/components/schemas/SystemCommandGetSubsystemNames' + responses: + 200: + description: Successful command execution + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/SystemGetLogLevelsResult' + - $ref: '#/components/schemas/SystemCommandGetLogLevelNamesResult' + - $ref: '#/components/schemas/SystemGetSubSystemNamesResult' + 403: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + get: + tags: + - System Commands + summary: Retrieve different values from the running service. + operationId: getSystemCommand + parameters: + - in: query + description: Get a value + name: command + schema: + type: string + enum: + - info + required: true + + responses: + 200: + description: Successful command execution + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/SystemInfoResults' + 403: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' diff --git a/src/Daemon.cpp b/src/Daemon.cpp index ca576b0..28fe61f 100644 --- a/src/Daemon.cpp +++ b/src/Daemon.cpp @@ -13,6 +13,7 @@ #include "Daemon.h" #include "StorageService.h" +#include "VenueCoordinator.h" namespace OpenWifi { class Daemon *Daemon::instance_ = nullptr; @@ -25,7 +26,8 @@ namespace OpenWifi { vDAEMON_APP_NAME, vDAEMON_BUS_TIMER, SubSystemVec{ - OpenWifi::StorageService() + OpenWifi::StorageService(), + VenueCoordinator() }); } return instance_; diff --git a/src/RESTObjects/RESTAPI_SubObjects.cpp b/src/RESTObjects/RESTAPI_SubObjects.cpp new file mode 100644 index 0000000..61d92e8 --- /dev/null +++ b/src/RESTObjects/RESTAPI_SubObjects.cpp @@ -0,0 +1,565 @@ +// +// Created by stephane bourque on 2021-10-27. +// + +#include "RESTAPI_SubObjects.h" +#include "framework/MicroService.h" + +using OpenWifi::RESTAPI_utils::field_to_json; +using OpenWifi::RESTAPI_utils::field_from_json; + + +namespace OpenWifi::SubObjects { + + void HomeDeviceMode::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "enableLEDS", enableLEDS); + field_to_json(Obj, "type", type); + field_to_json(Obj, "subnet", subnet); + field_to_json(Obj, "subnetMask", subnetMask); + field_to_json(Obj, "startIP", startIP); + field_to_json(Obj, "endIP", endIP); + field_to_json(Obj, "created", created); + field_to_json(Obj, "modified", modified); + field_to_json(Obj, "subnetV6", subnetV6); + field_to_json(Obj, "subnetMaskV6", subnetMaskV6); + field_to_json(Obj, "startIPV6", startIPV6); + field_to_json(Obj, "endIPV6", endIPV6); + } + + bool HomeDeviceMode::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "enableLEDS", enableLEDS); + field_from_json(Obj, "type", type); + field_from_json(Obj, "subnet", subnet); + field_from_json(Obj, "subnetMask", subnetMask); + field_from_json(Obj, "startIP", startIP); + field_from_json(Obj, "endIP", endIP); + field_from_json(Obj, "created", created); + field_from_json(Obj, "modified", modified); + field_from_json(Obj, "subnetV6", subnetV6); + field_from_json(Obj, "subnetMaskV6", subnetMaskV6); + field_from_json(Obj, "startIPV6", startIPV6); + field_from_json(Obj, "endIPV6", endIPV6); + return true; + } catch (...) { + } + return false; + } + + void IPReservation::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "nickname", nickname); + field_to_json(Obj, "ipAddress", ipAddress); + field_to_json(Obj, "macAddress", macAddress); + } + + bool IPReservation::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "nickname", nickname); + field_from_json(Obj, "ipAddress", ipAddress); + field_from_json(Obj, "macAddress", macAddress); + return true; + } catch (...) { + } + return false; + } + + void IPReservationList::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "id", id); + field_to_json(Obj, "reservations", reservations); + field_to_json(Obj, "created", created); + field_to_json(Obj, "modified", modified); + } + + bool IPReservationList::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "id", id); + field_from_json(Obj, "reservations", reservations); + field_from_json(Obj, "created", created); + field_from_json(Obj, "modified", modified); + return true; + } catch (...) { + } + return false; + } + + void DnsConfiguration::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "ISP", ISP); + field_to_json(Obj, "custom", custom); + field_to_json(Obj, "primary", primary); + field_to_json(Obj, "secondary", secondary); + field_to_json(Obj, "primaryV6", primaryV6); + field_to_json(Obj, "secondaryV6", secondaryV6); + } + + bool DnsConfiguration::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "ISP", ISP); + field_from_json(Obj, "custom", custom); + field_from_json(Obj, "primary", primary); + field_from_json(Obj, "secondary", secondary); + field_from_json(Obj, "primaryV6", primaryV6); + field_from_json(Obj, "secondaryV6", secondaryV6); + return true; + } catch (...) { + } + return false; + } + + void InternetConnection::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "type", type); + field_to_json(Obj, "username", username); + field_to_json(Obj, "password", password); + field_to_json(Obj, "ipAddress", ipAddress); + field_to_json(Obj, "subnetMask", subnetMask); + field_to_json(Obj, "defaultGateway", defaultGateway); + field_to_json(Obj, "sendHostname", sendHostname); + field_to_json(Obj, "primaryDns", primaryDns); + field_to_json(Obj, "secondaryDns", secondaryDns); + field_to_json(Obj, "created", created); + field_to_json(Obj, "modified", modified); + field_to_json(Obj, "ipV6Support", ipV6Support); + field_to_json(Obj, "ipAddressV6", ipAddressV6); + field_to_json(Obj, "subnetMaskV6", subnetMaskV6); + field_to_json(Obj, "defaultGatewayV6", defaultGatewayV6); + field_to_json(Obj, "primaryDnsV6", primaryDnsV6); + field_to_json(Obj, "secondaryDnsV6", secondaryDnsV6); + } + + bool InternetConnection::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "type", type); + field_from_json(Obj, "username", username); + field_from_json(Obj, "password", password); + field_from_json(Obj, "ipAddress", ipAddress); + field_from_json(Obj, "subnetMask", subnetMask); + field_from_json(Obj, "defaultGateway", defaultGateway); + field_from_json(Obj, "sendHostname", sendHostname); + field_from_json(Obj, "primaryDns", primaryDns); + field_from_json(Obj, "secondaryDns", secondaryDns); + field_from_json(Obj, "created", created); + field_from_json(Obj, "modified", modified); + field_from_json(Obj, "ipV6Support", ipV6Support); + field_from_json(Obj, "ipAddressV6", ipAddressV6); + field_from_json(Obj, "subnetMaskV6", subnetMaskV6); + field_from_json(Obj, "defaultGatewayV6", defaultGatewayV6); + field_from_json(Obj, "primaryDnsV6", primaryDnsV6); + field_from_json(Obj, "secondaryDnsV6", secondaryDnsV6); + return true; + } catch (...) { + } + return false; + } + + void WifiNetwork::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "type", type); + field_to_json(Obj, "name", name); + field_to_json(Obj, "password", password); + field_to_json(Obj, "encryption", encryption); + field_to_json(Obj, "bands", bands); + } + + bool WifiNetwork::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "type", type); + field_from_json(Obj, "name", name); + field_from_json(Obj, "password", password); + field_from_json(Obj, "encryption", encryption); + field_from_json(Obj, "bands", bands); + return true; + } catch (...) { + } + return false; + } + + void WifiNetworkList::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "wifiNetworks", wifiNetworks); + field_to_json(Obj, "created", created); + field_to_json(Obj, "modified", modified); + } + + bool WifiNetworkList::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "wifiNetworks", wifiNetworks); + field_from_json(Obj, "created", created); + field_from_json(Obj, "modified", modified); + return true; + } catch (...) { + } + return false; + } + + void AccessTime::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "day", day); + field_to_json(Obj, "rangeList", rangeList); + } + + bool AccessTime::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "day", day); + field_from_json(Obj, "rangeList", rangeList); + return true; + } catch (...) { + } + return false; + } + + void AccessTimes::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "schedule", schedule); + field_to_json(Obj, "created", created); + field_to_json(Obj, "modified", modified); + } + + bool AccessTimes::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "schedule", schedule); + field_from_json(Obj, "created", created); + field_from_json(Obj, "modified", modified); + return true; + } catch (...) { + } + return false; + } + + void SubscriberDevice::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "name", name); + field_to_json(Obj, "description", description); + field_to_json(Obj, "macAddress", macAddress); + field_to_json(Obj, "manufacturer", manufacturer); + field_to_json(Obj, "firstContact", firstContact); + field_to_json(Obj, "lastContact", lastContact); + field_to_json(Obj, "group", group); + field_to_json(Obj, "icon", icon); + field_to_json(Obj, "suspended", suspended); + field_to_json(Obj, "ip", ip); + field_to_json(Obj, "schedule", schedule); + } + + bool SubscriberDevice::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "name", name); + field_from_json(Obj, "description", description); + field_from_json(Obj, "macAddress", macAddress); + field_from_json(Obj, "manufacturer", manufacturer); + field_from_json(Obj, "firstContact", firstContact); + field_from_json(Obj, "lastContact", lastContact); + field_from_json(Obj, "group", group); + field_from_json(Obj, "icon", icon); + field_from_json(Obj, "suspended", suspended); + field_from_json(Obj, "ip", ip); + field_from_json(Obj, "schedule", schedule); + return true; + } catch (...) { + } + return false; + } + + void SubscriberDeviceList::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "devices", devices); + field_to_json(Obj, "created", created); + field_to_json(Obj, "modified", modified); + } + + bool SubscriberDeviceList::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "devices", devices); + field_from_json(Obj, "created", created); + field_from_json(Obj, "modified", modified); + return true; + } catch (...) { + } + return false; + } + + void Association::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "name", name); + field_to_json(Obj, "ssid", ssid); + field_to_json(Obj, "macAddress", macAddress); + field_to_json(Obj, "rssi", rssi); + field_to_json(Obj, "power", power); + field_to_json(Obj, "ipv4", ipv4); + field_to_json(Obj, "ipv6", ipv6); + field_to_json(Obj, "tx", tx); + field_to_json(Obj, "rx", rx); + } + + bool Association::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "name", name); + field_from_json(Obj, "ssid", ssid); + field_from_json(Obj, "macAddress", macAddress); + field_from_json(Obj, "rssi", rssi); + field_from_json(Obj, "power", power); + field_from_json(Obj, "ipv4", ipv4); + field_from_json(Obj, "ipv6", ipv6); + field_from_json(Obj, "tx", tx); + field_from_json(Obj, "rx", rx); + return true; + } catch (...) { + } + return false; + } + + void AssociationList::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "associations", associations); + field_to_json(Obj, "created", created); + field_to_json(Obj, "modified", modified); + } + + bool AssociationList::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "associations", associations); + field_from_json(Obj, "created", created); + field_from_json(Obj, "modified", modified); + return true; + } catch (...) { + } + return false; + } + + void Client::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "macAddress", macAddress); + field_to_json(Obj, "speed", speed); + field_to_json(Obj, "mode", mode); + field_to_json(Obj, "ipv4", ipv4); + field_to_json(Obj, "ipv6", ipv6); + field_to_json(Obj, "tx", tx); + field_to_json(Obj, "rx", rx); + } + + bool Client::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "macAddress", macAddress); + field_from_json(Obj, "speed", speed); + field_from_json(Obj, "mode", mode); + field_from_json(Obj, "ipv4", ipv4); + field_from_json(Obj, "ipv6", ipv6); + field_from_json(Obj, "tx", tx); + field_from_json(Obj, "rx", rx); + return true; + } catch (...) { + } + return false; + } + + void ClientList::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "clients", clients); + field_to_json(Obj, "created", created); + field_to_json(Obj, "modified", modified); + } + + bool ClientList::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "clients", clients); + field_from_json(Obj, "created", created); + field_from_json(Obj, "modified", modified); + return true; + } catch (...) { + } + return false; + } + + void Location::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "buildingName", buildingName); + field_to_json(Obj, "addressLines", addressLines); + field_to_json(Obj, "city", city); + field_to_json(Obj, "state", state); + field_to_json(Obj, "postal", postal); + field_to_json(Obj, "country", country); + field_to_json(Obj, "phones", phones); + field_to_json(Obj, "mobiles", mobiles); + } + + bool Location::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "buildingName", buildingName); + field_from_json(Obj, "addressLines", addressLines); + field_from_json(Obj, "city", city); + field_from_json(Obj, "state", state); + field_from_json(Obj, "postal", postal); + field_from_json(Obj, "country", country); + field_from_json(Obj, "phones", phones); + field_from_json(Obj, "mobiles", mobiles); + return true; + } catch (...) { + } + return false; + } + + void RadioHE::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "multipleBSSID", multipleBSSID); + field_to_json(Obj, "ema", ema); + field_to_json(Obj, "bssColor", bssColor); + } + + bool RadioHE::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "multipleBSSID", multipleBSSID); + field_from_json(Obj, "ema", ema); + field_from_json(Obj, "bssColor", bssColor); + return true; + } catch (...) { + } + return false; + } + + void RadioRates::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "beacon", beacon); + field_to_json(Obj, "multicast", multicast); + } + + bool RadioRates::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "beacon", beacon); + field_from_json(Obj, "multicast", multicast); + return true; + } catch (...) { + } + return false; + } + + void RadioInformation::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "band", band); + field_to_json(Obj, "bandwidth", bandwidth); + field_to_json(Obj, "channel", channel); + field_to_json(Obj, "country", country); + field_to_json(Obj, "channelMode", channelMode); + field_to_json(Obj, "channelWidth", channelWidth); + field_to_json(Obj, "requireMode", requireMode); + field_to_json(Obj, "txpower", txpower); + field_to_json(Obj, "legacyRates", legacyRates); + field_to_json(Obj, "beaconInterval", beaconInterval); + field_to_json(Obj, "dtimPeriod", dtimPeriod); + field_to_json(Obj, "maximumClients", maximumClients); + field_to_json(Obj, "rates", rates); + field_to_json(Obj, "he", he); + field_to_json(Obj, "rawInfo", rawInfo); + field_to_json(Obj, "allowDFS", allowDFS); + field_to_json(Obj, "mimo", mimo); + } + + bool RadioInformation::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "band", band); + field_from_json(Obj, "bandwidth", bandwidth); + field_from_json(Obj, "channel", channel); + field_from_json(Obj, "country", country); + field_from_json(Obj, "channelMode", channelMode); + field_from_json(Obj, "channelWidth", channelWidth); + field_from_json(Obj, "requireMode", requireMode); + field_from_json(Obj, "txpower", txpower); + field_from_json(Obj, "legacyRates", legacyRates); + field_from_json(Obj, "beaconInterval", beaconInterval); + field_from_json(Obj, "dtimPeriod", dtimPeriod); + field_from_json(Obj, "maximumClients", maximumClients); + field_from_json(Obj, "rates", rates); + field_from_json(Obj, "he", he); + field_from_json(Obj, "rawInfo", rawInfo); + field_from_json(Obj, "allowDFS", allowDFS); + field_from_json(Obj, "mimo", mimo); + return true; + } catch (...) { + } + return false; + } + + void AccessPoint::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "id", id); + field_to_json(Obj, "macAddress", macAddress); + field_to_json(Obj, "serialNumber", serialNumber); + field_to_json(Obj, "name", name); + field_to_json(Obj, "deviceType", deviceType); + field_to_json(Obj, "subscriberDevices", subscriberDevices); + field_to_json(Obj, "ipReservations", ipReservations); + field_to_json(Obj, "address", address); + field_to_json(Obj, "wifiNetworks", wifiNetworks); + field_to_json(Obj, "internetConnection", internetConnection); + field_to_json(Obj, "deviceMode", deviceMode); + field_to_json(Obj, "dnsConfiguration", dnsConfiguration); + field_to_json(Obj, "radios", radios); + field_to_json(Obj, "automaticUpgrade", automaticUpgrade); + field_to_json(Obj, "configurationUUID", configurationUUID); + field_to_json(Obj, "currentFirmware", currentFirmware); + field_to_json(Obj, "currentFirmwareDate", currentFirmwareDate); + field_to_json(Obj, "latestFirmware", latestFirmware); + field_to_json(Obj, "latestFirmwareDate", latestFirmwareDate); + field_to_json(Obj, "newFirmwareAvailable", newFirmwareAvailable); + field_to_json(Obj, "latestFirmwareURI", latestFirmwareURI); + } + + bool AccessPoint::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "id", id); + field_from_json(Obj, "macAddress", macAddress); + field_from_json(Obj, "serialNumber", serialNumber); + field_from_json(Obj, "name", name); + field_from_json(Obj, "deviceType", deviceType); + field_from_json(Obj, "subscriberDevices", subscriberDevices); + field_from_json(Obj, "ipReservations", ipReservations); + field_from_json(Obj, "address", address); + field_from_json(Obj, "wifiNetworks", wifiNetworks); + field_from_json(Obj, "internetConnection", internetConnection); + field_from_json(Obj, "deviceMode", deviceMode); + field_from_json(Obj, "dnsConfiguration", dnsConfiguration); + field_from_json(Obj, "radios", radios); + field_from_json(Obj, "automaticUpgrade", automaticUpgrade); + field_from_json(Obj, "configurationUUID", configurationUUID); + field_from_json(Obj, "currentFirmware", currentFirmware); + field_from_json(Obj, "currentFirmwareDate", currentFirmwareDate); + field_from_json(Obj, "latestFirmware", latestFirmware); + field_from_json(Obj, "latestFirmwareDate", latestFirmwareDate); + field_from_json(Obj, "newFirmwareAvailable", newFirmwareAvailable); + field_from_json(Obj, "latestFirmwareURI", latestFirmwareURI); + return true; + } catch (...) { + } + return false; + } + + void AccessPointList::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "list", list); + } + + bool AccessPointList::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "list", list); + return true; + } catch (...) { + } + return false; + } + + void SubscriberInfo::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "id", id); + field_to_json(Obj, "userId", userId); + field_to_json(Obj, "firstName", firstName); + field_to_json(Obj, "initials", initials); + field_to_json(Obj, "lastName", lastName); + field_to_json(Obj, "phoneNumber", phoneNumber); + field_to_json(Obj, "secondaryEmail", secondaryEmail); + field_to_json(Obj, "accessPoints", accessPoints); + field_to_json(Obj, "serviceAddress", serviceAddress); + field_to_json(Obj, "billingAddress", billingAddress); + field_to_json(Obj, "created", created); + field_to_json(Obj, "modified", modified); + } + + bool SubscriberInfo::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "id", id); + field_from_json(Obj, "userId", userId); + field_from_json(Obj, "firstName", firstName); + field_from_json(Obj, "initials", initials); + field_from_json(Obj, "lastName", lastName); + field_from_json(Obj, "phoneNumber", phoneNumber); + field_from_json(Obj, "secondaryEmail", secondaryEmail); + field_from_json(Obj, "accessPoints", accessPoints); + field_from_json(Obj, "serviceAddress", serviceAddress); + field_from_json(Obj, "billingAddress", billingAddress); + field_from_json(Obj, "created", created); + field_from_json(Obj, "modified", modified); + return true; + } catch (...) { + } + return false; + } +} \ No newline at end of file diff --git a/src/RESTObjects/RESTAPI_SubObjects.h b/src/RESTObjects/RESTAPI_SubObjects.h new file mode 100644 index 0000000..4eed396 --- /dev/null +++ b/src/RESTObjects/RESTAPI_SubObjects.h @@ -0,0 +1,303 @@ +// +// Created by stephane bourque on 2021-10-27. +// + +#ifndef OWSUB_RESTAPI_SUBOBJECTS_H +#define OWSUB_RESTAPI_SUBOBJECTS_H + +#include + +#include "Poco/JSON/Object.h" + +namespace OpenWifi::SubObjects { + + struct HomeDeviceMode { + bool enableLEDS = true; + std::string type; // bridge, manual, automatic + std::string subnet; + std::string subnetMask; + std::string startIP; + std::string endIP; + uint64_t created = 0 ; + uint64_t modified = 0 ; + std::string subnetV6; + int subnetMaskV6=0; + std::string startIPV6; + std::string endIPV6; + std::string leaseTime; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct IPReservation { + std::string nickname; + std::string ipAddress; + std::string macAddress; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct IPReservationList { + std::string id; + std::vector reservations; + uint64_t created = 0 ; + uint64_t modified = 0 ; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct DnsConfiguration { + bool ISP=false; + bool custom=false; + std::string primary; + std::string secondary; + std::string primaryV6; + std::string secondaryV6; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct InternetConnection { + std::string type; // automatic, pppoe, manual + std::string username; + std::string password; + std::string ipAddress; + std::string subnetMask; + std::string defaultGateway; + bool sendHostname = true; + std::string primaryDns; + std::string secondaryDns; + uint64_t created=0; + uint64_t modified=0; + bool ipV6Support=false; + std::string ipAddressV6; + int subnetMaskV6=0; + std::string defaultGatewayV6; + std::string primaryDnsV6; + std::string secondaryDnsV6; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct WifiNetwork { + std::string type; // main, guest + std::string name; + std::string password; + std::string encryption; + std::vector bands; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct WifiNetworkList { + std::vector wifiNetworks; + uint64_t created=0; + uint64_t modified=0; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct AccessTime { + std::string day; + std::vector rangeList; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct AccessTimes { + std::vector schedule; + uint64_t created=0; + uint64_t modified=0; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct SubscriberDevice { + std::string name; + std::string description; + std::string macAddress; + std::string manufacturer; + uint64_t firstContact=0; + uint64_t lastContact=0; + std::string group; + std::string icon; + bool suspended=false; + std::string ip; + std::vector schedule; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct SubscriberDeviceList { + std::vector devices; + uint64_t created=0; + uint64_t modified=0; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct Association { + std::string name; + std::string ssid; + std::string macAddress; + int rssi=0; + int power=0; + std::string ipv4; + std::string ipv6; + uint64_t tx=0; + uint64_t rx=0; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct AssociationList { + std::vector associations; + uint64_t created=0; + uint64_t modified=0; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct Client { + std::string macAddress; + std::string speed; + std::string mode; + std::string ipv4; + std::string ipv6; + uint64_t tx=0; + uint64_t rx=0; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct ClientList { + std::vector clients; + uint64_t created=0; + uint64_t modified=0; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct Location { + std::string buildingName; + std::vector addressLines; + std::string city; + std::string state; + std::string postal; + std::string country; + std::vector phones; + std::vector mobiles; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct RadioHE { + bool multipleBSSID = false; + bool ema = false; + uint64_t bssColor = 64; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct RadioRates { + uint64_t beacon = 6000; + uint64_t multicast = 24000; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct RadioInformation { + std::string band; + uint64_t bandwidth; + uint64_t channel = 0 ; + std::string country; + std::string channelMode{"HE"}; + uint64_t channelWidth = 80; + std::string requireMode; + uint64_t txpower=0; + bool legacyRates = false; + uint64_t beaconInterval = 100; + uint64_t dtimPeriod = 2; + uint64_t maximumClients = 64; + RadioRates rates; + RadioHE he; + bool allowDFS=false; + std::string mimo; + std::vector rawInfo; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct AccessPoint { + std::string id; + std::string macAddress; + std::string serialNumber; + std::string name; + std::string deviceType; + SubscriberDeviceList subscriberDevices; + IPReservationList ipReservations; + Location address; + WifiNetworkList wifiNetworks; + InternetConnection internetConnection; + HomeDeviceMode deviceMode; + DnsConfiguration dnsConfiguration; + std::vector radios; + bool automaticUpgrade = true; + std::string configurationUUID; + std::string currentFirmware; + uint64_t currentFirmwareDate; + std::string latestFirmware; + uint64_t latestFirmwareDate; + bool newFirmwareAvailable; + std::string latestFirmwareURI; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct AccessPointList { + std::vector list; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct SubscriberInfo { + std::string id; + std::string userId; + std::string firstName; + std::string initials; + std::string lastName; + std::string phoneNumber; + std::string secondaryEmail; + AccessPointList accessPoints; + Location serviceAddress; + Location billingAddress; + uint64_t created = 0; + uint64_t modified = 0; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; +} + +#endif //OWSUB_RESTAPI_SUBOBJECTS_H diff --git a/src/StateReceiver.cpp b/src/StateReceiver.cpp new file mode 100644 index 0000000..f65c04a --- /dev/null +++ b/src/StateReceiver.cpp @@ -0,0 +1,90 @@ +// +// Created by stephane bourque on 2022-03-10. +// + +#include "StateReceiver.h" +#include "VenueWatcher.h" + +namespace OpenWifi { + + int StateReceiver::Start() { + Running_ = true; + Types::TopicNotifyFunction F = [this](const std::string &Key, const std::string &Payload) { this->StateReceived(Key,Payload); }; + StateWatcherId_ = KafkaManager()->RegisterTopicWatcher(KafkaTopics::STATE, F); + Worker_.start(*this); + return 0; + }; + + void StateReceiver::Stop() { + Running_ = false; + KafkaManager()->UnregisterTopicWatcher(KafkaTopics::STATE, StateWatcherId_); + Queue_.wakeUpAll(); + Worker_.join(); + }; + + void StateReceiver::run() { + Poco::AutoPtr Note(Queue_.waitDequeueNotification()); + while(Note && Running_) { + auto Msg = dynamic_cast(Note.get()); + if(Msg!= nullptr) { + try { + nlohmann::json msg = nlohmann::json::parse(Msg->Payload()); + if (msg.contains(uCentralProtocol::PAYLOAD)) { + auto payload = msg[uCentralProtocol::PAYLOAD]; + if (payload.contains("state") && payload.contains("serial")) { + auto serialNumber = payload["serial"].get(); + auto state = std::make_shared(payload["state"]); + auto it = Notifiers_.find(Utils::SerialNumberToInt(serialNumber)); + if(it!=Notifiers_.end()) { + for(const auto &i:it->second) { + i->Post(Utils::SerialNumberToInt(serialNumber), state); + } + } + } + } + } catch (const Poco::Exception &E) { + Logger().log(E); + } catch (...) { + + } + } else { + + } + Note = Queue_.waitDequeueNotification(); + } + } + + void StateReceiver::StateReceived( const std::string & Key, const std::string & Payload) { + std::lock_guard G(Mutex_); + Logger().information(Poco::format("Device(%s): State message.", Key)); + Queue_.enqueueNotification( new StateMessage(Key,Payload)); + } + + void StateReceiver::Register(uint64_t SerialNumber, VenueWatcher *VW) { + std::lock_guard G(Mutex_); + auto it = Notifiers_.find(SerialNumber); + if(it == Notifiers_.end()) { + std::list L; + L.push_back(VW); + Notifiers_[SerialNumber] = L; + } else { + it->second.push_back(VW); + } + } + + void StateReceiver::DeRegister(uint64_t SerialNumber, VenueWatcher *VW) { + std::lock_guard G(Mutex_); + auto it = Notifiers_.find(SerialNumber); + if(it==Notifiers_.end()) + return; + for(auto i=it->second.begin();i!=it->second.end();i++) { + if(*i==VW) { + it->second.erase(i); + break; + } + } + } + + + +} \ No newline at end of file diff --git a/src/StateReceiver.h b/src/StateReceiver.h new file mode 100644 index 0000000..90c2704 --- /dev/null +++ b/src/StateReceiver.h @@ -0,0 +1,54 @@ +// +// Created by stephane bourque on 2022-03-10. +// + +#pragma once +#include "framework/MicroService.h" + +namespace OpenWifi { + class StateMessage : public Poco::Notification { + public: + explicit StateMessage(const std::string &Key, const std::string &Payload ) : + Key_(Key), + Payload_(Payload) {} + const std::string & Key() { return Key_; } + const std::string & Payload() { return Payload_; } + private: + std::string Key_; + std::string Payload_; + }; + + class VenueWatcher; + + class StateReceiver : public SubSystemServer, Poco::Runnable { + public: + + static auto instance() { + static auto instance_ = new StateReceiver; + return instance_; + } + + int Start() override; + void Stop() override; + void StateReceived( const std::string & Key, const std::string & Payload); + void run() override; + void Register(uint64_t SerialNumber, VenueWatcher *VW); + void DeRegister(uint64_t SerialNumber, VenueWatcher *VW); + + private: + // map of mac(as int), list of (id,func) + std::map> Notifiers_; + uint64_t StateWatcherId_=0; + Poco::NotificationQueue Queue_; + Poco::Thread Worker_; + std::atomic_bool Running_=false; + + StateReceiver() noexcept: + SubSystemServer("StatsReceiver", "STATS-RECEIVER", "stats.receiver") + { + } + }; + + inline auto StateReceiver() { return StateReceiver::instance(); } + +} \ No newline at end of file diff --git a/src/VenueCoordinator.cpp b/src/VenueCoordinator.cpp new file mode 100644 index 0000000..3e14d85 --- /dev/null +++ b/src/VenueCoordinator.cpp @@ -0,0 +1,47 @@ +// +// Created by stephane bourque on 2022-03-10. +// + +#include "VenueCoordinator.h" +#include "VenueWatcher.h" + +namespace OpenWifi { + + int VenueCoordinator::Start() { + + std::vector S{ + "903cb3bb1ef4", + "04f8f8fc3772", + "04f8f8fc3b02", + "24f5a207a130", + "68215f0427da" + }; + + std::vector Numbers; + for(const auto &i:S) + Numbers.push_back(Utils::SerialNumberToInt(i)); + std::sort(Numbers.begin(),Numbers.end()); + + Watchers_.push_back(std::make_shared(std::string{"id-1"},Logger(),Numbers)); + + + + + Worker_.start(*this); + return 0; + } + + void VenueCoordinator::Stop() { + Running_=false; + Worker_.wakeUp(); + Worker_.join(); + } + + void VenueCoordinator::run() { + Running_=true; + while(Running_) { + Poco::Thread::trySleep(2000); + } + } + +} \ No newline at end of file diff --git a/src/VenueCoordinator.h b/src/VenueCoordinator.h new file mode 100644 index 0000000..8ac00a2 --- /dev/null +++ b/src/VenueCoordinator.h @@ -0,0 +1,35 @@ +// +// Created by stephane bourque on 2022-03-10. +// + +#pragma once +#include "framework/MicroService.h" +#include "VenueWatcher.h" + +namespace OpenWifi { + + class VenueCoordinator : public SubSystemServer, Poco::Runnable { + public: + + static auto instance() { + static auto instance_ = new VenueCoordinator; + return instance_; + } + + int Start() override; + void Stop() override; + void run() override; + + private: + Poco::Thread Worker_; + std::atomic_bool Running_=false; + std::vector> Watchers_; + + VenueCoordinator() noexcept: + SubSystemServer("VenueCoordinator", "VENUE-COORD", "venue.coordinator") + { + } + }; + inline auto VenueCoordinator() { return VenueCoordinator::instance(); } + +} \ No newline at end of file diff --git a/src/VenueWatcher.cpp b/src/VenueWatcher.cpp new file mode 100644 index 0000000..436e21f --- /dev/null +++ b/src/VenueWatcher.cpp @@ -0,0 +1,66 @@ +// +// Created by stephane bourque on 2022-03-10. +// + +#include "VenueWatcher.h" +#include "StateReceiver.h" + +namespace OpenWifi { + + void VenueWatcher::Start() { + for(const auto &i:SerialNumbers_) + StateReceiver()->Register(i,this); + Worker_.start(*this); + } + + void VenueWatcher::Stop() { + Running_ = false; + Queue_.wakeUpAll(); + Worker_.join(); + } + + void VenueWatcher::run() { + Running_ = true; + Poco::AutoPtr Msg(Queue_.waitDequeueNotification()); + while(Msg && Running_) { + auto MsgContent = dynamic_cast(Msg.get()); + if(MsgContent!= nullptr) { + try { + auto State = MsgContent->Payload(); + std::cout << "SerialNumber: " << Utils::IntToSerialNumber(MsgContent->SerialNumber()) << std::endl; + } catch (const Poco::Exception &E) { + Logger().log(E); + } catch (...) { + + } + } else { + + } + Msg = Queue_.waitDequeueNotification(); + } + } + + void VenueWatcher::SetSerialNumbers(const std::vector &SerialNumbers) { + std::lock_guard G(Mutex_); + + std::vector Diff; + std::set_symmetric_difference(SerialNumbers_.begin(),SerialNumbers_.end(),SerialNumbers.begin(), + SerialNumbers.end(),std::inserter(Diff,Diff.begin())); + + std::vector ToRemove; + std::set_intersection(SerialNumbers_.begin(),SerialNumbers_.end(),Diff.begin(), + Diff.end(),std::inserter(ToRemove,ToRemove.begin())); + + std::vector ToAdd; + std::set_intersection(SerialNumbers.begin(),SerialNumbers.end(),Diff.begin(), + Diff.end(),std::inserter(ToAdd,ToAdd.begin())); + + for(const auto &i:ToRemove) + StateReceiver()->DeRegister(i,this); + for(const auto &i:ToAdd) + StateReceiver()->Register(i,this); + + SerialNumbers_ = SerialNumbers; + } + +} \ No newline at end of file diff --git a/src/VenueWatcher.h b/src/VenueWatcher.h new file mode 100644 index 0000000..20c5a2d --- /dev/null +++ b/src/VenueWatcher.h @@ -0,0 +1,57 @@ +// +// Created by stephane bourque on 2022-03-10. +// + +#pragma once + +#include "framework/MicroService.h" + +namespace OpenWifi { + + class VenueMessage : public Poco::Notification { + public: + explicit VenueMessage(uint64_t SerialNumber, std::shared_ptr &M ) : + Payload_(M), + SerialNumber_(SerialNumber) { + } + inline std::shared_ptr & Payload() { return Payload_; } + inline auto SerialNumber() { return SerialNumber_; } + private: + std::shared_ptr &Payload_; + uint64_t SerialNumber_=0; + }; + + class VenueWatcher : public Poco::Runnable { + public: + explicit VenueWatcher(const std::string &id, Poco::Logger &L, const std::vector & SerialNumbers) : + Id_(id), + Logger_(L), + SerialNumbers_(SerialNumbers) { + std::sort(SerialNumbers_.begin(),SerialNumbers_.end()); + auto last = std::unique(SerialNumbers_.begin(),SerialNumbers_.end()); + SerialNumbers_.erase(last,SerialNumbers_.end()); + } + + void Post(uint64_t SerialNumber, std::shared_ptr &Msg) { + std::lock_guard G(Mutex_); + Queue_.enqueueNotification(new VenueMessage(SerialNumber, Msg)); + } + + void Start(); + void Stop(); + + void run() final; + inline Poco::Logger & Logger() { return Logger_; } + void SetSerialNumbers(const std::vector &SerialNumbers); + + private: + std::recursive_mutex Mutex_; + std::string Id_; + Poco::NotificationQueue Queue_; + Poco::Logger &Logger_; + Poco::Thread Worker_; + std::atomic_bool Running_=false; + std::vector SerialNumbers_; + }; + +} \ No newline at end of file diff --git a/src/framework/MicroService.h b/src/framework/MicroService.h index 9dc0201..109789c 100644 --- a/src/framework/MicroService.h +++ b/src/framework/MicroService.h @@ -915,13 +915,11 @@ namespace OpenWifi::Utils { [[nodiscard]] inline bool ValidEMailAddress(const std::string &email) { // define a regular expression static const std::regex pattern - (("(\\w+)(\\.|_\\+)?(\\w*)@(\\w+)(\\.(\\w+))+")); - + (("(\\w+)(\\.|_|\\++)?(\\w*)@(\\w+)(\\.(\\w+))+")); // try to match the string with the regular expression return std::regex_match(email, pattern); } - [[nodiscard]] inline std::string LoadFile( const Poco::File & F) { std::string Result; try { @@ -1809,14 +1807,14 @@ namespace OpenWifi { return std::stoull(Hint->second); } - [[nodiscard]] inline bool GetBoolParameter(const std::string &Name, bool Default) { + [[nodiscard]] inline bool GetBoolParameter(const std::string &Name, bool Default=false) { auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[Name](const std::pair &S){ return S.first==Name; }); if(Hint==end(Parameters_) || !is_bool(Hint->second)) return Default; return Hint->second=="true"; } - [[nodiscard]] inline std::string GetParameter(const std::string &Name, const std::string &Default) { + [[nodiscard]] inline std::string GetParameter(const std::string &Name, const std::string &Default="") { auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[Name](const std::pair &S){ return S.first==Name; }); if(Hint==end(Parameters_)) return Default;