diff --git a/CMakeLists.txt b/CMakeLists.txt index edbbbfc..6f2dbe8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,13 +33,24 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Werror=shadow -Werror=stack-protector \ -Wstrict-aliasing=2 -Wno-unused \ -Werror=unreachable-code \ - -Wvariadic-macros \ + -Wvariadic-macros -Werror=overloaded-virtual \ -Wwrite-strings -Werror=non-virtual-dtor -Werror=return-type") + +get_directory_property(HAS_PARENT PARENT_DIRECTORY) +if (HAS_PARENT) + option(BUILD_APP "" OFF) +else () + option(BUILD_APP "" ON) + message(STATUS "Skipping build of ${PROJECT_NAME} app") +endif () + # library output set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR}/build) set(CMAKE_LIBRARY_INCLUDE_DIRECTORY ${CMAKE_PREFIX_PATH}/include) +set(POCO_INSTALL_DIR ${CMAKE_BINARY_DIR}) +include_directories(SYSTEM ${CMAKE_BINARY_DIR}/include) link_directories(${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) link_directories(${CMAKE_PREFIX_PATH}/lib) @@ -52,11 +63,10 @@ set(BUILD_GTEST ON CACHE BOOL "Build the library with GTest.") set(BUILD_AWS_LOG_UPLOADER OFF CACHE BOOL "Build the library with AwsLogUploader.") option(POCO_BUILD_DATA "" ON) option(POCO_BUILD_NET "" ON) +option(BUILD_AWS_LOG_UPLOADER "" OFF) add_subdirectory(WolkSDK-Cpp) # WolkGateway library -include_directories("WolkSDK-Cpp/core") - file(GLOB_RECURSE LIB_HEADER_FILES "src/*.h" "src/*.hpp") file(GLOB_RECURSE LIB_SOURCE_FILES "src/*.cpp") @@ -79,7 +89,7 @@ target_include_directories(${PROJECT_NAME}Tests SYSTEM PRIVATE ${CMAKE_LIBRARY_I set_target_properties(${PROJECT_NAME}Tests PROPERTIES INSTALL_RPATH "$ORIGIN/lib") add_dependencies(${PROJECT_NAME}Tests libgtest) -add_custom_target(tests ALL ${PROJECT_NAME}Tests) +add_custom_target(tests ${PROJECT_NAME}Tests) add_test(gtest ${PROJECT_NAME}Tests) @@ -87,27 +97,29 @@ add_test(gtest ${PROJECT_NAME}Tests) file(GLOB_RECURSE BIN_HEADER_FILES "application/*.h") file(GLOB_RECURSE BIN_SOURCE_FILES "application/*.cpp") -add_executable(${PROJECT_NAME}App ${BIN_SOURCE_FILES}) -target_link_libraries(${PROJECT_NAME}App WolkGateway) -set_target_properties(${PROJECT_NAME}App PROPERTIES INSTALL_RPATH "$ORIGIN/lib") +if (${BUILD_APP}) + add_executable(${PROJECT_NAME}App ${BIN_SOURCE_FILES}) + target_link_libraries(${PROJECT_NAME}App WolkGateway) + set_target_properties(${PROJECT_NAME}App PROPERTIES INSTALL_RPATH "$ORIGIN/lib") -# Install target -install(DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} DESTINATION /usr - FILES_MATCHING - PATTERN "*so*") + # Install target + install(DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} DESTINATION /usr + FILES_MATCHING + PATTERN "*so*") -# Place the executable file in /usr/bin -install(TARGETS ${PROJECT_NAME}App DESTINATION /usr/bin) + # Place the executable file in /usr/bin + install(TARGETS ${PROJECT_NAME}App DESTINATION /usr/bin) -# Place the ca.crt file in /usr/bin -install(FILES out/ca.crt DESTINATION /etc/wolkGateway - PERMISSIONS OWNER_WRITE OWNER_READ GROUP_WRITE GROUP_READ WORLD_READ) + # Place the ca.crt file in /usr/bin + install(FILES out/ca.crt DESTINATION /etc/wolkGateway + PERMISSIONS OWNER_WRITE OWNER_READ GROUP_WRITE GROUP_READ WORLD_READ) -# Place the config files and service unit file. -install(FILES out/gatewayConfiguration.json DESTINATION /etc/wolkGateway - PERMISSIONS OWNER_WRITE OWNER_READ GROUP_WRITE GROUP_READ WORLD_READ) + # Place the config files and service unit file. + install(FILES out/gatewayConfiguration.json DESTINATION /etc/wolkGateway + PERMISSIONS OWNER_WRITE OWNER_READ GROUP_WRITE GROUP_READ WORLD_READ) -install(CODE "MESSAGE(\"Installed WolkGateway. Configuration files for the module are placed in /etc/wolkGateway.\")") + install(CODE "MESSAGE(\"Installed WolkGateway. Configuration files for the module are placed in /etc/wolkGateway.\")") +endif () # CMake utilities add_subdirectory(cmake) diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt index 6700f3a..b7d0bb9 100644 --- a/RELEASE_NOTES.txt +++ b/RELEASE_NOTES.txt @@ -5,6 +5,16 @@ WolkGateway bridges communication between WolkAbout IoT platform and multiple devices connected to it. +**Version 4.3.0** + - [IMPROVEMENT] - Created WolkDefault and WolkExternal that provide different interfaces for custom subdevice data providing. + - [IMPROVEMENT] - Introduced the FSFileRepository to provide info about FileManagement files from the File System. + - [IMPROVEMENT] - Disabled the Gateway Update message sending and mechanisms as they have been found unnecessary. + - [IMPROVEMENT] - Imported the newest WolkSDK-Cpp that introduces various fixes and improvements. + - [IMPROVEMENT] - Moved the version file when firmware installing to a different location. Also, finishing the install method with `true` now reports success. + - [IMPROVEMENT] - Introduced the custom data and device status protocols, that allow us to define custom MQTT topics/payloads for those messages. + - [IMPROVEMENT] - Added the FileListener interface for FileManagementService to allow input into the File Management process and allow for hooks. + - [IMPROVEMENT] - Made optimizations for the application to be built as a submodule. + **Version 4.2.9** - [BUGFIX] - Fixed the gateway update to allow us to pass if it has already been updated. - [BUGFIX] - Disabled the gateway update fow now, as it doesn't really have a purpose right now. diff --git a/WolkSDK-Cpp b/WolkSDK-Cpp index 8c34aef..a7da559 160000 --- a/WolkSDK-Cpp +++ b/WolkSDK-Cpp @@ -1 +1 @@ -Subproject commit 8c34aefcbba0c7e9a29f3a332d0846f5a848e19a +Subproject commit a7da5592c71265bbdfb2428274a330991ed6fea1 diff --git a/application/Application.cpp b/application/Application.cpp index ea44c7f..8fb3542 100644 --- a/application/Application.cpp +++ b/application/Application.cpp @@ -91,9 +91,9 @@ int main(int argc, char** argv) wolkabout::GatewayDevice device(gatewayConfiguration.getKey(), gatewayConfiguration.getPassword(), gatewayConfiguration.getSubdeviceManagement()); - auto builder = wolkabout::Wolk::newBuilder(device) - .gatewayHost(gatewayConfiguration.getLocalMqttUri()) - .platformHost(gatewayConfiguration.getPlatformMqttUri()); + auto builder = std::move(wolkabout::Wolk::newBuilder(device) + .gatewayHost(gatewayConfiguration.getLocalMqttUri()) + .platformHost(gatewayConfiguration.getPlatformMqttUri())); if (gatewayConfiguration.getPlatformTrustStore()) { diff --git a/application/Configuration.cpp b/application/Configuration.cpp index 1c09b0d..48ad92d 100644 --- a/application/Configuration.cpp +++ b/application/Configuration.cpp @@ -16,8 +16,8 @@ #include "Configuration.h" -#include "utilities/FileSystemUtils.h" -#include "utilities/json.hpp" +#include "core/utilities/FileSystemUtils.h" +#include "core/utilities/json.hpp" #include #include diff --git a/application/Configuration.h b/application/Configuration.h index d28eb2f..9456b4a 100644 --- a/application/Configuration.h +++ b/application/Configuration.h @@ -14,8 +14,8 @@ * limitations under the License. */ +#include "core/model/WolkOptional.h" #include "model/SubdeviceManagement.h" -#include "model/WolkOptional.h" #include diff --git a/debian/changelog b/debian/changelog index 9940904..893cec2 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,7 +1,12 @@ -wolkgateway (4.2.9) stable; urgency=medium +wolkgateway (4.3.0) stable; urgency=medium - * Fixed the gateway update to allow us to pass if it has already been updated. - * Disabled the gateway update fow now, as it doesn't really have a purpose right now. - * Imported newest WolkSDK-Cpp that allows for optional `description` properties in JsonDto messages. + * Created WolkDefault and WolkExternal that provide different interfaces for custom subdevice data providing. + * Introduced the FSFileRepository to provide info about FileManagement files from the File System. + * Disabled the Gateway Update message sending and mechanisms as they have been found unnecessary. + * Imported the newest WolkSDK-Cpp that introduces various fixes and improvements. + * Moved the version file when firmware installing to a different location. Also, finishing the install method with `true` now reports success. + * Introduced the custom data and device status protocols, that allow us to define custom MQTT topics/payloads for those messages. + * Added the FileListener interface for FileManagementService to allow input into the File Management process and allow for hooks. + * Made optimizations for the application to be built as a submodule. - -- Wolkabout ELab Tue, 11 May 2021 00:00:00 +0100 + -- Wolkabout ELab Fri, 14 May 2021 00:00:00 +0100 diff --git a/src/ActuatorStatusProvider.h b/src/ActuatorStatusProvider.h index eb8e85e..774a5a0 100644 --- a/src/ActuatorStatusProvider.h +++ b/src/ActuatorStatusProvider.h @@ -17,7 +17,7 @@ #ifndef ACTUATORSTATUSPROVIDER_H #define ACTUATORSTATUSPROVIDER_H -#include "model/ActuatorStatus.h" +#include "core/model/ActuatorStatus.h" #include diff --git a/src/ConfigurationHandler.h b/src/ConfigurationHandler.h index 62ef150..bc86a03 100644 --- a/src/ConfigurationHandler.h +++ b/src/ConfigurationHandler.h @@ -17,7 +17,7 @@ #ifndef CONFIGURATIONHANDLER_H #define CONFIGURATIONHANDLER_H -#include "model/ConfigurationItem.h" +#include "core/model/ConfigurationItem.h" #include diff --git a/src/ConfigurationProvider.h b/src/ConfigurationProvider.h index 90db7fa..6049688 100644 --- a/src/ConfigurationProvider.h +++ b/src/ConfigurationProvider.h @@ -17,7 +17,7 @@ #ifndef CONFIGURATIONPROVIDER_H #define CONFIGURATIONPROVIDER_H -#include "model/ConfigurationItem.h" +#include "core/model/ConfigurationItem.h" #include diff --git a/src/FileHandler.cpp b/src/FileHandler.cpp index ab761d6..bd756ef 100644 --- a/src/FileHandler.cpp +++ b/src/FileHandler.cpp @@ -16,8 +16,8 @@ #include "FileHandler.h" -#include "model/BinaryData.h" -#include "utilities/FileSystemUtils.h" +#include "core/model/BinaryData.h" +#include "core/utilities/FileSystemUtils.h" namespace wolkabout { diff --git a/src/FileHandler.h b/src/FileHandler.h index 869d292..292993f 100644 --- a/src/FileHandler.h +++ b/src/FileHandler.h @@ -17,7 +17,7 @@ #ifndef FILEHANDLER_H #define FILEHANDLER_H -#include "utilities/ByteUtils.h" +#include "core/utilities/ByteUtils.h" #include diff --git a/src/FileListener.h b/src/FileListener.h new file mode 100644 index 0000000..2ebb6bf --- /dev/null +++ b/src/FileListener.h @@ -0,0 +1,72 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FILELISTENER_H +#define FILELISTENER_H + +#include +#include + +namespace wolkabout +{ +/** + * This class presents an interface for an object that is interested in ongoing of the `FileDownloadService`, and it + * will be notified of new files that are downloaded/deleted. + */ +class FileListener +{ +public: + /** + * The default overridable destructor. + */ + virtual ~FileListener() = default; + + /** + * This is the method that is invoked by the service to notify of the directory that is being used to store files, + * and in which they are all accessible. + * + * @param absolutePath The absolute path of the directory where files are stored. + */ + virtual void receiveDirectory(const std::string& absolutePath) = 0; + + /** + * This is the method that is invoked by the service to notify that a new file has been made available to download, + * and we can say whether or not we want to download the file. + * + * @param fileName The name of the newly initiated file. + * @return Whether or not we want to download the file. + */ + virtual bool chooseToDownload(const std::string& fileName) = 0; + + /** + * This is the method that is invoked by the service to notify that a new file has been downloaded and is placed in + * the file system. From this point on, the service can manipulate the file further, and cause action based on that. + * + * @param fileName The name of the newly downloaded file. + */ + virtual void onNewFile(const std::string& fileName) = 0; + + /** + * This is the method that is invoked by the service to notify that a file has been deleted or purged with all the + * other ones off the file system. From this point on, the service can no longer count on the file existing. + * + * @param fileName The name of the removed file. + */ + virtual void onRemovedFile(const std::string& fileName) = 0; +}; +} // namespace wolkabout + +#endif // FILELISTENER_H diff --git a/src/GatewayInboundDeviceMessageHandler.cpp b/src/GatewayInboundDeviceMessageHandler.cpp index c671faa..136b3c4 100644 --- a/src/GatewayInboundDeviceMessageHandler.cpp +++ b/src/GatewayInboundDeviceMessageHandler.cpp @@ -16,10 +16,10 @@ #include "GatewayInboundDeviceMessageHandler.h" -#include "model/Message.h" +#include "core/model/Message.h" +#include "core/utilities/Logger.h" +#include "core/utilities/StringUtils.h" #include "protocol/GatewayProtocol.h" -#include "utilities/Logger.h" -#include "utilities/StringUtils.h" #include diff --git a/src/GatewayInboundDeviceMessageHandler.h b/src/GatewayInboundDeviceMessageHandler.h index 276d5c0..405beff 100644 --- a/src/GatewayInboundDeviceMessageHandler.h +++ b/src/GatewayInboundDeviceMessageHandler.h @@ -18,7 +18,7 @@ #define GATEWAYINBOUNDDEVICEMESSAGEHANDLER_H #include "InboundDeviceMessageHandler.h" -#include "utilities/CommandBuffer.h" +#include "core/utilities/CommandBuffer.h" #include #include diff --git a/src/GatewayInboundPlatformMessageHandler.cpp b/src/GatewayInboundPlatformMessageHandler.cpp index 4af511f..70a8b26 100644 --- a/src/GatewayInboundPlatformMessageHandler.cpp +++ b/src/GatewayInboundPlatformMessageHandler.cpp @@ -16,10 +16,10 @@ #include "GatewayInboundPlatformMessageHandler.h" -#include "model/Message.h" -#include "protocol/Protocol.h" -#include "utilities/Logger.h" -#include "utilities/StringUtils.h" +#include "core/model/Message.h" +#include "core/protocol/Protocol.h" +#include "core/utilities/Logger.h" +#include "core/utilities/StringUtils.h" #include diff --git a/src/GatewayInboundPlatformMessageHandler.h b/src/GatewayInboundPlatformMessageHandler.h index 0170ea0..d361f3a 100644 --- a/src/GatewayInboundPlatformMessageHandler.h +++ b/src/GatewayInboundPlatformMessageHandler.h @@ -18,7 +18,7 @@ #define GATEWAYINBOUNDPLATFORMMESSAGEHANDLER_H #include "InboundPlatformMessageHandler.h" -#include "utilities/CommandBuffer.h" +#include "core/utilities/CommandBuffer.h" #include #include diff --git a/src/InboundDeviceMessageHandler.h b/src/InboundDeviceMessageHandler.h index 6e8e53c..1fd59bf 100644 --- a/src/InboundDeviceMessageHandler.h +++ b/src/InboundDeviceMessageHandler.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #ifndef INBOUNDDEVICEMESSAGEHANDLER_H #define INBOUNDDEVICEMESSAGEHANDLER_H diff --git a/src/InboundPlatformMessageHandler.h b/src/InboundPlatformMessageHandler.h index 71d71ae..e595371 100644 --- a/src/InboundPlatformMessageHandler.h +++ b/src/InboundPlatformMessageHandler.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #ifndef INBOUNDPLATFORMMESSAGEHANDLER_H #define INBOUNDPLATFORMMESSAGEHANDLER_H diff --git a/src/OutboundRetryMessageHandler.cpp b/src/OutboundRetryMessageHandler.cpp index fdde0c8..2411641 100644 --- a/src/OutboundRetryMessageHandler.cpp +++ b/src/OutboundRetryMessageHandler.cpp @@ -17,9 +17,9 @@ #include "OutboundRetryMessageHandler.h" #include "OutboundMessageHandler.h" -#include "model/Message.h" -#include "utilities/Logger.h" -#include "utilities/StringUtils.h" +#include "core/model/Message.h" +#include "core/utilities/Logger.h" +#include "core/utilities/StringUtils.h" namespace { diff --git a/src/OutboundRetryMessageHandler.h b/src/OutboundRetryMessageHandler.h index 92be840..d4df579 100644 --- a/src/OutboundRetryMessageHandler.h +++ b/src/OutboundRetryMessageHandler.h @@ -17,7 +17,7 @@ #ifndef OUTBOUNDRETRYMESSAGEHANDLER_H #define OUTBOUNDRETRYMESSAGEHANDLER_H -#include "utilities/Timer.h" +#include "core/utilities/Timer.h" #include #include diff --git a/src/RegistrationMessageRouter.cpp b/src/RegistrationMessageRouter.cpp index 766910c..33b7f29 100644 --- a/src/RegistrationMessageRouter.cpp +++ b/src/RegistrationMessageRouter.cpp @@ -16,10 +16,10 @@ #include "RegistrationMessageRouter.h" -#include "model/Message.h" +#include "core/model/Message.h" +#include "core/protocol/RegistrationProtocol.h" +#include "core/utilities/Logger.h" #include "protocol/GatewaySubdeviceRegistrationProtocol.h" -#include "protocol/RegistrationProtocol.h" -#include "utilities/Logger.h" namespace wolkabout { diff --git a/src/StatusMessageRouter.cpp b/src/StatusMessageRouter.cpp index 7e48dd4..5cae2bb 100644 --- a/src/StatusMessageRouter.cpp +++ b/src/StatusMessageRouter.cpp @@ -16,10 +16,10 @@ #include "StatusMessageRouter.h" -#include "model/Message.h" +#include "core/model/Message.h" +#include "core/protocol/StatusProtocol.h" +#include "core/utilities/Logger.h" #include "protocol/GatewayStatusProtocol.h" -#include "protocol/StatusProtocol.h" -#include "utilities/Logger.h" namespace wolkabout { diff --git a/src/StatusMessageRouter.h b/src/StatusMessageRouter.h index 381d32a..318491b 100644 --- a/src/StatusMessageRouter.h +++ b/src/StatusMessageRouter.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #ifndef STATUSMESSAGEROUTER_H #define STATUSMESSAGEROUTER_H diff --git a/src/Wolk.cpp b/src/Wolk.cpp index 0d1fda7..6bb11e6 100644 --- a/src/Wolk.cpp +++ b/src/Wolk.cpp @@ -16,31 +16,31 @@ #include "Wolk.h" -#include "connectivity/ConnectivityService.h" -#include "model/ConfigurationSetCommand.h" -#include "persistence/Persistence.h" -#include "protocol/DataProtocol.h" +#include "core/connectivity/ConnectivityService.h" +#include "core/model/ConfigurationSetCommand.h" +#include "core/persistence/Persistence.h" +#include "core/protocol/DataProtocol.h" +#include "core/protocol/RegistrationProtocol.h" +#include "core/protocol/StatusProtocol.h" +#include "core/protocol/json/JsonDFUProtocol.h" +#include "core/protocol/json/JsonDownloadProtocol.h" +#include "core/utilities/Logger.h" #include "protocol/GatewayDataProtocol.h" #include "protocol/GatewayFirmwareUpdateProtocol.h" #include "protocol/GatewayStatusProtocol.h" #include "protocol/GatewaySubdeviceRegistrationProtocol.h" -#include "protocol/RegistrationProtocol.h" -#include "protocol/StatusProtocol.h" -#include "protocol/json/JsonDFUProtocol.h" -#include "protocol/json/JsonDownloadProtocol.h" #include "repository/DeviceRepository.h" #include "repository/ExistingDevicesRepository.h" #include "repository/FileRepository.h" -#include "service/DataService.h" -#include "service/DeviceStatusService.h" #include "service/FileDownloadService.h" #include "service/FirmwareUpdateService.h" -#include "service/GatewayDataService.h" #include "service/GatewayUpdateService.h" #include "service/KeepAliveService.h" #include "service/PublishingService.h" #include "service/SubdeviceRegistrationService.h" -#include "utilities/Logger.h" +#include "service/data/DataService.h" +#include "service/data/GatewayDataService.h" +#include "service/status/DeviceStatusService.h" #include #include @@ -62,16 +62,14 @@ WolkBuilder Wolk::newBuilder(GatewayDevice device) return WolkBuilder(std::move(device)); } -void Wolk::connect() +bool Wolk::isConnectedToPlatform() { - connectToPlatform(true); - connectToDevices(true); + return m_connected; } -void Wolk::disconnect() +void Wolk::setPlatformConnectionStatusListener(const std::function& platformConnectionStatusListener) { - addToCommandBuffer([=]() -> void { m_platformConnectivityService->disconnect(); }); - addToCommandBuffer([=]() -> void { m_deviceConnectivityService->disconnect(); }); + this->m_platformConnectionStatusListener = platformConnectionStatusListener; } void Wolk::addSensorReading(const std::string& reference, const std::string& value, unsigned long long rtc) @@ -283,14 +281,6 @@ void Wolk::platformDisconnected() }); } -void Wolk::devicesDisconnected() -{ - addToCommandBuffer([=] { - notifyDevicesDisonnected(); - connectToDevices(true); - }); -} - void Wolk::gatewayUpdated() { addToCommandBuffer([=] { @@ -309,19 +299,6 @@ void Wolk::gatewayUpdated() }); } -void Wolk::deviceRegistered(const std::string& deviceKey) -{ - addToCommandBuffer([=] { - m_deviceStatusService->sendLastKnownStatusForDevice(deviceKey); - m_existingDevicesRepository->addDeviceKey(deviceKey); - }); -} - -void Wolk::deviceUpdated(const std::string& deviceKey) -{ - addToCommandBuffer([=] { m_deviceStatusService->sendLastKnownStatusForDevice(deviceKey); }); -} - void Wolk::publishEverything() { publishFirmwareStatus(); @@ -365,7 +342,10 @@ void Wolk::updateGatewayAndDeleteDevices() // m_gatewayUpdateService->updateGateway(m_device); shouldUpdate = false; - m_subdeviceRegistrationService->deleteDevicesOtherThan(m_existingDevicesRepository->getDeviceKeys()); + if (m_subdeviceRegistrationService && m_device.getSubdeviceManagement().value() == SubdeviceManagement::GATEWAY) + { + m_subdeviceRegistrationService->deleteDevicesOtherThan(m_existingDevicesRepository->getDeviceKeys()); + } } } @@ -373,6 +353,10 @@ void Wolk::notifyPlatformConnected() { LOG(INFO) << "Connection to platform established"; + m_connected = true; + if (m_platformConnectionStatusListener) + m_platformConnectionStatusListener(m_connected); + m_platformPublisher->connected(); if (m_keepAliveService) @@ -385,6 +369,10 @@ void Wolk::notifyPlatformDisonnected() { LOG(INFO) << "Connection to platform lost"; + m_connected = false; + if (m_platformConnectionStatusListener) + m_platformConnectionStatusListener(m_connected); + m_platformPublisher->disconnected(); if (m_keepAliveService) @@ -393,24 +381,6 @@ void Wolk::notifyPlatformDisonnected() } } -void Wolk::notifyDevicesConnected() -{ - LOG(INFO) << "Connection to local bus established"; - - m_devicePublisher->connected(); - - m_deviceStatusService->connected(); -} - -void Wolk::notifyDevicesDisonnected() -{ - LOG(INFO) << "Connection to local bus lost"; - - m_devicePublisher->disconnected(); - - m_deviceStatusService->disconnected(); -} - void Wolk::connectToPlatform(bool firstTime) { addToCommandBuffer([=]() -> void { @@ -440,27 +410,6 @@ void Wolk::connectToPlatform(bool firstTime) }); } -void Wolk::connectToDevices(bool firstTime) -{ - addToCommandBuffer([=]() -> void { - if (firstTime) - LOG(INFO) << "Connecting to local bus..."; - - if (m_deviceConnectivityService->connect()) - { - notifyDevicesConnected(); - } - else - { - if (firstTime) - LOG(INFO) << "Failed to connect to local bus"; - - std::this_thread::sleep_for(std::chrono::milliseconds(RECONNECT_DELAY_MSEC)); - connectToDevices(false); - } - }); -} - void Wolk::requestActuatorStatusesForDevices() { if (m_device.getSubdeviceManagement().value() == SubdeviceManagement::GATEWAY) diff --git a/src/Wolk.h b/src/Wolk.h index 8697f24..2523162 100644 --- a/src/Wolk.h +++ b/src/Wolk.h @@ -22,10 +22,11 @@ #include "ConfigurationHandler.h" #include "ConfigurationProvider.h" #include "WolkBuilder.h" +#include "core/utilities/StringUtils.h" #include "model/GatewayDevice.h" -#include "utilities/StringUtils.h" #include +#include #include #include #include @@ -40,8 +41,8 @@ class ConfigurationSetCommand; class ConnectivityService; class DataProtocol; class DataService; -class DeviceStatusService; class DeviceRepository; +class DeviceStatusService; class ExistingDevicesRepository; class FileDownloadService; class FileRepository; @@ -82,12 +83,26 @@ class Wolk /** * @brief connect Establishes connection with WolkAbout IoT platform */ - void connect(); + virtual void connect() = 0; /** * @brief disconnect Disconnects from WolkAbout IoT platform */ - void disconnect(); + virtual void disconnect() = 0; + + /** + * This is the default getter method for obtaining the platform connection status. + * + * @return Platform connection status. + */ + virtual bool isConnectedToPlatform(); + + /** + * This is the default setter method for setting a callback function that listens to the platform connection status. + * + * @param platformConnectionStatusListener The callback function. + */ + virtual void setPlatformConnectionStatusListener(const std::function& platformConnectionStatusListener); /** * @brief Publishes sensor reading to WolkAbout IoT Cloud
@@ -211,7 +226,7 @@ class Wolk */ void publish(); -private: +protected: static const constexpr std::chrono::seconds KEEP_ALIVE_INTERVAL{600}; explicit Wolk(GatewayDevice device); @@ -233,11 +248,8 @@ class Wolk void handleConfigurationGetCommand(); void platformDisconnected(); - void devicesDisconnected(); void gatewayUpdated(); - void deviceRegistered(const std::string& deviceKey); - void deviceUpdated(const std::string& deviceKey); // void publishEverything(); @@ -247,17 +259,17 @@ class Wolk void notifyPlatformConnected(); void notifyPlatformDisonnected(); - void notifyDevicesConnected(); - void notifyDevicesDisonnected(); void connectToPlatform(bool firstTime = false); - void connectToDevices(bool firstTime = false); void requestActuatorStatusesForDevices(); void requestActuatorStatusesForDevice(const std::string& deviceKey); GatewayDevice m_device; + std::atomic m_connected; + std::function m_platformConnectionStatusListener; + std::unique_ptr m_deviceRepository; std::unique_ptr m_existingDevicesRepository; std::unique_ptr m_fileRepository; diff --git a/src/WolkBuilder.cpp b/src/WolkBuilder.cpp index 1d6e9db..afc7592 100644 --- a/src/WolkBuilder.cpp +++ b/src/WolkBuilder.cpp @@ -21,36 +21,44 @@ #include "RegistrationMessageRouter.h" #include "StatusMessageRouter.h" #include "Wolk.h" -#include "connectivity/ConnectivityService.h" -#include "connectivity/mqtt/MqttConnectivityService.h" -#include "connectivity/mqtt/PahoMqttClient.h" +#include "WolkDefault.h" +#include "WolkExternal.h" +#include "core/connectivity/ConnectivityService.h" +#include "core/connectivity/mqtt/MqttConnectivityService.h" +#include "core/connectivity/mqtt/PahoMqttClient.h" +#include "core/model/Message.h" +#include "core/persistence/InMemoryPersistence.h" +#include "core/protocol/json/JsonDFUProtocol.h" +#include "core/protocol/json/JsonDownloadProtocol.h" +#include "core/protocol/json/JsonProtocol.h" +#include "core/protocol/json/JsonRegistrationProtocol.h" +#include "core/protocol/json/JsonStatusProtocol.h" +#include "core/utilities/FileSystemUtils.h" #include "model/GatewayDevice.h" -#include "model/Message.h" #include "persistence/inmemory/GatewayInMemoryPersistence.h" -#include "persistence/inmemory/InMemoryPersistence.h" -#include "protocol/json/JsonDFUProtocol.h" -#include "protocol/json/JsonDownloadProtocol.h" #include "protocol/json/JsonGatewayDFUProtocol.h" #include "protocol/json/JsonGatewayDataProtocol.h" #include "protocol/json/JsonGatewayStatusProtocol.h" #include "protocol/json/JsonGatewaySubdeviceRegistrationProtocol.h" -#include "protocol/json/JsonProtocol.h" -#include "protocol/json/JsonRegistrationProtocol.h" -#include "protocol/json/JsonStatusProtocol.h" #include "repository/ExistingDevicesRepository.h" +#include "repository/FSFileRepository.h" #include "repository/JsonFileExistingDevicesRepository.h" #include "repository/SQLiteDeviceRepository.h" #include "repository/SQLiteFileRepository.h" -#include "service/DataService.h" -#include "service/DeviceStatusService.h" #include "service/FileDownloadService.h" #include "service/FirmwareUpdateService.h" -#include "service/GatewayDataService.h" #include "service/GatewayUpdateService.h" #include "service/KeepAliveService.h" #include "service/PublishingService.h" #include "service/SubdeviceRegistrationService.h" - +#include "service/data/ExternalDataService.h" +#include "service/data/GatewayDataService.h" +#include "service/data/InternalDataService.h" +#include "service/status/DeviceStatusService.h" +#include "service/status/ExternalDeviceStatusService.h" +#include "service/status/InternalDeviceStatusService.h" + +#include #include namespace wolkabout @@ -151,106 +159,133 @@ WolkBuilder& WolkBuilder::fileDownloadDirectory(const std::string& path) return *this; } +WolkBuilder& WolkBuilder::fileDownloadDirectory(const std::string& path, std::shared_ptr fileListener) +{ + m_fileDownloadDirectory = path; + m_fileListener = std::move(fileListener); + return *this; +} + +WolkBuilder& WolkBuilder::withExternalDataProvider(DataProvider* provider) +{ + m_externalDataProvider = provider; + return *this; +} + +WolkBuilder& WolkBuilder::withProtocol(std::unique_ptr dataProtocol, + std::unique_ptr statusProtocol) +{ + m_dataProtocol = std::move(dataProtocol); + m_statusProtocol = std::move(statusProtocol); + return *this; +} + +WolkBuilder& WolkBuilder::withPersistence(std::shared_ptr persistence) +{ + m_persistence = std::move(persistence); + return *this; +} + std::unique_ptr WolkBuilder::build() { + // Right away check a bunch of the parameters if (m_device.getKey().empty()) - { throw std::logic_error("No device key present."); - } + if (!m_device.getSubdeviceManagement()) + throw std::logic_error("Subdevice management must be specified"); + // Check if the actuator handle and provider are set if they exist if (!m_device.getActuatorReferences().empty()) { if (m_actuationHandler == nullptr && m_actuationHandlerLambda == nullptr) - { throw std::logic_error("Actuation handler not set."); - } if (m_actuatorStatusProvider == nullptr && m_actuatorStatusProviderLambda == nullptr) - { throw std::logic_error("Actuator status provider not set."); - } } + // Check everything we need for configurations if (!m_configurationHandlerLambda != !m_configurationProviderLambda) - { throw std::logic_error("Both ConfigurationProvider and ConfigurationHandler must be set."); - } - if (!m_configurationHandler != !m_configurationProvider) - { throw std::logic_error("Both ConfigurationProvider and ConfigurationHandler must be set."); - } - - if (!m_device.getSubdeviceManagement()) - { - throw std::logic_error("Subdevice management must be specified"); - } + // Check if we have the firmware installer for the firmware update (if it is enabled) if (m_device.getFirmwareUpdate() && m_device.getFirmwareUpdate().value() && m_device.getTemplate().getFirmwareUpdateType().empty()) - { throw std::logic_error("Firmware installer must be provided when firmware update is enabled"); - } + // Check if we have the url downloader (if it is enabled) if (m_device.getUrlDownload() && m_device.getUrlDownload().value() && !m_urlFileDownloader) - { throw std::logic_error("Url downloader must be provided when url download is enabled"); - } - auto wolk = std::unique_ptr(new Wolk(m_device)); + // If a single custom protocol is set, then both must be set. + if ((m_dataProtocol == nullptr && m_statusProtocol != nullptr) || + (m_dataProtocol != nullptr && m_statusProtocol == nullptr)) + throw std::logic_error("Both data and status protocols must be set"); + + // Create the instance of the Wolk based on the data provider source. + auto wolk = [&] { + if (m_externalDataProvider) + return std::unique_ptr(new WolkExternal(m_device)); + else + return std::unique_ptr(new WolkDefault(m_device)); + }(); auto wolkRaw = wolk.get(); - // Setup protocols - wolk->m_dataProtocol.reset(new wolkabout::JsonProtocol(true)); + // Set up the protocols + if (m_dataProtocol && m_statusProtocol) + { + // This is if they're set to custom + wolk->m_dataProtocol = std::move(m_dataProtocol); + wolk->m_statusProtocol = std::move(m_statusProtocol); + } + else + { + // And if we use the default one - "Wolkabout Protocol". + wolk->m_dataProtocol.reset(new wolkabout::JsonProtocol(true)); + wolk->m_statusProtocol.reset(new JsonStatusProtocol(true)); + } + + // Setup the gateway data and status protocol wolk->m_gatewayDataProtocol.reset(new wolkabout::JsonGatewayDataProtocol()); + wolk->m_gatewayStatusProtocol.reset(new JsonGatewayStatusProtocol()); + // Setup the gateway specific registration protocols wolk->m_registrationProtocol.reset(new JsonRegistrationProtocol()); wolk->m_gatewayRegistrationProtocol.reset(new JsonGatewaySubdeviceRegistrationProtocol()); - wolk->m_statusProtocol.reset(new JsonStatusProtocol(true)); - wolk->m_gatewayStatusProtocol.reset(new JsonGatewayStatusProtocol()); - wolk->m_firmwareUpdateProtocol.reset(new JsonDFUProtocol(true)); wolk->m_gatewayFirmwareUpdateProtocol.reset(new JsonGatewayDFUProtocol()); wolk->m_fileDownloadProtocol.reset(new JsonDownloadProtocol(true)); - // Setup device repository - wolk->m_deviceRepository.reset(new SQLiteDeviceRepository()); - - // Setup existing devices repository - wolk->m_existingDevicesRepository.reset(new JsonFileExistingDevicesRepository()); - - // Setup file repository - wolk->m_fileRepository.reset(new SQLiteFileRepository(DATABASE)); - - // Setup connectivity services + // Create the connectivity service for the platform + const std::string localMqttClientId = std::string("Gateway-").append(m_device.getKey()); wolk->m_platformConnectivityService.reset(new MqttConnectivityService(std::make_shared(), m_device.getKey(), m_device.getPassword(), m_platformHost, m_platformTrustStore)); wolk->m_platformConnectivityService->setUncontrolledDisonnectMessage( wolk->m_statusProtocol->makeLastWillMessage(m_device.getKey())); - const std::string localMqttClientId = std::string("Gateway-").append(m_device.getKey()); - wolk->m_deviceConnectivityService.reset(new MqttConnectivityService( - std::make_shared(), m_device.getKey(), m_device.getPassword(), m_gatewayHost, localMqttClientId)); - + // Create the publisher for the platform connectivity service wolk->m_platformPublisher.reset(new PublishingService( *wolk->m_platformConnectivityService, std::unique_ptr(new GatewayInMemoryPersistence()))); - wolk->m_devicePublisher.reset(new PublishingService( - *wolk->m_deviceConnectivityService, std::unique_ptr(new GatewayInMemoryPersistence()))); + // Create the inbound handlers for platform wolk->m_inboundPlatformMessageHandler.reset(new GatewayInboundPlatformMessageHandler(m_device.getKey())); - wolk->m_inboundDeviceMessageHandler.reset(new GatewayInboundDeviceMessageHandler()); + // Create the platform manager and bind it to the connectivity service wolk->m_platformConnectivityManager = std::make_shared>( *wolk->m_inboundPlatformMessageHandler, [wolkRaw] { wolkRaw->platformDisconnected(); }); - - wolk->m_deviceConnectivityManager = std::make_shared>( - *wolk->m_inboundDeviceMessageHandler, [wolkRaw] { wolkRaw->devicesDisconnected(); }); - wolk->m_platformConnectivityService->setListener(wolk->m_platformConnectivityManager); - wolk->m_deviceConnectivityService->setListener(wolk->m_deviceConnectivityManager); + + // Setup keep alive service + if (m_keepAliveEnabled) + { + wolk->m_keepAliveService.reset(new KeepAliveService(m_device.getKey(), *wolk->m_statusProtocol, + *wolk->m_platformPublisher, Wolk::KEEP_ALIVE_INTERVAL)); + } // Setup actuation and configuration handlers wolk->m_actuationHandlerLambda = m_actuationHandlerLambda; @@ -265,111 +300,168 @@ std::unique_ptr WolkBuilder::build() wolk->m_configurationProviderLambda = m_configurationProviderLambda; wolk->m_configurationProvider = m_configurationProvider; + // Setup the data provider + if (m_externalDataProvider) + setupWithExternalData(dynamic_cast(wolk.get())); + else + setupWithInternalData(dynamic_cast(wolk.get())); + + // Create the working directory if it does not exist + if (!FileSystemUtils::isDirectoryPresent(m_fileDownloadDirectory)) + FileSystemUtils::createDirectory(m_fileDownloadDirectory); + + // Create the file repository + wolk->m_fileRepository.reset(new FSFileRepository(m_fileDownloadDirectory)); + + // setup file download service + wolk->m_fileDownloadService = std::make_shared( + m_device.getKey(), *wolk->m_fileDownloadProtocol, m_fileDownloadDirectory, *wolk->m_platformPublisher, + *wolk->m_fileRepository, m_urlFileDownloader, m_fileListener); + wolk->m_inboundPlatformMessageHandler->addListener(wolk->m_fileDownloadService); + + // setup firmware update service + wolk->m_firmwareUpdateService = std::make_shared( + m_device.getKey(), *wolk->m_firmwareUpdateProtocol, *wolk->m_gatewayFirmwareUpdateProtocol, + *wolk->m_fileRepository, *wolk->m_platformPublisher, *wolk->m_platformPublisher, m_firmwareInstaller, + m_firmwareVersion); + wolk->m_inboundPlatformMessageHandler->addListener(wolk->m_firmwareUpdateService); + + return wolk; +} + +void WolkBuilder::setupWithInternalData(WolkDefault* wolk) +{ + // Setup device repository + wolk->m_deviceRepository.reset(new SQLiteDeviceRepository()); + + // Setup existing devices repository + wolk->m_existingDevicesRepository.reset(new JsonFileExistingDevicesRepository()); + // Setup gateway update service wolk->m_gatewayUpdateService.reset(new GatewayUpdateService(m_device.getKey(), *wolk->m_registrationProtocol, *wolk->m_deviceRepository, *wolk->m_platformPublisher)); + wolk->m_gatewayUpdateService->onGatewayUpdated([=] { wolk->gatewayUpdated(); }); - wolk->m_gatewayUpdateService->onGatewayUpdated([wolkRaw] { wolkRaw->gatewayUpdated(); }); + // Create the connectivity service for the devices (local MQTT) + const std::string localMqttClientId = std::string("Gateway-").append(m_device.getKey()); + wolk->m_deviceConnectivityService.reset(new MqttConnectivityService( + std::make_shared(), m_device.getKey(), m_device.getPassword(), m_gatewayHost, localMqttClientId)); + // Create the publisher for the devices (local MQTT) + wolk->m_devicePublisher.reset( + new PublishingService(*wolk->m_deviceConnectivityService, std::make_shared())); + + // Create the inbound message handler for the devices (local MQTT) + wolk->m_inboundDeviceMessageHandler.reset(new GatewayInboundDeviceMessageHandler()); + + // Setup the device connectivity manager for devices (local MQTT) + wolk->m_deviceConnectivityManager = std::make_shared>( + *wolk->m_inboundDeviceMessageHandler, [=] { wolk->devicesDisconnected(); }); + wolk->m_deviceConnectivityService->setListener(wolk->m_deviceConnectivityManager); + + // If we decided that the gateway is in control of sub devices, make the subdevice registration service. if (m_device.getSubdeviceManagement().value() == SubdeviceManagement::GATEWAY) { - // Setup registration service wolk->m_subdeviceRegistrationService.reset(new SubdeviceRegistrationService( m_device.getKey(), *wolk->m_registrationProtocol, *wolk->m_gatewayRegistrationProtocol, *wolk->m_deviceRepository, *wolk->m_platformPublisher, *wolk->m_devicePublisher)); wolk->m_subdeviceRegistrationService->onDeviceRegistered( - [wolkRaw](const std::string& deviceKey) { wolkRaw->deviceRegistered(deviceKey); }); + [=](const std::string& deviceKey) { wolk->deviceRegistered(deviceKey); }); wolk->m_subdeviceRegistrationService->onDeviceUpdated( - [wolkRaw](const std::string& deviceKey) { wolkRaw->deviceUpdated(deviceKey); }); + [=](const std::string& deviceKey) { wolk->deviceUpdated(deviceKey); }); } + // Create the registration message router wolk->m_registrationMessageRouter = std::make_shared( *wolk->m_registrationProtocol, *wolk->m_gatewayRegistrationProtocol, wolk->m_gatewayUpdateService.get(), wolk->m_subdeviceRegistrationService.get(), wolk->m_subdeviceRegistrationService.get(), wolk->m_subdeviceRegistrationService.get(), wolk->m_subdeviceRegistrationService.get(), wolk->m_subdeviceRegistrationService.get()); + // Add the message that the registration router listens to wolk->m_inboundDeviceMessageHandler->addListener(wolk->m_registrationMessageRouter); wolk->m_inboundPlatformMessageHandler->addListener(wolk->m_registrationMessageRouter); - // Setup device status and keep alive service - if (m_device.getSubdeviceManagement().value() == SubdeviceManagement::GATEWAY) - { - wolk->m_deviceStatusService.reset(new DeviceStatusService( - m_device.getKey(), *wolk->m_statusProtocol, *wolk->m_gatewayStatusProtocol, wolk->m_deviceRepository.get(), - *wolk->m_platformPublisher, *wolk->m_devicePublisher, Wolk::KEEP_ALIVE_INTERVAL)); - } - else - { - wolk->m_deviceStatusService.reset( - new DeviceStatusService(m_device.getKey(), *wolk->m_statusProtocol, *wolk->m_gatewayStatusProtocol, nullptr, - *wolk->m_platformPublisher, *wolk->m_devicePublisher, Wolk::KEEP_ALIVE_INTERVAL)); - } + // Setup device status service + wolk->m_deviceStatusService.reset(new InternalDeviceStatusService( + m_device.getKey(), *wolk->m_statusProtocol, *wolk->m_gatewayStatusProtocol, + m_device.getSubdeviceManagement().value() == SubdeviceManagement::GATEWAY ? wolk->m_deviceRepository.get() : + nullptr, + *wolk->m_platformPublisher, *wolk->m_devicePublisher, Wolk::KEEP_ALIVE_INTERVAL)); + + // Setup the data service + wolk->m_dataService = std::make_shared( + m_device.getKey(), *wolk->m_dataProtocol, *wolk->m_gatewayDataProtocol, + m_device.getSubdeviceManagement().value() == SubdeviceManagement::GATEWAY ? wolk->m_deviceRepository.get() : + nullptr, + *wolk->m_platformPublisher, *wolk->m_devicePublisher); + + // Add the data service to the message handlers + wolk->m_inboundDeviceMessageHandler->addListener( + std::dynamic_pointer_cast(wolk->m_dataService)); + wolk->m_inboundPlatformMessageHandler->addListener(wolk->m_dataService); - if (m_keepAliveEnabled) - { - wolk->m_keepAliveService.reset(new KeepAliveService(m_device.getKey(), *wolk->m_statusProtocol, - *wolk->m_platformPublisher, Wolk::KEEP_ALIVE_INTERVAL)); - } + // Setup the data service + setupGatewayDataService(wolk, *wolk->m_dataService); + wolk->m_dataService->setGatewayMessageListener(wolk->m_gatewayDataService.get()); + // Create the status message router wolk->m_statusMessageRouter = std::make_shared( *wolk->m_statusProtocol, *wolk->m_gatewayStatusProtocol, wolk->m_deviceStatusService.get(), wolk->m_deviceStatusService.get(), wolk->m_deviceStatusService.get(), wolk->m_keepAliveService.get()); + // And add the status message router to listen to it. wolk->m_inboundDeviceMessageHandler->addListener(wolk->m_statusMessageRouter); wolk->m_inboundPlatformMessageHandler->addListener(wolk->m_statusMessageRouter); +} - if (m_device.getSubdeviceManagement().value() == SubdeviceManagement::GATEWAY) - { - wolk->m_dataService = std::make_shared( - m_device.getKey(), *wolk->m_dataProtocol, *wolk->m_gatewayDataProtocol, wolk->m_deviceRepository.get(), - *wolk->m_platformPublisher, *wolk->m_devicePublisher); - } - else - { - wolk->m_dataService = - std::make_shared(m_device.getKey(), *wolk->m_dataProtocol, *wolk->m_gatewayDataProtocol, nullptr, - *wolk->m_platformPublisher, *wolk->m_devicePublisher); - } - - wolk->m_inboundDeviceMessageHandler->addListener(wolk->m_dataService); +void WolkBuilder::setupWithExternalData(WolkExternal* wolk) +{ + // Setup the external data and status services. + wolk->m_dataService = std::make_shared( + wolk->m_device.getKey(), *wolk->m_dataProtocol, *wolk->m_gatewayDataProtocol, *wolk->m_platformPublisher); + wolk->m_deviceStatusService.reset( + new ExternalDeviceStatusService(m_device.getKey(), *wolk->m_statusProtocol, *wolk->m_platformPublisher)); + + // Setup the data API that can be used to actually publish data. + wolk->m_dataApi.reset( + new DataHandlerApiFacade(dynamic_cast(*wolk->m_dataService), *wolk->m_deviceStatusService)); + m_externalDataProvider->setDataHandler(wolk->m_dataApi.get(), wolk->m_device.getKey()); + + // Setup the data service + setupGatewayDataService(wolk, *wolk->m_dataService); + wolk->m_dataService->setGatewayMessageListener(wolk->m_gatewayDataService.get()); wolk->m_inboundPlatformMessageHandler->addListener(wolk->m_dataService); + // Create the status message router + wolk->m_statusMessageRouter = + std::make_shared(*wolk->m_statusProtocol, *wolk->m_gatewayStatusProtocol, nullptr, nullptr, + nullptr, wolk->m_keepAliveService.get()); + + // And add the status message router to listen to it. + wolk->m_inboundPlatformMessageHandler->addListener(wolk->m_statusMessageRouter); +} + +void WolkBuilder::setupGatewayDataService(Wolk* wolk, OutboundMessageHandler& outboundMessageHandler) +{ // setup gateway data service if gateway template is not empty const auto gwTemplate = m_device.getTemplate(); if (!gwTemplate.getSensors().empty() || !gwTemplate.getActuators().empty() || !gwTemplate.getAlarms().empty() || !gwTemplate.getConfigurations().empty()) { + auto wolkRaw = wolk; wolk->m_gatewayPersistence.reset(new InMemoryPersistence()); wolk->m_gatewayDataService.reset(new GatewayDataService( - m_device.getKey(), *wolk->m_dataProtocol, *wolk->m_gatewayPersistence, *wolk->m_dataService, + m_device.getKey(), *wolk->m_dataProtocol, *wolk->m_gatewayPersistence, outboundMessageHandler, [wolkRaw](const std::string& reference, const std::string& value) { wolkRaw->handleActuatorSetCommand(reference, value); }, [wolkRaw](const std::string& reference) { wolkRaw->handleActuatorGetCommand(reference); }, [wolkRaw](const ConfigurationSetCommand& command) { wolkRaw->handleConfigurationSetCommand(command); }, [wolkRaw] { wolkRaw->handleConfigurationGetCommand(); })); - - wolk->m_dataService->setGatewayMessageListener(wolk->m_gatewayDataService.get()); } - - // setup file download service - wolk->m_fileDownloadService = - std::make_shared(m_device.getKey(), *wolk->m_fileDownloadProtocol, m_fileDownloadDirectory, - *wolk->m_platformPublisher, *wolk->m_fileRepository, m_urlFileDownloader); - wolk->m_inboundPlatformMessageHandler->addListener(wolk->m_fileDownloadService); - - // setup firmware update service - wolk->m_firmwareUpdateService = std::make_shared( - m_device.getKey(), *wolk->m_firmwareUpdateProtocol, *wolk->m_gatewayFirmwareUpdateProtocol, - *wolk->m_fileRepository, *wolk->m_platformPublisher, *wolk->m_devicePublisher, m_firmwareInstaller, - m_firmwareVersion); - wolk->m_inboundDeviceMessageHandler->addListener(wolk->m_firmwareUpdateService); - wolk->m_inboundPlatformMessageHandler->addListener(wolk->m_firmwareUpdateService); - - return wolk; } wolkabout::WolkBuilder::operator std::unique_ptr() @@ -378,7 +470,11 @@ wolkabout::WolkBuilder::operator std::unique_ptr() } WolkBuilder::WolkBuilder(GatewayDevice device) -: m_platformHost{WOLK_DEMO_HOST}, m_gatewayHost{MESSAGE_BUS_HOST}, m_device{std::move(device)}, m_keepAliveEnabled{true} +: m_platformHost{WOLK_DEMO_HOST} +, m_gatewayHost{MESSAGE_BUS_HOST} +, m_device{std::move(device)} +, m_persistence{new GatewayInMemoryPersistence()} +, m_keepAliveEnabled{true} { } } // namespace wolkabout diff --git a/src/WolkBuilder.h b/src/WolkBuilder.h index 017214f..2915aa4 100644 --- a/src/WolkBuilder.h +++ b/src/WolkBuilder.h @@ -21,9 +21,14 @@ #include "ActuatorStatusProvider.h" #include "ConfigurationHandler.h" #include "ConfigurationProvider.h" +#include "FileListener.h" #include "FirmwareInstaller.h" -#include "connectivity/ConnectivityService.h" +#include "api/DataProvider.h" +#include "core/connectivity/ConnectivityService.h" +#include "core/protocol/DataProtocol.h" +#include "core/protocol/StatusProtocol.h" #include "model/GatewayDevice.h" +#include "persistence/GatewayPersistence.h" #include "service/UrlFileDownloader.h" #include @@ -34,6 +39,10 @@ namespace wolkabout { class Wolk; +class WolkDefault; +class WolkExternal; + +class OutboundMessageHandler; class WolkBuilder final { @@ -42,7 +51,7 @@ class WolkBuilder final * @brief WolkBuilder Initiates wolkabout::Wolk builder * @param device Device for which wolkabout::WolkBuilder is instantiated */ - WolkBuilder(GatewayDevice device); + explicit WolkBuilder(GatewayDevice device); /** * @brief Allows passing of URI to custom WolkAbout IoT platform instance @@ -148,6 +157,39 @@ class WolkBuilder final */ WolkBuilder& fileDownloadDirectory(const std::string& path); + /** + * @brief fileDownloadDirectory specifies directory where to download files and a listener for file changes + * By default files are stored in the working directory of gateway + * @param path Path to directory where files will be stored + * @param fileListener The listener for file changes. + * @return Reference to current wolkabout::WolkBuilder instance (Provides fluent interface) + */ + WolkBuilder& fileDownloadDirectory(const std::string& path, std::shared_ptr fileListener); + + /** + * @brief withExternalDataProvider Use external data provider instead of expecting data on local mqtt + * with WolkAbout protocol + * @param provider Implementation of DataProvider + * @return Reference to current wolkabout::WolkBuilder instance (Provides fluent interface) + */ + WolkBuilder& withExternalDataProvider(DataProvider* provider); + + /** + * @brief withProtocol Use external protocol implementatoion for data and status messages + * @param dataProtocol Implementation of DataProtocol + * @param statusProtocol Implementation of StatusProtocol + * @return Reference to current wolkabout::WolkBuilder instance (Provides fluent interface) + */ + WolkBuilder& withProtocol(std::unique_ptr dataProtocol, + std::unique_ptr statusProtocol); + + /** + * @brief withPersistence Persistence mechanism for outbound messages + * @param persistence Implementation of GatewayPersistence + * @return Reference to current wolkabout::WolkBuilder instance (Provides fluent interface) + */ + WolkBuilder& withPersistence(std::shared_ptr persistence); + /** * @brief Builds Wolk instance * @return Wolk instance as std::unique_ptr @@ -165,6 +207,11 @@ class WolkBuilder final operator std::unique_ptr(); private: + void setupWithInternalData(WolkDefault* wolk); + void setupWithExternalData(WolkExternal* wolk); + + void setupGatewayDataService(Wolk* wolk, OutboundMessageHandler& outboundMessageHandler); + std::string m_platformHost; std::string m_platformTrustStore = TRUST_STORE; std::string m_gatewayHost; @@ -183,12 +230,19 @@ class WolkBuilder final std::shared_ptr m_configurationProvider; std::string m_fileDownloadDirectory = "files"; + std::shared_ptr m_fileListener; std::string m_firmwareVersion; std::shared_ptr m_firmwareInstaller; std::shared_ptr m_urlFileDownloader; + std::unique_ptr m_dataProtocol = nullptr; + std::unique_ptr m_statusProtocol = nullptr; + DataProvider* m_externalDataProvider = nullptr; + + std::shared_ptr m_persistence; + bool m_keepAliveEnabled = true; static const constexpr char* WOLK_DEMO_HOST = "ssl://api-demo.wolkabout.com:8883"; diff --git a/src/WolkDefault.cpp b/src/WolkDefault.cpp new file mode 100644 index 0000000..f64b6d2 --- /dev/null +++ b/src/WolkDefault.cpp @@ -0,0 +1,117 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "WolkDefault.h" + +#include "core/utilities/Logger.h" +#include "repository/ExistingDevicesRepository.h" +#include "service/PublishingService.h" +#include "service/status/InternalDeviceStatusService.h" + +namespace +{ +const unsigned RECONNECT_DELAY_MSEC = 2000; +} + +namespace wolkabout +{ +WolkDefault::~WolkDefault() = default; + +void WolkDefault::connect() +{ + connectToPlatform(true); + connectToDevices(true); +} + +void WolkDefault::disconnect() +{ + addToCommandBuffer([=]() -> void { m_platformConnectivityService->disconnect(); }); + addToCommandBuffer([=]() -> void { + if (m_deviceConnectivityService) + { + m_deviceConnectivityService->disconnect(); + } + }); +} + +WolkDefault::WolkDefault(GatewayDevice device) : Wolk(device) {} + +void WolkDefault::deviceRegistered(const std::string& deviceKey) +{ + addToCommandBuffer([=] { + m_deviceStatusService->sendLastKnownStatusForDevice(deviceKey); + m_existingDevicesRepository->addDeviceKey(deviceKey); + }); +} + +void WolkDefault::deviceUpdated(const std::string& deviceKey) +{ + addToCommandBuffer([=] { m_deviceStatusService->sendLastKnownStatusForDevice(deviceKey); }); +} + +void WolkDefault::devicesDisconnected() +{ + addToCommandBuffer([=] { + notifyDevicesDisonnected(); + connectToDevices(true); + }); +} + +void WolkDefault::notifyDevicesConnected() +{ + LOG(INFO) << "Connection to local bus established"; + + m_devicePublisher->connected(); + + m_deviceStatusService->connected(); +} + +void WolkDefault::notifyDevicesDisonnected() +{ + LOG(INFO) << "Connection to local bus lost"; + + m_devicePublisher->disconnected(); + + m_deviceStatusService->disconnected(); +} + +void WolkDefault::connectToDevices(bool firstTime) +{ + if (!m_deviceConnectivityService) + { + return; + } + + addToCommandBuffer([=]() -> void { + if (firstTime) + LOG(INFO) << "Connecting to local bus..."; + + if (m_deviceConnectivityService->connect()) + { + notifyDevicesConnected(); + } + else + { + if (firstTime) + LOG(INFO) << "Failed to connect to local bus"; + + std::this_thread::sleep_for(std::chrono::milliseconds(RECONNECT_DELAY_MSEC)); + connectToDevices(false); + } + }); +} + +} // namespace wolkabout diff --git a/src/WolkDefault.h b/src/WolkDefault.h new file mode 100644 index 0000000..5a6a842 --- /dev/null +++ b/src/WolkDefault.h @@ -0,0 +1,57 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WOLKABOUT_WOLKDEFAULT_H +#define WOLKABOUT_WOLKDEFAULT_H + +#include "Wolk.h" + +namespace wolkabout +{ +class InternalDeviceStatusService; + +class WolkDefault : public Wolk +{ + friend class WolkBuilder; + +public: + ~WolkDefault(); + + void connect() override; + void disconnect() override; + +private: + explicit WolkDefault(GatewayDevice device); + + void deviceRegistered(const std::string& deviceKey); + void deviceUpdated(const std::string& deviceKey); + + void devicesDisconnected(); + + void notifyDevicesConnected(); + void notifyDevicesDisonnected(); + + void connectToDevices(bool firstTime = false); + + std::unique_ptr m_deviceConnectivityService; + std::unique_ptr m_inboundDeviceMessageHandler; + std::unique_ptr m_devicePublisher; + + std::unique_ptr m_deviceStatusService; +}; +} // namespace wolkabout + +#endif // WOLKABOUT_WOLKDEFAULT_H diff --git a/src/WolkExternal.cpp b/src/WolkExternal.cpp new file mode 100644 index 0000000..5808b5a --- /dev/null +++ b/src/WolkExternal.cpp @@ -0,0 +1,31 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "WolkExternal.h" + +namespace wolkabout +{ +void WolkExternal::connect() +{ + connectToPlatform(true); +} + +void WolkExternal::disconnect() +{ + addToCommandBuffer([=]() -> void { m_platformConnectivityService->disconnect(); }); +} + +} // namespace wolkabout diff --git a/src/WolkExternal.h b/src/WolkExternal.h new file mode 100644 index 0000000..df2f8ed --- /dev/null +++ b/src/WolkExternal.h @@ -0,0 +1,43 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WOLKEXTERNAL_H +#define WOLKEXTERNAL_H + +#include "Wolk.h" +#include "service/DataHandlerApiFacade.h" +#include "service/status/ExternalDeviceStatusService.h" + +namespace wolkabout +{ +class WolkExternal : public Wolk +{ +public: + using Wolk::Wolk; + + void connect() override; + void disconnect() override; + +private: + friend class WolkBuilder; + + std::unique_ptr m_deviceStatusService; + + std::unique_ptr m_dataApi; +}; +} // namespace wolkabout + +#endif // WOLKEXTERNAL_H diff --git a/src/api/DataHandler.h b/src/api/DataHandler.h new file mode 100644 index 0000000..696e5e9 --- /dev/null +++ b/src/api/DataHandler.h @@ -0,0 +1,50 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WOLKABOUT_DATAHANDLER_H +#define WOLKABOUT_DATAHANDLER_H + +#include "core/model/ActuatorStatus.h" +#include "core/model/Alarm.h" +#include "core/model/ConfigurationItem.h" +#include "core/model/DeviceStatus.h" +#include "core/model/SensorReading.h" + +#include +#include + +namespace wolkabout +{ +class DataHandler +{ +public: + virtual ~DataHandler() = default; + + virtual void addSensorReading(const std::string& deviceKey, const SensorReading& reading) = 0; + virtual void addSensorReadings(const std::string& deviceKey, const std::vector& readings) = 0; + + virtual void addAlarm(const std::string& deviceKey, const Alarm& alarm) = 0; + + virtual void addActuatorStatus(const std::string& deviceKey, const ActuatorStatus& status) = 0; + + virtual void addConfiguration(const std::string& deviceKey, + const std::vector& configurations) = 0; + + virtual void addDeviceStatus(const DeviceStatus& status) = 0; +}; +} // namespace wolkabout + +#endif // WOLKABOUT_DATAHANDLER_H diff --git a/src/api/DataProvider.h b/src/api/DataProvider.h new file mode 100644 index 0000000..093c81d --- /dev/null +++ b/src/api/DataProvider.h @@ -0,0 +1,33 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WOLKABOUT_DATAPROVIDER_H +#define WOLKABOUT_DATAPROVIDER_H + +#include "DataHandler.h" + +namespace wolkabout +{ +class DataProvider +{ +public: + virtual ~DataProvider() = default; + + virtual void setDataHandler(DataHandler* handler, const std::string& gatewayKey) = 0; +}; +} // namespace wolkabout + +#endif // WOLKABOUT_DATAPROVIDER_H diff --git a/src/model/GatewayDevice.cpp b/src/model/GatewayDevice.cpp index 3359c90..2999981 100644 --- a/src/model/GatewayDevice.cpp +++ b/src/model/GatewayDevice.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "model/GatewayDevice.h" +#include "GatewayDevice.h" #include diff --git a/src/model/GatewayDevice.h b/src/model/GatewayDevice.h index 157c24c..88ae1e7 100644 --- a/src/model/GatewayDevice.h +++ b/src/model/GatewayDevice.h @@ -17,9 +17,9 @@ #ifndef GATEWAYDEVICE_H #define GATEWAYDEVICE_H -#include "model/DetailedDevice.h" +#include "core/model/DetailedDevice.h" +#include "core/model/WolkOptional.h" #include "model/SubdeviceManagement.h" -#include "model/WolkOptional.h" #include diff --git a/src/persistence/GatewayPersistence.h b/src/persistence/GatewayPersistence.h index 9e86a59..c4c17e1 100644 --- a/src/persistence/GatewayPersistence.h +++ b/src/persistence/GatewayPersistence.h @@ -1,5 +1,5 @@ /* - * Copyright 2018 WolkAbout Technology s.r.o. + * Copyright 2021 WolkAbout Technology s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,12 @@ namespace wolkabout { class Message; +enum class PersistenceMethod +{ + FIFO = 0, + LIFO +}; + /** * @brief A storage designed for holding messages in persistent store prior to publishing. * @@ -48,12 +54,9 @@ class GatewayPersistence virtual bool push(std::shared_ptr message) = 0; /** - * @brief Retrieves, first wolkabout::Message of this storage and removes it from storage. - * - * @return Message {@code std::shared_ptr} or returns nullptr {@code std::shared_ptr} if this - * storage is empty. + * @brief Removes first wolkabout::Message from storage. */ - virtual std::shared_ptr pop() = 0; + virtual void pop() = 0; /** * @brief Retrieves, first wolkabout::Message of this storage without removing it from storage. diff --git a/src/persistence/filesystem/GatewayCircularFileSystemPersistence.cpp b/src/persistence/filesystem/GatewayCircularFileSystemPersistence.cpp new file mode 100644 index 0000000..aed05de --- /dev/null +++ b/src/persistence/filesystem/GatewayCircularFileSystemPersistence.cpp @@ -0,0 +1,101 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GatewayCircularFileSystemPersistence.h" + +#include "core/model/Message.h" +#include "core/utilities/FileSystemUtils.h" +#include "core/utilities/Logger.h" + +namespace wolkabout +{ +GatewayCircularFileSystemPersistence::GatewayCircularFileSystemPersistence(const std::string& persistPath, + PersistenceMethod method, + unsigned sizeLimitBytes) +: GatewayFilesystemPersistence(persistPath, method), m_sizeLimitBytes{sizeLimitBytes} +{ + loadFileSize(); +} + +bool GatewayCircularFileSystemPersistence::push(std::shared_ptr message) +{ + std::lock_guard guard{m_mutex}; + + auto file = saveToDisk(message); + + if (file.empty()) + return false; + + auto size = static_cast(FileSystemUtils::getFileSize(readingPath(m_readingFiles.back()))); + m_totalFileSize += size; + + checkSizeAndNormalize(); + + return true; +} + +void GatewayCircularFileSystemPersistence::pop() +{ + std::lock_guard guard{m_mutex}; + + if (empty()) + { + return; + } + + const auto reading = m_method == PersistenceMethod::FIFO ? firstReading() : lastReading(); + + auto size = static_cast(FileSystemUtils::getFileSize(readingPath(reading))); + + m_totalFileSize = size > m_totalFileSize ? 0 : m_totalFileSize - size; + + m_method == PersistenceMethod::FIFO ? deleteFirstReading() : deleteLastReading(); +} + +void GatewayCircularFileSystemPersistence::setSizeLimit(unsigned bytes) +{ + LOG(INFO) << "Circular Persistence: Setting size limit " << bytes; + + std::lock_guard guard{m_mutex}; + m_sizeLimitBytes = bytes; + + checkSizeAndNormalize(); +} + +void GatewayCircularFileSystemPersistence::loadFileSize() +{ + for (const auto& reading : m_readingFiles) + { + auto size = static_cast(FileSystemUtils::getFileSize(readingPath(reading))); + m_totalFileSize += size; + } +} + +void GatewayCircularFileSystemPersistence::checkSizeAndNormalize() +{ + if (m_sizeLimitBytes == 0) + return; + + while (m_totalFileSize > m_sizeLimitBytes && !m_readingFiles.empty()) + { + LOG(INFO) << "Circular Persistence: Size over limit " << m_totalFileSize; + + m_method == PersistenceMethod::FIFO ? deleteLastReading() : deleteFirstReading(); + + loadFileSize(); + } +} +} // namespace wolkabout diff --git a/src/persistence/filesystem/GatewayCircularFileSystemPersistence.h b/src/persistence/filesystem/GatewayCircularFileSystemPersistence.h new file mode 100644 index 0000000..0958a5f --- /dev/null +++ b/src/persistence/filesystem/GatewayCircularFileSystemPersistence.h @@ -0,0 +1,48 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GATEWAYCIRCULARFILESYSTEMPERSISTENCE_H +#define GATEWAYCIRCULARFILESYSTEMPERSISTENCE_H + +#include "GatewayFilesystemPersistence.h" + +namespace wolkabout +{ +/** + * @brief The GatewayCircularFileSystemPersistence class + * Specialization of GatewayFilesystemPersistence for limited storage + */ +class GatewayCircularFileSystemPersistence : public GatewayFilesystemPersistence +{ +public: + explicit GatewayCircularFileSystemPersistence(const std::string& persistPath, PersistenceMethod method, + unsigned sizeLimitBytes = 0); + + bool push(std::shared_ptr message) override; + void pop() override; + + void setSizeLimit(unsigned bytes); + +private: + void loadFileSize(); + void checkSizeAndNormalize(); + + unsigned m_sizeLimitBytes; + unsigned long long m_totalFileSize = 0; +}; +} // namespace wolkabout + +#endif // GATEWAYCIRCULARFILESYSTEMPERSISTENCE_H diff --git a/src/persistence/filesystem/GatewayFilesystemPersistence.cpp b/src/persistence/filesystem/GatewayFilesystemPersistence.cpp new file mode 100644 index 0000000..aaffe2b --- /dev/null +++ b/src/persistence/filesystem/GatewayFilesystemPersistence.cpp @@ -0,0 +1,238 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GatewayFilesystemPersistence.h" + +#include "MessagePersister.h" +#include "core/utilities/FileSystemUtils.h" +#include "core/utilities/Logger.h" + +#include + +namespace +{ +const std::string READING_FILE_NAME = "reading_"; +const std::string REGEX = READING_FILE_NAME + "(\\d+)"; +} // namespace + +namespace wolkabout +{ +GatewayFilesystemPersistence::GatewayFilesystemPersistence(const std::string& persistPath, PersistenceMethod method) +: m_persister(new MessagePersister()), m_persistPath(persistPath), m_method(method), m_messageNum(0) +{ + initialize(); +} + +GatewayFilesystemPersistence::~GatewayFilesystemPersistence() = default; + +bool GatewayFilesystemPersistence::push(std::shared_ptr message) +{ + std::lock_guard guard{m_mutex}; + return saveToDisk(message) != ""; +} + +void GatewayFilesystemPersistence::pop() +{ + std::lock_guard guard{m_mutex}; + if (empty()) + { + return; + } + + m_method == PersistenceMethod::FIFO ? deleteFirstReading() : deleteLastReading(); +} + +std::shared_ptr GatewayFilesystemPersistence::front() +{ + std::lock_guard guard{m_mutex}; + if (empty()) + { + LOG(DEBUG) << "No readings to load"; + return nullptr; + } + + const auto reading = m_method == PersistenceMethod::FIFO ? firstReading() : lastReading(); + const std::string path = readingPath(reading); + LOG(INFO) << "Loading reading " << reading; + + std::string messageContent; + + if (!FileSystemUtils::readFileContent(path, messageContent)) + { + LOG(ERROR) << "Failed to read readings file " << reading; + + pop(); + + return nullptr; + } + + return m_persister->load(messageContent); +} + +bool GatewayFilesystemPersistence::empty() const +{ + std::lock_guard guard{m_mutex}; + return m_readingFiles.empty(); +} + +std::string GatewayFilesystemPersistence::saveToDisk(std::shared_ptr message) +{ + const std::string fileName = READING_FILE_NAME + std::to_string(++m_messageNum); + const std::string path = m_persistPath + "/" + fileName; + LOG(INFO) << "Persisting reading " << fileName; + + const auto messageContent = m_persister->save(*message); + + if (!FileSystemUtils::createFileWithContent(path, messageContent)) + { + LOG(ERROR) << "Failed to persist reading " << fileName; + return ""; + } + + saveReading(fileName); + + return path; +} + +void GatewayFilesystemPersistence::initialize() +{ + if (FileSystemUtils::isDirectoryPresent(m_persistPath)) + { + // read file names + auto files = FileSystemUtils::listFiles(m_persistPath); + + // filter those that match regex + auto itend = std::remove_if(files.begin(), files.end(), + [](const std::string s) { return !std::regex_match(s, std::regex(REGEX)); }); + files.erase(itend, files.end()); + + if (files.size() > 0) + { + LOG(INFO) << "WolkPersister: Unpersisting " << static_cast(files.size()) << " readings"; + + // sort filenames by message number + std::sort(files.begin(), files.end(), [this](const std::string& s1, const std::string& s2) { + unsigned long fileNumber1, fileNumber2; + + if (!matchFileNumber(s1, fileNumber1)) + { + return false; + } + else if (!matchFileNumber(s2, fileNumber2)) + { + return true; + } + + return fileNumber1 < fileNumber2; + }); + + // get the highest number + unsigned long fileNum; + if (matchFileNumber(files.back(), fileNum)) + { + m_messageNum = fileNum; + } + + std::copy(files.begin(), files.end(), std::back_inserter(m_readingFiles)); + } + } + else + { + if (!FileSystemUtils::createDirectory(m_persistPath)) + { + LOG(ERROR) << "Could not create persist directory: " << m_persistPath; + } + } +} + +void GatewayFilesystemPersistence::saveReading(const std::string& fileName) +{ + m_readingFiles.push_back(fileName); +} + +std::string GatewayFilesystemPersistence::readingPath(const std::string& readingFileName) const +{ + return m_persistPath + "/" + readingFileName; +} + +void GatewayFilesystemPersistence::deleteFirstReading() +{ + const std::string path = readingPath(m_readingFiles.front()); + + LOG(INFO) << "Deleting reading " << m_readingFiles.front(); + if (FileSystemUtils::deleteFile(path)) + { + m_readingFiles.pop_front(); + + if (m_readingFiles.size() == 0) + { + m_messageNum = 0; + } + } + else + { + LOG(ERROR) << "Failed to delete readings file " << m_readingFiles.front(); + } +} + +void GatewayFilesystemPersistence::deleteLastReading() +{ + const std::string path = readingPath(m_readingFiles.back()); + + LOG(INFO) << "Deleting reading " << m_readingFiles.back(); + if (FileSystemUtils::deleteFile(path)) + { + m_readingFiles.pop_back(); + + if (m_readingFiles.size() == 0) + { + m_messageNum = 0; + } + } + else + { + LOG(ERROR) << "Failed to delete readings file " << m_readingFiles.front(); + } +} + +std::string GatewayFilesystemPersistence::firstReading() +{ + return m_readingFiles.front(); +} + +std::string GatewayFilesystemPersistence::lastReading() +{ + return m_readingFiles.back(); +} + +bool GatewayFilesystemPersistence::matchFileNumber(const std::string& fileName, unsigned long& number) const +{ + const auto fileNameLen = READING_FILE_NAME.length(); + + try + { + const std::string num = fileName.substr(fileNameLen, fileName.length() - fileNameLen); + number = std::stoul(num); + return true; + } + catch (...) + { + LOG(ERROR) << "Invalid reading file name: " << fileName; + } + + return false; +} +} // namespace wolkabout diff --git a/src/persistence/filesystem/GatewayFilesystemPersistence.h b/src/persistence/filesystem/GatewayFilesystemPersistence.h new file mode 100644 index 0000000..d345c7f --- /dev/null +++ b/src/persistence/filesystem/GatewayFilesystemPersistence.h @@ -0,0 +1,71 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GATEWAYFILESYSTEMPERSISTENCE_H +#define GATEWAYFILESYSTEMPERSISTENCE_H + +#include "persistence/GatewayPersistence.h" + +#include +#include +#include + +namespace wolkabout +{ +class MessagePersister; + +/** + * @brief The GatewayFilesystemPersistence class + * Persists messages on file system + */ +class GatewayFilesystemPersistence : public GatewayPersistence +{ +public: + GatewayFilesystemPersistence(const std::string& persistPath, PersistenceMethod method); + ~GatewayFilesystemPersistence(); + + bool push(std::shared_ptr message) override; + void pop() override; + std::shared_ptr front() override; + bool empty() const override; + +protected: + void initialize(); + + void saveReading(const std::string& fileName); + void deleteFirstReading(); + void deleteLastReading(); + + std::string firstReading(); + std::string lastReading(); + std::string readingPath(const std::string& readingFileName) const; + bool matchFileNumber(const std::string& fileName, unsigned long& number) const; + + std::string saveToDisk(std::shared_ptr message); + + std::unique_ptr m_persister; + + mutable std::recursive_mutex m_mutex; + std::queue> m_queue; + + const std::string m_persistPath; + PersistenceMethod m_method; + std::list m_readingFiles; + unsigned long m_messageNum; +}; +} // namespace wolkabout + +#endif diff --git a/src/persistence/filesystem/MessagePersister.cpp b/src/persistence/filesystem/MessagePersister.cpp new file mode 100644 index 0000000..c3a1ef3 --- /dev/null +++ b/src/persistence/filesystem/MessagePersister.cpp @@ -0,0 +1,51 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MessagePersister.h" + +#include + +namespace +{ +const std::string DELIMITER = "\n"; +} + +namespace wolkabout +{ +std::string MessagePersister::save(const wolkabout::Message& message) const +{ + std::stringstream stream; + + stream << message.getChannel() << DELIMITER << message.getContent(); + + return stream.str(); +} + +std::unique_ptr MessagePersister::load(const std::string& text) const +{ + const auto pos = text.find(DELIMITER); + + if (pos == std::string::npos) + { + return nullptr; + } + + const auto channel = text.substr(0, pos); + const auto content = text.substr(pos + DELIMITER.size()); + + return std::unique_ptr(new Message(channel, content)); +} +} // namespace wolkabout diff --git a/src/persistence/filesystem/MessagePersister.h b/src/persistence/filesystem/MessagePersister.h new file mode 100644 index 0000000..54bdc87 --- /dev/null +++ b/src/persistence/filesystem/MessagePersister.h @@ -0,0 +1,36 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MESSAGEPERSISTER_H +#define MESSAGEPERSISTER_H + +#include "core/model/Message.h" + +#include + +namespace wolkabout +{ +class MessagePersister +{ +public: + virtual ~MessagePersister() = default; + + virtual std::string save(const Message& message) const; + virtual std::unique_ptr load(const std::string& text) const; +}; +} // namespace wolkabout + +#endif // MESSAGEPERSISTER_H diff --git a/src/persistence/inmemory/GatewayInMemoryPersistence.cpp b/src/persistence/inmemory/GatewayInMemoryPersistence.cpp index e51ffbe..fb1e1f5 100644 --- a/src/persistence/inmemory/GatewayInMemoryPersistence.cpp +++ b/src/persistence/inmemory/GatewayInMemoryPersistence.cpp @@ -25,13 +25,10 @@ bool GatewayInMemoryPersistence::push(std::shared_ptr message) return true; } -std::shared_ptr GatewayInMemoryPersistence::pop() +void GatewayInMemoryPersistence::pop() { std::lock_guard lg{m_lock}; - auto message = m_queue.front(); m_queue.pop(); - - return message; } std::shared_ptr GatewayInMemoryPersistence::front() diff --git a/src/persistence/inmemory/GatewayInMemoryPersistence.h b/src/persistence/inmemory/GatewayInMemoryPersistence.h index 4831725..9bfa216 100644 --- a/src/persistence/inmemory/GatewayInMemoryPersistence.h +++ b/src/persistence/inmemory/GatewayInMemoryPersistence.h @@ -28,7 +28,7 @@ class GatewayInMemoryPersistence : public GatewayPersistence { public: bool push(std::shared_ptr message) override; - std::shared_ptr pop() override; + void pop() override; std::shared_ptr front() override; bool empty() const override; diff --git a/src/protocol/GatewayProtocol.cpp b/src/protocol/GatewayProtocol.cpp index 112cc34..e08328f 100644 --- a/src/protocol/GatewayProtocol.cpp +++ b/src/protocol/GatewayProtocol.cpp @@ -16,7 +16,7 @@ #include "protocol/GatewayProtocol.h" -#include "utilities/StringUtils.h" +#include "core/utilities/StringUtils.h" namespace wolkabout { @@ -35,4 +35,4 @@ std::string GatewayProtocol::extractDeviceKeyFromChannel(const std::string& topi return ""; } -} // namespace wolkabout \ No newline at end of file +} // namespace wolkabout diff --git a/src/protocol/json/JsonGatewayDFUProtocol.cpp b/src/protocol/json/JsonGatewayDFUProtocol.cpp index 38dbfa2..5a0d962 100644 --- a/src/protocol/json/JsonGatewayDFUProtocol.cpp +++ b/src/protocol/json/JsonGatewayDFUProtocol.cpp @@ -16,15 +16,15 @@ #include "protocol/json/JsonGatewayDFUProtocol.h" -#include "model/FirmwareUpdateAbort.h" -#include "model/FirmwareUpdateInstall.h" -#include "model/FirmwareUpdateStatus.h" -#include "model/FirmwareVersion.h" -#include "model/Message.h" -#include "protocol/json/Json.h" -#include "utilities/Logger.h" -#include "utilities/StringUtils.h" -#include "utilities/json.hpp" +#include "core/model/FirmwareUpdateAbort.h" +#include "core/model/FirmwareUpdateInstall.h" +#include "core/model/FirmwareUpdateStatus.h" +#include "core/model/FirmwareVersion.h" +#include "core/model/Message.h" +#include "core/protocol/json/Json.h" +#include "core/utilities/Logger.h" +#include "core/utilities/StringUtils.h" +#include "core/utilities/json.hpp" using nlohmann::json; diff --git a/src/protocol/json/JsonGatewayDataProtocol.cpp b/src/protocol/json/JsonGatewayDataProtocol.cpp index 7b00853..7ca149a 100644 --- a/src/protocol/json/JsonGatewayDataProtocol.cpp +++ b/src/protocol/json/JsonGatewayDataProtocol.cpp @@ -16,15 +16,15 @@ #include "protocol/json/JsonGatewayDataProtocol.h" -#include "model/ActuatorGetCommand.h" -#include "model/ActuatorSetCommand.h" -#include "model/ActuatorStatus.h" -#include "model/Alarm.h" -#include "model/Message.h" -#include "protocol/json/Json.h" -#include "utilities/Logger.h" -#include "utilities/StringUtils.h" -#include "utilities/json.hpp" +#include "core/model/ActuatorGetCommand.h" +#include "core/model/ActuatorSetCommand.h" +#include "core/model/ActuatorStatus.h" +#include "core/model/Alarm.h" +#include "core/model/Message.h" +#include "core/protocol/json/Json.h" +#include "core/utilities/Logger.h" +#include "core/utilities/StringUtils.h" +#include "core/utilities/json.hpp" #include diff --git a/src/protocol/json/JsonGatewayStatusProtocol.cpp b/src/protocol/json/JsonGatewayStatusProtocol.cpp index 2a0d38a..1adaa18 100644 --- a/src/protocol/json/JsonGatewayStatusProtocol.cpp +++ b/src/protocol/json/JsonGatewayStatusProtocol.cpp @@ -16,12 +16,12 @@ #include "protocol/json/JsonGatewayStatusProtocol.h" -#include "model/DeviceStatus.h" -#include "model/Message.h" -#include "protocol/json/Json.h" -#include "utilities/Logger.h" -#include "utilities/StringUtils.h" -#include "utilities/json.hpp" +#include "core/model/DeviceStatus.h" +#include "core/model/Message.h" +#include "core/protocol/json/Json.h" +#include "core/utilities/Logger.h" +#include "core/utilities/StringUtils.h" +#include "core/utilities/json.hpp" #include diff --git a/src/protocol/json/JsonGatewaySubdeviceRegistrationProtocol.cpp b/src/protocol/json/JsonGatewaySubdeviceRegistrationProtocol.cpp index fbb179c..3634135 100644 --- a/src/protocol/json/JsonGatewaySubdeviceRegistrationProtocol.cpp +++ b/src/protocol/json/JsonGatewaySubdeviceRegistrationProtocol.cpp @@ -16,16 +16,16 @@ #include "protocol/json/JsonGatewaySubdeviceRegistrationProtocol.h" -#include "model/Message.h" -#include "model/SubdeviceRegistrationRequest.h" -#include "model/SubdeviceRegistrationResponse.h" -#include "model/SubdeviceUpdateRequest.h" -#include "model/SubdeviceUpdateResponse.h" -#include "protocol/json/Json.h" -#include "protocol/json/JsonDto.h" -#include "utilities/Logger.h" -#include "utilities/StringUtils.h" -#include "utilities/json.hpp" +#include "core/model/Message.h" +#include "core/model/SubdeviceRegistrationRequest.h" +#include "core/model/SubdeviceRegistrationResponse.h" +#include "core/model/SubdeviceUpdateRequest.h" +#include "core/model/SubdeviceUpdateResponse.h" +#include "core/protocol/json/Json.h" +#include "core/protocol/json/JsonDto.h" +#include "core/utilities/Logger.h" +#include "core/utilities/StringUtils.h" +#include "core/utilities/json.hpp" #include diff --git a/src/repository/FSFileRepository.cpp b/src/repository/FSFileRepository.cpp new file mode 100644 index 0000000..a47d103 --- /dev/null +++ b/src/repository/FSFileRepository.cpp @@ -0,0 +1,144 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "FSFileRepository.h" + +#include "core/utilities/FileSystemUtils.h" +#include "core/utilities/Logger.h" +#include "core/utilities/StringUtils.h" + +#include +#include +#include +#include +#include + +namespace wolkabout +{ +const std::string FILE_SYSTEM_DIVIDER = "/"; + +FSFileRepository::FSFileRepository(std::string folderPath) : m_folderPath(std::move(folderPath)) +{ + // Check that the folder exists + if (!FileSystemUtils::isDirectoryPresent(m_folderPath)) + throw std::runtime_error("FSFileRepository: The folder '" + m_folderPath + "' does not exist!"); + + LOG(TRACE) << "FSFileRepository: Created in folder '" << m_folderPath << "'."; +} + +std::unique_ptr FSFileRepository::getFileInfo(const std::string& fileName) +{ + // Check if the file is found in the folder. + const auto filePath = composeFilePath(fileName); + if (!FileSystemUtils::isFilePresent(filePath)) + { + LOG(DEBUG) << "FSFileRepository: Failed to obtain `FileInfo` for a file '" << fileName << "'. File not found."; + return nullptr; + } + + // Get the hash value for this file + auto hash = calculateFileHash(filePath); + LOG(DEBUG) << "FSFileRepository: Obtained info about file '" << fileName << "', hash: '" << hash << "', path: '" + << filePath << "'"; + + // Return the `FileInfo` about this file. + return std::unique_ptr(new FileInfo(fileName, hash, filePath)); +} + +std::unique_ptr> FSFileRepository::getAllFileNames() +{ + // Get the list of files in the folder + auto files = FileSystemUtils::listFiles(m_folderPath); + LOG(DEBUG) << "FSFileRepository: Obtained " << files.size() << " files."; + + // And return the list of files + auto fileVector = std::unique_ptr>(new std::vector()); + if (!files.empty()) + fileVector->swap(files); + return fileVector; +} + +void FSFileRepository::store(const FileInfo& /** info */) +{ + // This doesn't have to do anything, as the file should already be in the directory. +} + +void FSFileRepository::remove(const std::string& fileName) +{ + // Check if the file exists + const auto filePath = composeFilePath(fileName); + if (FileSystemUtils::isFilePresent(filePath)) + { + FileSystemUtils::deleteFile(filePath); + LOG(DEBUG) << "FSFileRepository: File '" << fileName << "' has been deleted."; + } +} + +void FSFileRepository::removeAll() +{ + // Go through all the files in the folder and remove them all + for (const auto& fileName : FileSystemUtils::listFiles(m_folderPath)) + { + const auto filePath = composeFilePath(fileName); + FileSystemUtils::deleteFile(filePath); + LOG(DEBUG) << "FSFileRepository: File '" << fileName << "' has been deleted."; + } +} + +bool FSFileRepository::containsInfoForFile(const std::string& fileName) +{ + // Check if the file exists + const auto filePath = composeFilePath(fileName); + return FileSystemUtils::isFilePresent(filePath); +} + +std::string FSFileRepository::composeFilePath(const std::string& fileName) +{ + return m_folderPath + FILE_SYSTEM_DIVIDER + fileName; +} + +std::string FSFileRepository::calculateFileHash(const std::string& filePath) +{ + LOG(TRACE) << "FSFileRepository: Calculating hash for file at '" << filePath << "'."; + + // Check if the file exists + if (!FileSystemUtils::isFilePresent(filePath)) + { + LOG(TRACE) << "FSFileRepository: File could not be found at location '" << filePath << "'."; + return ""; + } + + // Load the entire content of the file, and get the hash. + std::string stringContent; + if (!FileSystemUtils::readFileContent(filePath, stringContent)) + { + LOG(TRACE) << "FSFileRepository: Failed to read content of file at location '" << filePath << "'."; + return ""; + } + + // Calculate the hash + const auto hash = wolkabout::ByteUtils::hashSHA256(wolkabout::ByteUtils::toByteArray(stringContent)); + + // Generate the hash string + std::stringstream ss; + ss << std::hex << std::setfill('0'); + for (const auto byte : hash) + ss << std::setw(2) << static_cast(byte); + + // Return the generated string + return ss.str(); +} +} // namespace wolkabout diff --git a/src/repository/FSFileRepository.h b/src/repository/FSFileRepository.h new file mode 100644 index 0000000..79559a0 --- /dev/null +++ b/src/repository/FSFileRepository.h @@ -0,0 +1,104 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WOLKGATEWAY_FSFILEREPOSITORY_H +#define WOLKGATEWAY_FSFILEREPOSITORY_H + +#include "FileRepository.h" + +namespace wolkabout +{ +class FSFileRepository : public FileRepository +{ +public: + /** + * Default constructor for the FileSystemFileRepository. + * + * @param folderPath The path to the folder which stores the files. + */ + explicit FSFileRepository(std::string folderPath); + + /** + * This method serves to return the information about a file. + * This is an overridden method from `FileRepository`. + * + * @param fileName The name of the file we wish to gather information about. + * @return The file info if the file with the name is found. + */ + std::unique_ptr getFileInfo(const std::string& fileName) override; + + /** + * This method serves to return a list of files that are found locally. + * This is an overridden method from `FileRepository`. + * + * @return The list of names of files that are found locally. + */ + std::unique_ptr> getAllFileNames() override; + + /** + * This method serves to store information about a file. + * This is an overridden method from `FileRepository`. + * + * @param info The info struct that needs to be stored about files. + */ + void store(const FileInfo& info) override; + + /** + * This method removes the specific file with the name. + * This is an overridden method from `FileRepository`. + * + * @param fileName The name of the file the user wishes to be removed. + */ + void remove(const std::string& fileName) override; + + /** + * This method removes all files that are locally found. + * This is an overridden method from `FileRepository`. + */ + void removeAll() override; + + /** + * This method returns whether or not we can obtain information about a file. + * This is an overridden method from `FileRepository`. + * + * @param fileName The name of the file the user wishes to get information. + * @return Whether or not we can obtain info about a file. + */ + bool containsInfoForFile(const std::string& fileName) override; + +private: + /** + * This is an internal method that composes the full path for a file, with the folder that it is in. + * + * @param fileName The name of the file. + * @return The full path with the folder we are working with. + */ + std::string composeFilePath(const std::string& fileName); + + /** + * This is an internal method that explicitly obtains a SHA256 hash of a file. + * + * @param filePath The path to the file that we wish to find a SHA256 hash of. + * @return The SHA256 of the file content. + */ + static std::string calculateFileHash(const std::string& filePath); + + // This is where we store the path to the folder that is used for file management. + std::string m_folderPath; +}; +} // namespace wolkabout + +#endif // WOLKGATEWAY_FSFILEREPOSITORY_H diff --git a/src/repository/JsonFileExistingDevicesRepository.cpp b/src/repository/JsonFileExistingDevicesRepository.cpp index 826748e..6097955 100644 --- a/src/repository/JsonFileExistingDevicesRepository.cpp +++ b/src/repository/JsonFileExistingDevicesRepository.cpp @@ -16,8 +16,8 @@ #include "repository/JsonFileExistingDevicesRepository.h" -#include "utilities/FileSystemUtils.h" -#include "utilities/json.hpp" +#include "core/utilities/FileSystemUtils.h" +#include "core/utilities/json.hpp" #include #include diff --git a/src/repository/SQLiteDeviceRepository.cpp b/src/repository/SQLiteDeviceRepository.cpp index cb783e3..65effaf 100644 --- a/src/repository/SQLiteDeviceRepository.cpp +++ b/src/repository/SQLiteDeviceRepository.cpp @@ -16,13 +16,13 @@ #include "repository/SQLiteDeviceRepository.h" -#include "model/ActuatorTemplate.h" -#include "model/AlarmTemplate.h" -#include "model/ConfigurationTemplate.h" -#include "model/DataType.h" -#include "model/DetailedDevice.h" -#include "model/DeviceTemplate.h" -#include "utilities/Logger.h" +#include "core/model/ActuatorTemplate.h" +#include "core/model/AlarmTemplate.h" +#include "core/model/ConfigurationTemplate.h" +#include "core/model/DataType.h" +#include "core/model/DetailedDevice.h" +#include "core/model/DeviceTemplate.h" +#include "core/utilities/Logger.h" #include #include diff --git a/src/repository/SQLiteFileRepository.cpp b/src/repository/SQLiteFileRepository.cpp index edbb10c..2d811ca 100644 --- a/src/repository/SQLiteFileRepository.cpp +++ b/src/repository/SQLiteFileRepository.cpp @@ -16,7 +16,7 @@ #include "repository/SQLiteFileRepository.h" -#include "utilities/Logger.h" +#include "core/utilities/Logger.h" #include #include @@ -184,4 +184,4 @@ void SQLiteFileRepository::update(const FileInfo& info) remove(info.name); store(info); } -} // namespace wolkabout \ No newline at end of file +} // namespace wolkabout diff --git a/src/service/DataHandlerApiFacade.cpp b/src/service/DataHandlerApiFacade.cpp new file mode 100644 index 0000000..501552a --- /dev/null +++ b/src/service/DataHandlerApiFacade.cpp @@ -0,0 +1,56 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DataHandlerApiFacade.h" + +namespace wolkabout +{ +DataHandlerApiFacade::DataHandlerApiFacade(ExternalDataService& dataHandler, ExternalDeviceStatusService& statusHandler) +: m_dataHandler{dataHandler}, m_statusHandler{statusHandler} +{ +} + +void DataHandlerApiFacade::addSensorReading(const std::string& deviceKey, const SensorReading& reading) +{ + m_dataHandler.addSensorReading(deviceKey, reading); +} + +void DataHandlerApiFacade::addSensorReadings(const std::string& deviceKey, const std::vector& readings) +{ + m_dataHandler.addSensorReadings(deviceKey, readings); +} + +void DataHandlerApiFacade::addAlarm(const std::string& deviceKey, const Alarm& alarm) +{ + m_dataHandler.addAlarm(deviceKey, alarm); +} + +void DataHandlerApiFacade::addActuatorStatus(const std::string& deviceKey, const ActuatorStatus& status) +{ + m_dataHandler.addActuatorStatus(deviceKey, status); +} + +void DataHandlerApiFacade::addConfiguration(const std::string& deviceKey, + const std::vector& configurations) +{ + m_dataHandler.addConfiguration(deviceKey, configurations); +} + +void DataHandlerApiFacade::addDeviceStatus(const DeviceStatus& status) +{ + m_statusHandler.addDeviceStatus(status); +} +} // namespace wolkabout diff --git a/src/service/DataHandlerApiFacade.h b/src/service/DataHandlerApiFacade.h new file mode 100644 index 0000000..c546eef --- /dev/null +++ b/src/service/DataHandlerApiFacade.h @@ -0,0 +1,44 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DATAHANDLERAPIFACADE_H +#define DATAHANDLERAPIFACADE_H + +#include "api/DataHandler.h" +#include "data/ExternalDataService.h" +#include "status/ExternalDeviceStatusService.h" + +namespace wolkabout +{ +class DataHandlerApiFacade : public DataHandler +{ +public: + DataHandlerApiFacade(ExternalDataService& dataHandler, ExternalDeviceStatusService& statusHandler); + + void addSensorReading(const std::string& deviceKey, const SensorReading& reading) override; + void addSensorReadings(const std::string& deviceKey, const std::vector& readings) override; + void addAlarm(const std::string& deviceKey, const Alarm& alarm) override; + void addActuatorStatus(const std::string& deviceKey, const ActuatorStatus& status) override; + void addConfiguration(const std::string& deviceKey, const std::vector& configurations) override; + void addDeviceStatus(const DeviceStatus& status) override; + +private: + ExternalDataService& m_dataHandler; + ExternalDeviceStatusService& m_statusHandler; +}; +} // namespace wolkabout + +#endif // DATAHANDLERAPIFACADE_H diff --git a/src/service/FileDownloadService.cpp b/src/service/FileDownloadService.cpp index 41944ae..c0bcceb 100644 --- a/src/service/FileDownloadService.cpp +++ b/src/service/FileDownloadService.cpp @@ -17,27 +17,28 @@ #include "service/FileDownloadService.h" #include "FileHandler.h" +#include "FileListener.h" #include "OutboundMessageHandler.h" -#include "connectivity/ConnectivityService.h" -#include "model/BinaryData.h" -#include "model/FileList.h" -#include "model/FilePacketRequest.h" -#include "model/FileTransferStatus.h" -#include "model/FileUploadStatus.h" -#include "model/FileUrlDownloadAbort.h" -#include "model/FileUrlDownloadInitiate.h" -#include "model/FileUrlDownloadStatus.h" -#include "model/Message.h" -#include "protocol/json/JsonDownloadProtocol.h" +#include "core/connectivity/ConnectivityService.h" +#include "core/model/BinaryData.h" +#include "core/model/FileList.h" +#include "core/model/FilePacketRequest.h" +#include "core/model/FileTransferStatus.h" +#include "core/model/FileUploadStatus.h" +#include "core/model/FileUrlDownloadAbort.h" +#include "core/model/FileUrlDownloadInitiate.h" +#include "core/model/FileUrlDownloadStatus.h" +#include "core/model/Message.h" +#include "core/protocol/json/JsonDownloadProtocol.h" +#include "core/utilities/ByteUtils.h" +#include "core/utilities/FileSystemUtils.h" +#include "core/utilities/Logger.h" +#include "core/utilities/StringUtils.h" #include "repository/FileRepository.h" #include "service/UrlFileDownloader.h" -#include "utilities/ByteUtils.h" -#include "utilities/FileSystemUtils.h" -#include "utilities/Logger.h" #include #include -#include namespace { @@ -51,20 +52,27 @@ namespace wolkabout FileDownloadService::FileDownloadService(std::string gatewayKey, JsonDownloadProtocol& protocol, std::string fileDownloadDirectory, OutboundMessageHandler& outboundMessageHandler, FileRepository& fileRepository, - std::shared_ptr urlFileDownloader) + std::shared_ptr urlFileDownloader, + std::shared_ptr fileListener) : m_gatewayKey{std::move(gatewayKey)} -, m_protocol{protocol} , m_fileDownloadDirectory{std::move(fileDownloadDirectory)} +, m_protocol{protocol} , m_outboundMessageHandler{outboundMessageHandler} , m_fileRepository{fileRepository} , m_urlFileDownloader{std::move(urlFileDownloader)} -, m_activeDownload{""} +, m_fileListener{std::move(fileListener)} , m_run{true} , m_garbageCollector(&FileDownloadService::clearDownloads, this) { + // Create the working directory if it does not exist if (!FileSystemUtils::isDirectoryPresent(m_fileDownloadDirectory)) - { FileSystemUtils::createDirectory(m_fileDownloadDirectory); + + // If we have a listener, give it the directory and lambda expression + if (m_fileListener != nullptr) + { + // Pass it the absolute path for directory + m_fileListener->receiveDirectory(getDirectory()); } } @@ -79,6 +87,11 @@ FileDownloadService::~FileDownloadService() } } +std::string FileDownloadService::getDirectory() const +{ + return std::string(FileSystemUtils::absolutePath(m_fileDownloadDirectory)); +} + void FileDownloadService::platformMessageReceived(std::shared_ptr message) { assert(message); @@ -198,6 +211,13 @@ void FileDownloadService::handle(const FileUploadInitiate& request) return; } + if (m_fileListener != nullptr && !m_fileListener->chooseToDownload(request.getName())) + { + LOG(WARN) << "File listener denied the file download"; + sendStatus(FileUploadStatus{request.getName(), FileTransferError::UNSUPPORTED_FILE_SIZE}); + return; + } + auto fileInfo = m_fileRepository.getFileInfo(request.getName()); if (!fileInfo) @@ -384,6 +404,8 @@ void FileDownloadService::deleteFile(const std::string& fileName) } m_fileRepository.remove(fileName); + if (m_fileListener != nullptr) + m_fileListener->onRemovedFile(fileName); sendFileList(); } @@ -417,6 +439,8 @@ void FileDownloadService::purgeFiles() } m_fileRepository.remove(name); + if (m_fileListener != nullptr) + m_fileListener->onRemovedFile(name); } sendFileList(); @@ -498,6 +522,8 @@ void FileDownloadService::downloadCompleted(const std::string& fileName, const s addToCommandBuffer([=] { m_fileRepository.store(FileInfo{fileName, fileHash, filePath}); sendStatus(FileUploadStatus{fileName, FileTransferStatus::FILE_READY}); + if (m_fileListener != nullptr) + m_fileListener->onNewFile(fileName); }); sendFileList(); @@ -530,6 +556,8 @@ void FileDownloadService::urlDownloadCompleted(const std::string& fileUrl, const m_fileRepository.store(FileInfo{fileName, hashStr, filePath}); sendStatus(FileUrlDownloadStatus{fileUrl, fileName}); + if (m_fileListener != nullptr) + m_fileListener->onNewFile(fileName); }); sendFileList(); diff --git a/src/service/FileDownloadService.h b/src/service/FileDownloadService.h index e23a871..e8ddb93 100644 --- a/src/service/FileDownloadService.h +++ b/src/service/FileDownloadService.h @@ -19,17 +19,17 @@ #include "GatewayInboundPlatformMessageHandler.h" #include "WolkaboutFileDownloader.h" +#include "core/model/FileDelete.h" +#include "core/model/FileUploadAbort.h" +#include "core/model/FileUploadInitiate.h" +#include "core/utilities/ByteUtils.h" +#include "core/utilities/CommandBuffer.h" #include "service/FileDownloader.h" -#include "utilities/ByteUtils.h" -#include "utilities/CommandBuffer.h" #include #include #include #include -#include -#include -#include #include #include #include @@ -40,6 +40,7 @@ namespace wolkabout class BinaryData; class JsonDownloadProtocol; class FileDelete; +class FileListener; class FileRepository; class FileUploadAbort; class FileUploadInitiate; @@ -55,13 +56,16 @@ class FileDownloadService : public PlatformMessageListener public: FileDownloadService(std::string gatewayKey, JsonDownloadProtocol& protocol, std::string fileDownloadDirectory, OutboundMessageHandler& outboundMessageHandler, FileRepository& fileRepository, - std::shared_ptr urlFileDownloader = nullptr); + std::shared_ptr urlFileDownloader = nullptr, + std::shared_ptr fileListener = nullptr); ~FileDownloadService(); FileDownloadService(const FileDownloadService&) = delete; FileDownloadService& operator=(const FileDownloadService&) = delete; + std::string getDirectory() const; + void platformMessageReceived(std::shared_ptr message) override; const Protocol& getProtocol() const override; @@ -109,6 +113,7 @@ class FileDownloadService : public PlatformMessageListener FileRepository& m_fileRepository; std::shared_ptr m_urlFileDownloader; + std::shared_ptr m_fileListener; // temporary to disallow simultaneous downloads std::string m_activeDownload; diff --git a/src/service/FileDownloader.cpp b/src/service/FileDownloader.cpp index d634829..b632a54 100644 --- a/src/service/FileDownloader.cpp +++ b/src/service/FileDownloader.cpp @@ -16,10 +16,10 @@ #include "service/FileDownloader.h" -#include "model/BinaryData.h" -#include "model/FilePacketRequest.h" -#include "utilities/FileSystemUtils.h" -#include "utilities/Logger.h" +#include "core/model/BinaryData.h" +#include "core/model/FilePacketRequest.h" +#include "core/utilities/FileSystemUtils.h" +#include "core/utilities/Logger.h" #include diff --git a/src/service/FileDownloader.h b/src/service/FileDownloader.h index 41dcd39..512f4c8 100644 --- a/src/service/FileDownloader.h +++ b/src/service/FileDownloader.h @@ -17,10 +17,10 @@ #define FILEDOWNLOADER_H #include "FileHandler.h" -#include "model/FileTransferStatus.h" -#include "utilities/ByteUtils.h" -#include "utilities/CommandBuffer.h" -#include "utilities/Timer.h" +#include "core/model/FileTransferStatus.h" +#include "core/utilities/ByteUtils.h" +#include "core/utilities/CommandBuffer.h" +#include "core/utilities/Timer.h" #include #include diff --git a/src/service/FirmwareUpdateService.cpp b/src/service/FirmwareUpdateService.cpp index 3db5c68..0394fb0 100644 --- a/src/service/FirmwareUpdateService.cpp +++ b/src/service/FirmwareUpdateService.cpp @@ -17,17 +17,17 @@ #include "service/FirmwareUpdateService.h" #include "OutboundMessageHandler.h" -#include "model/FirmwareUpdateAbort.h" -#include "model/FirmwareUpdateInstall.h" -#include "model/FirmwareVersion.h" -#include "model/Message.h" +#include "core/model/FirmwareUpdateAbort.h" +#include "core/model/FirmwareUpdateInstall.h" +#include "core/model/FirmwareVersion.h" +#include "core/model/Message.h" +#include "core/protocol/json/JsonDFUProtocol.h" +#include "core/service/FirmwareInstaller.h" +#include "core/utilities/FileSystemUtils.h" +#include "core/utilities/Logger.h" +#include "core/utilities/StringUtils.h" #include "protocol/GatewayFirmwareUpdateProtocol.h" -#include "protocol/json/JsonDFUProtocol.h" #include "repository/FileRepository.h" -#include "service/FirmwareInstaller.h" -#include "utilities/FileSystemUtils.h" -#include "utilities/Logger.h" -#include "utilities/StringUtils.h" #include @@ -45,7 +45,6 @@ FirmwareUpdateService::FirmwareUpdateService(std::string gatewayKey, JsonDFUProt , m_outboundPlatformMessageHandler{outboundPlatformMessageHandler} , m_outboundDeviceMessageHandler{outboundDeviceMessageHandler} , m_firmwareInstaller{nullptr} -, m_currentFirmwareVersion{""} { } @@ -296,6 +295,10 @@ void FirmwareUpdateService::installGatewayFirmware(const std::string& filePath) return; } + const auto directory = StringUtils::tokenize(FIRMWARE_VERSION_FILE, "/").front(); + if (!FileSystemUtils::isDirectoryPresent(directory)) + FileSystemUtils::createDirectory(directory); + if (!FileSystemUtils::createFileWithContent(FIRMWARE_VERSION_FILE, m_currentFirmwareVersion)) { LOG(ERROR) << "Failed to create firmware version file"; @@ -305,7 +308,13 @@ void FirmwareUpdateService::installGatewayFirmware(const std::string& filePath) sendStatus(FirmwareUpdateStatus{{m_gatewayKey}, FirmwareUpdateStatus::Status::INSTALLATION}); - if (!m_firmwareInstaller->install(filePath)) + if (m_firmwareInstaller->install(filePath)) + { + LOG(INFO) << "Successfully installed gateway firmware"; + + sendStatus(FirmwareUpdateStatus{{m_gatewayKey}, FirmwareUpdateStatus::Status::COMPLETED}); + } + else { LOG(ERROR) << "Failed to install gateway firmware"; diff --git a/src/service/FirmwareUpdateService.h b/src/service/FirmwareUpdateService.h index cea9b76..7a7d126 100644 --- a/src/service/FirmwareUpdateService.h +++ b/src/service/FirmwareUpdateService.h @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #ifndef FIRMWAREUPDATESERVICE_H #define FIRMWAREUPDATESERVICE_H #include "GatewayInboundDeviceMessageHandler.h" #include "GatewayInboundPlatformMessageHandler.h" -#include "model/FirmwareUpdateStatus.h" -#include "utilities/CommandBuffer.h" +#include "core/model/FirmwareUpdateStatus.h" +#include "core/utilities/CommandBuffer.h" #include #include @@ -107,7 +108,7 @@ class FirmwareUpdateService : public DeviceMessageListener, public PlatformMessa CommandBuffer m_commandBuffer; - static const constexpr char* FIRMWARE_VERSION_FILE = ".dfu-version"; + static const constexpr char* FIRMWARE_VERSION_FILE = "version/.dfu-version"; }; } // namespace wolkabout diff --git a/src/service/GatewayUpdateService.cpp b/src/service/GatewayUpdateService.cpp index 819c1fd..b8e643b 100644 --- a/src/service/GatewayUpdateService.cpp +++ b/src/service/GatewayUpdateService.cpp @@ -17,12 +17,12 @@ #include "GatewayUpdateService.h" #include "OutboundMessageHandler.h" -#include "model/GatewayUpdateRequest.h" -#include "model/GatewayUpdateResponse.h" -#include "model/Message.h" -#include "protocol/RegistrationProtocol.h" +#include "core/model/GatewayUpdateRequest.h" +#include "core/model/GatewayUpdateResponse.h" +#include "core/model/Message.h" +#include "core/protocol/RegistrationProtocol.h" +#include "core/utilities/Logger.h" #include "repository/DeviceRepository.h" -#include "utilities/Logger.h" #include diff --git a/src/service/KeepAliveService.cpp b/src/service/KeepAliveService.cpp index 6a2ddb2..e185616 100644 --- a/src/service/KeepAliveService.cpp +++ b/src/service/KeepAliveService.cpp @@ -17,9 +17,9 @@ #include "service/KeepAliveService.h" #include "OutboundMessageHandler.h" -#include "connectivity/ConnectivityService.h" -#include "model/Message.h" -#include "protocol/StatusProtocol.h" +#include "core/connectivity/ConnectivityService.h" +#include "core/model/Message.h" +#include "core/protocol/StatusProtocol.h" namespace wolkabout { diff --git a/src/service/KeepAliveService.h b/src/service/KeepAliveService.h index 9364ec1..0479744 100644 --- a/src/service/KeepAliveService.h +++ b/src/service/KeepAliveService.h @@ -19,7 +19,7 @@ #include "ConnectionStatusListener.h" #include "InboundPlatformMessageHandler.h" -#include "utilities/Timer.h" +#include "core/utilities/Timer.h" namespace wolkabout { diff --git a/src/service/PublishingService.cpp b/src/service/PublishingService.cpp index 626af4b..8eb977a 100644 --- a/src/service/PublishingService.cpp +++ b/src/service/PublishingService.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2018 WolkAbout Technology s.r.o. + * Copyright 2021 WolkAbout Technology s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +16,19 @@ #include "PublishingService.h" -#include "connectivity/ConnectivityService.h" -#include "model/Message.h" -#include "utilities/Logger.h" +#include "core/connectivity/ConnectivityService.h" +#include "core/model/Message.h" +#include "core/utilities/Logger.h" namespace wolkabout { PublishingService::PublishingService(ConnectivityService& connectivityService, - std::unique_ptr persistence) + std::shared_ptr persistence) : m_connectivityService{connectivityService} , m_persistence{std::move(persistence)} +, m_connectedState{*this} +, m_disconnectedState{*this} +, m_currentState{&m_disconnectedState} , m_connected{false} , m_run{true} , m_worker{new std::thread(&PublishingService::run, this)} @@ -35,44 +38,97 @@ PublishingService::PublishingService(ConnectivityService& connectivityService, PublishingService::~PublishingService() { m_run = false; - m_condition.notify_one(); - m_worker->join(); + m_buffer.stop(); + if (m_worker->joinable()) + m_worker->join(); } void PublishingService::addMessage(std::shared_ptr message) { LOG(TRACE) << "PublishingService: Message added. Channel: '" << message->getChannel() << "' Payload: '" << message->getContent() << "'"; - m_persistence->push(message); - m_condition.notify_one(); + m_buffer.push(std::move(message)); } void PublishingService::connected() { m_connected = true; - m_condition.notify_one(); + m_currentState = &m_connectedState; + m_buffer.notify(); } void PublishingService::disconnected() { m_connected = false; + m_currentState = &m_disconnectedState; + m_buffer.notify(); } void PublishingService::run() { while (m_run) { - while (m_connected && !m_persistence->empty()) + auto state = m_currentState.load(); + state->run(); + } + + m_buffer.notify(); +} + +PublishingService::State::State(PublishingService& service) : m_service{service} {} + +void PublishingService::DisconnectedState::run() +{ + while (m_service.m_run && !m_service.m_connected && !m_service.m_buffer.isEmpty()) + { + const auto message = m_service.m_buffer.pop(); + if (!message) + break; + + if (!m_service.m_persistence->push(message)) + { + LOG(ERROR) << "Failed to persist message"; + } + } + + m_service.m_buffer.swapBuffers(); +} + +void PublishingService::ConnectedState::run() +{ + while (m_service.m_run && m_service.m_connected && !m_service.m_buffer.isEmpty()) + { + const auto message = m_service.m_buffer.pop(); + if (!message) + break; + + if (!m_service.m_connectivityService.publish(message)) { - const auto message = m_persistence->front(); - if (m_connectivityService.publish(message)) - { - m_persistence->pop(); - } + m_service.m_persistence->push(message); + LOG(ERROR) << "Failed to publish message"; + break; } + } + + // publish persisted until new message arrives + while (m_service.m_run && m_service.m_connected && !m_service.m_persistence->empty() && + m_service.m_buffer.isEmpty()) + { + const auto message = m_service.m_persistence->front(); + if (!message) + break; - std::unique_lock locker{m_lock}; - m_condition.wait(locker); + if (m_service.m_connectivityService.publish(message)) + { + m_service.m_persistence->pop(); + } + else + { + LOG(ERROR) << "Failed to publish message"; + } } + + m_service.m_buffer.swapBuffers(); } + } // namespace wolkabout diff --git a/src/service/PublishingService.h b/src/service/PublishingService.h index 2b2c603..7e2e9b7 100644 --- a/src/service/PublishingService.h +++ b/src/service/PublishingService.h @@ -1,5 +1,5 @@ /* - * Copyright 2018 WolkAbout Technology s.r.o. + * Copyright 2021 WolkAbout Technology s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ #include "ConnectionStatusListener.h" #include "OutboundMessageHandler.h" +#include "core/utilities/Buffer.h" #include "persistence/GatewayPersistence.h" #include @@ -34,7 +35,7 @@ class ConnectivityService; class PublishingService : public OutboundMessageHandler, public ConnectionStatusListener { public: - PublishingService(ConnectivityService& connectivityService, std::unique_ptr persistence); + PublishingService(ConnectivityService& connectivityService, std::shared_ptr persistence); ~PublishingService(); void addMessage(std::shared_ptr message) override; @@ -43,16 +44,44 @@ class PublishingService : public OutboundMessageHandler, public ConnectionStatus void disconnected() override; private: + class State + { + public: + State(PublishingService& service); + virtual ~State() = default; + virtual void run() = 0; + + protected: + PublishingService& m_service; + }; + + class DisconnectedState : public State + { + public: + using State::State; + void run() override; + }; + + class ConnectedState : public State + { + public: + using State::State; + void run() override; + }; + void run(); ConnectivityService& m_connectivityService; - std::unique_ptr m_persistence; + std::shared_ptr m_persistence; + ConnectedState m_connectedState; + DisconnectedState m_disconnectedState; + std::atomic m_currentState; std::atomic_bool m_connected; + Buffer> m_buffer; + std::atomic_bool m_run; - std::mutex m_lock; - std::condition_variable m_condition; std::unique_ptr m_worker; }; } // namespace wolkabout diff --git a/src/service/SubdeviceRegistrationService.cpp b/src/service/SubdeviceRegistrationService.cpp index 2561431..d76f220 100644 --- a/src/service/SubdeviceRegistrationService.cpp +++ b/src/service/SubdeviceRegistrationService.cpp @@ -17,17 +17,17 @@ #include "service/SubdeviceRegistrationService.h" #include "OutboundMessageHandler.h" -#include "model/DetailedDevice.h" -#include "model/Message.h" -#include "model/SubdeviceDeletionRequest.h" -#include "model/SubdeviceRegistrationRequest.h" -#include "model/SubdeviceRegistrationResponse.h" -#include "model/SubdeviceUpdateRequest.h" -#include "model/SubdeviceUpdateResponse.h" +#include "core/model/DetailedDevice.h" +#include "core/model/Message.h" +#include "core/model/SubdeviceDeletionRequest.h" +#include "core/model/SubdeviceRegistrationRequest.h" +#include "core/model/SubdeviceRegistrationResponse.h" +#include "core/model/SubdeviceUpdateRequest.h" +#include "core/model/SubdeviceUpdateResponse.h" +#include "core/protocol/RegistrationProtocol.h" +#include "core/utilities/Logger.h" #include "protocol/GatewaySubdeviceRegistrationProtocol.h" -#include "protocol/RegistrationProtocol.h" #include "repository/DeviceRepository.h" -#include "utilities/Logger.h" #include #include diff --git a/src/service/UrlFileDownloader.h b/src/service/UrlFileDownloader.h index ba4500a..16c4b07 100644 --- a/src/service/UrlFileDownloader.h +++ b/src/service/UrlFileDownloader.h @@ -17,7 +17,7 @@ #ifndef URLFILEDOWNLOADER_H #define URLFILEDOWNLOADER_H -#include "model/FileTransferStatus.h" +#include "core/model/FileTransferStatus.h" #include #include diff --git a/src/service/WolkaboutFileDownloader.h b/src/service/WolkaboutFileDownloader.h index d3abea0..1281076 100644 --- a/src/service/WolkaboutFileDownloader.h +++ b/src/service/WolkaboutFileDownloader.h @@ -17,7 +17,7 @@ #ifndef WOLKABOUTFILEDOWNLOADER_H #define WOLKABOUTFILEDOWNLOADER_H -#include "utilities/ByteUtils.h" +#include "core/utilities/ByteUtils.h" #include #include diff --git a/src/service/data/DataService.cpp b/src/service/data/DataService.cpp new file mode 100644 index 0000000..8f2dbc3 --- /dev/null +++ b/src/service/data/DataService.cpp @@ -0,0 +1,111 @@ +/* + * Copyright 2018 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DataService.h" + +#include "OutboundMessageHandler.h" +#include "core/InboundMessageHandler.h" +#include "core/model/Message.h" +#include "core/protocol/DataProtocol.h" +#include "core/utilities/Logger.h" +#include "protocol/GatewayDataProtocol.h" + +#include +#include + +namespace wolkabout +{ +DataService::DataService(const std::string& gatewayKey, DataProtocol& protocol, GatewayDataProtocol& gatewayProtocol, + OutboundMessageHandler& outboundPlatformMessageHandler, MessageListener* gatewayDevice) +: m_gatewayKey{gatewayKey} +, m_protocol{protocol} +, m_gatewayProtocol{gatewayProtocol} +, m_outboundPlatformMessageHandler{outboundPlatformMessageHandler} +, m_gatewayDevice{gatewayDevice} +{ +} + +void DataService::platformMessageReceived(std::shared_ptr message) +{ + LOG(TRACE) << METHOD_INFO; + + const std::string topic = message->getChannel(); + + const std::string deviceKey = m_protocol.extractDeviceKeyFromChannel(topic); + + if (deviceKey.empty()) + { + LOG(WARN) << "DataService: Failed to extract device key from channel '" << topic << "'"; + return; + } + + if (m_gatewayKey == deviceKey) + { + handleMessageForGateway(message); + } + else + { + // if message is for device remove gateway info from channel + handleMessageForDevice(message); + } +} + +const Protocol& DataService::getProtocol() const +{ + return m_protocol; +} + +void DataService::addMessage(std::shared_ptr message) +{ + LOG(TRACE) << METHOD_INFO; + + m_outboundPlatformMessageHandler.addMessage(message); +} + +void DataService::setGatewayMessageListener(MessageListener* gatewayDevice) +{ + std::lock_guard guard{m_messageListenerMutex}; + m_gatewayDevice = gatewayDevice; +} + +void DataService::routeDeviceToPlatformMessage(std::shared_ptr message) +{ + LOG(TRACE) << METHOD_INFO; + + const std::string channel = m_gatewayProtocol.routeDeviceToPlatformMessage(message->getChannel(), m_gatewayKey); + if (channel.empty()) + { + LOG(WARN) << "Failed to route device message: " << message->getChannel(); + return; + } + + const std::shared_ptr routedMessage{new Message(message->getContent(), channel)}; + + addMessage(routedMessage); +} + +void DataService::handleMessageForGateway(std::shared_ptr message) +{ + LOG(TRACE) << METHOD_INFO; + + std::lock_guard guard{m_messageListenerMutex}; + + if (m_gatewayDevice) + { + m_gatewayDevice->messageReceived(message); + } +} +} // namespace wolkabout diff --git a/src/service/DataService.h b/src/service/data/DataService.h similarity index 62% rename from src/service/DataService.h rename to src/service/data/DataService.h index 3e54df7..034d0ec 100644 --- a/src/service/DataService.h +++ b/src/service/data/DataService.h @@ -22,58 +22,50 @@ #include "InboundPlatformMessageHandler.h" #include "OutboundMessageHandler.h" -#include #include +#include #include namespace wolkabout { class DataProtocol; -class DeviceRepository; class GatewayDataProtocol; class MessageListener; -class DataService : public DeviceMessageListener, public PlatformMessageListener, public OutboundMessageHandler +class DataService : public PlatformMessageListener, public OutboundMessageHandler { public: + virtual ~DataService() = default; + DataService(const std::string& gatewayKey, DataProtocol& protocol, GatewayDataProtocol& gatewayProtocol, - DeviceRepository* deviceRepository, OutboundMessageHandler& outboundPlatformMessageHandler, - OutboundMessageHandler& outboundDeviceMessageHandler, MessageListener* gatewayDevice = nullptr); + OutboundMessageHandler& outboundPlatformMessageHandler, MessageListener* gatewayDevice = nullptr); void platformMessageReceived(std::shared_ptr message) override; - void deviceMessageReceived(std::shared_ptr message) override; - const Protocol& getProtocol() const override; - const GatewayProtocol& getGatewayProtocol() const override; - void addMessage(std::shared_ptr message) override; void setGatewayMessageListener(MessageListener* gatewayDevice); - virtual void requestActuatorStatusesForDevice(const std::string& deviceKey); - virtual void requestActuatorStatusesForAllDevices(); - -private: - void routeDeviceToPlatformMessage(std::shared_ptr message); - void routePlatformToDeviceMessage(std::shared_ptr message); + virtual void requestActuatorStatusesForDevice(const std::string& deviceKey) = 0; + virtual void requestActuatorStatusesForAllDevices() = 0; - void routeGatewayToPlatformMessage(std::shared_ptr message); - void routePlatformToGatewayMessage(std::shared_ptr message); +protected: + virtual void routeDeviceToPlatformMessage(std::shared_ptr message); const std::string m_gatewayKey; DataProtocol& m_protocol; GatewayDataProtocol& m_gatewayProtocol; - DeviceRepository* m_deviceRepository; +private: + virtual void handleMessageForDevice(std::shared_ptr message) = 0; + virtual void handleMessageForGateway(std::shared_ptr message); OutboundMessageHandler& m_outboundPlatformMessageHandler; - OutboundMessageHandler& m_outboundDeviceMessageHandler; - MessageListener* m_gatewayDevice; + std::mutex m_messageListenerMutex; }; - } // namespace wolkabout #endif diff --git a/src/service/data/ExternalDataService.cpp b/src/service/data/ExternalDataService.cpp new file mode 100644 index 0000000..f1cda3c --- /dev/null +++ b/src/service/data/ExternalDataService.cpp @@ -0,0 +1,90 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ExternalDataService.h" + +#include "core/model/Message.h" +#include "core/protocol/DataProtocol.h" +#include "core/utilities/Logger.h" + +#include + +namespace wolkabout +{ +void ExternalDataService::addSensorReading(const std::string& deviceKey, const SensorReading& reading) +{ + const std::shared_ptr message = + m_protocol.makeMessage(deviceKey, {std::make_shared(reading)}); + + addMessage(message); +} + +void ExternalDataService::addSensorReadings(const std::string& deviceKey, const std::vector& readings) +{ + if (readings.empty()) + { + return; + } + + std::vector> parsableReadings; + + std::transform(readings.begin(), readings.end(), std::back_inserter(parsableReadings), + [](const SensorReading& reading) { return std::make_shared(reading); }); + + const std::shared_ptr message = m_protocol.makeMessage(deviceKey, parsableReadings); + + addMessage(message); +} + +void ExternalDataService::addAlarm(const std::string& deviceKey, const Alarm& alarm) +{ + const std::shared_ptr message = m_protocol.makeMessage(deviceKey, {std::make_shared(alarm)}); + + addMessage(message); +} + +void ExternalDataService::addActuatorStatus(const std::string& deviceKey, const ActuatorStatus& status) +{ + const std::shared_ptr message = + m_protocol.makeMessage(deviceKey, {std::make_shared(status)}); + + addMessage(message); +} + +void ExternalDataService::addConfiguration(const std::string& deviceKey, + const std::vector& configurations) +{ + const std::shared_ptr message = m_protocol.makeMessage(deviceKey, configurations); + + addMessage(message); +} + +void ExternalDataService::requestActuatorStatusesForDevice(const std::string& /*deviceKey*/) +{ + LOG(WARN) << "Not requesting actuator status for device"; +} + +void ExternalDataService::requestActuatorStatusesForAllDevices() +{ + LOG(WARN) << "Not handling message for devices"; +} + +void ExternalDataService::handleMessageForDevice(std::shared_ptr /*message*/) +{ + LOG(WARN) << "Not handling message for device"; +} + +} // namespace wolkabout diff --git a/src/service/data/ExternalDataService.h b/src/service/data/ExternalDataService.h new file mode 100644 index 0000000..e060638 --- /dev/null +++ b/src/service/data/ExternalDataService.h @@ -0,0 +1,44 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WOLKABOUT_EXTERNALDATASERVICE_H +#define WOLKABOUT_EXTERNALDATASERVICE_H + +#include "DataService.h" +#include "api/DataHandler.h" + +namespace wolkabout +{ +class ExternalDataService : public DataService +{ +public: + using DataService::DataService; + + void addSensorReading(const std::string& deviceKey, const SensorReading& reading); + void addSensorReadings(const std::string& deviceKey, const std::vector& readings); + void addAlarm(const std::string& deviceKey, const Alarm& alarm); + void addActuatorStatus(const std::string& deviceKey, const ActuatorStatus& status); + void addConfiguration(const std::string& deviceKey, const std::vector& configurations); + + void requestActuatorStatusesForDevice(const std::string& deviceKey) override; + void requestActuatorStatusesForAllDevices() override; + +private: + void handleMessageForDevice(std::shared_ptr message) override; +}; +} // namespace wolkabout + +#endif // WOLKABOUT_EXTERNALDATASERVICE_H diff --git a/src/service/GatewayDataService.cpp b/src/service/data/GatewayDataService.cpp similarity index 95% rename from src/service/GatewayDataService.cpp rename to src/service/data/GatewayDataService.cpp index fc1a39d..a8a65dc 100644 --- a/src/service/GatewayDataService.cpp +++ b/src/service/data/GatewayDataService.cpp @@ -14,18 +14,18 @@ * limitations under the License. */ -#include "service/GatewayDataService.h" +#include "GatewayDataService.h" #include "OutboundMessageHandler.h" -#include "connectivity/ConnectivityService.h" -#include "model/ActuatorGetCommand.h" -#include "model/ActuatorSetCommand.h" -#include "model/ConfigurationSetCommand.h" -#include "model/Message.h" -#include "model/SensorReading.h" -#include "persistence/Persistence.h" -#include "protocol/DataProtocol.h" -#include "utilities/Logger.h" +#include "core/connectivity/ConnectivityService.h" +#include "core/model/ActuatorGetCommand.h" +#include "core/model/ActuatorSetCommand.h" +#include "core/model/ConfigurationSetCommand.h" +#include "core/model/Message.h" +#include "core/model/SensorReading.h" +#include "core/persistence/Persistence.h" +#include "core/protocol/DataProtocol.h" +#include "core/utilities/Logger.h" #include #include diff --git a/src/service/GatewayDataService.h b/src/service/data/GatewayDataService.h similarity index 96% rename from src/service/GatewayDataService.h rename to src/service/data/GatewayDataService.h index 9a85040..7bbb520 100644 --- a/src/service/GatewayDataService.h +++ b/src/service/data/GatewayDataService.h @@ -17,9 +17,9 @@ #ifndef GATEWAYDATASERVICE_H #define GATEWAYDATASERVICE_H -#include "InboundMessageHandler.h" -#include "model/ActuatorStatus.h" -#include "model/ConfigurationItem.h" +#include "core/InboundMessageHandler.h" +#include "core/model/ActuatorStatus.h" +#include "core/model/ConfigurationItem.h" #include #include diff --git a/src/service/DataService.cpp b/src/service/data/InternalDataService.cpp similarity index 61% rename from src/service/DataService.cpp rename to src/service/data/InternalDataService.cpp index 937157c..f0b4a47 100644 --- a/src/service/DataService.cpp +++ b/src/service/data/InternalDataService.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2018 WolkAbout Technology s.r.o. + * Copyright 2021 WolkAbout Technology s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,63 +14,36 @@ * limitations under the License. */ -#include "service/DataService.h" +#include "InternalDataService.h" -#include "InboundMessageHandler.h" -#include "OutboundMessageHandler.h" -#include "model/ActuatorGetCommand.h" -#include "model/ActuatorStatus.h" -#include "model/DetailedDevice.h" -#include "model/Message.h" -#include "protocol/DataProtocol.h" +#include "core/model/DetailedDevice.h" +#include "core/model/Message.h" +#include "core/protocol/DataProtocol.h" +#include "core/utilities/Logger.h" #include "protocol/GatewayDataProtocol.h" #include "repository/DeviceRepository.h" -#include "utilities/Logger.h" -#include #include namespace wolkabout { -DataService::DataService(const std::string& gatewayKey, DataProtocol& protocol, GatewayDataProtocol& gatewayProtocol, - DeviceRepository* deviceRepository, OutboundMessageHandler& outboundPlatformMessageHandler, - OutboundMessageHandler& outboundDeviceMessageHandler, MessageListener* gatewayDevice) -: m_gatewayKey{gatewayKey} -, m_protocol{protocol} -, m_gatewayProtocol{gatewayProtocol} +InternalDataService::InternalDataService(const std::string& gatewayKey, DataProtocol& protocol, + GatewayDataProtocol& gatewayProtocol, DeviceRepository* deviceRepository, + OutboundMessageHandler& outboundPlatformMessageHandler, + OutboundMessageHandler& outboundDeviceMessageHandler, + MessageListener* gatewayDevice) +: DataService(gatewayKey, protocol, gatewayProtocol, outboundPlatformMessageHandler, gatewayDevice) , m_deviceRepository{deviceRepository} -, m_outboundPlatformMessageHandler{outboundPlatformMessageHandler} , m_outboundDeviceMessageHandler{outboundDeviceMessageHandler} -, m_gatewayDevice{gatewayDevice} { } -void DataService::platformMessageReceived(std::shared_ptr message) +const GatewayProtocol& InternalDataService::getGatewayProtocol() const { - LOG(TRACE) << METHOD_INFO; - - const std::string topic = message->getChannel(); - - const std::string deviceKey = m_protocol.extractDeviceKeyFromChannel(topic); - - if (deviceKey.empty()) - { - LOG(WARN) << "DataService: Failed to extract device key from channel '" << topic << "'"; - return; - } - - if (m_gatewayKey == deviceKey) - { - routePlatformToGatewayMessage(message); - } - else - { - // if message is for device remove gateway info from channel - routePlatformToDeviceMessage(message); - } + return m_gatewayProtocol; } -void DataService::deviceMessageReceived(std::shared_ptr message) +void InternalDataService::deviceMessageReceived(std::shared_ptr message) { LOG(TRACE) << METHOD_INFO; @@ -139,27 +112,7 @@ void DataService::deviceMessageReceived(std::shared_ptr message) routeDeviceToPlatformMessage(message); } -const Protocol& DataService::getProtocol() const -{ - return m_protocol; -} - -const GatewayProtocol& DataService::getGatewayProtocol() const -{ - return m_gatewayProtocol; -} - -void DataService::addMessage(std::shared_ptr message) -{ - routeGatewayToPlatformMessage(message); -} - -void DataService::setGatewayMessageListener(MessageListener* gatewayDevice) -{ - m_gatewayDevice = gatewayDevice; -} - -void DataService::requestActuatorStatusesForDevice(const std::string& deviceKey) +void InternalDataService::requestActuatorStatusesForDevice(const std::string& deviceKey) { if (!m_deviceRepository) { @@ -182,28 +135,18 @@ void DataService::requestActuatorStatusesForDevice(const std::string& deviceKey) } } -void DataService::requestActuatorStatusesForAllDevices() +void InternalDataService::requestActuatorStatusesForAllDevices() { std::shared_ptr message = m_gatewayProtocol.makeMessage("", ActuatorGetCommand("")); m_outboundDeviceMessageHandler.addMessage(message); } -void DataService::routeDeviceToPlatformMessage(std::shared_ptr message) +void InternalDataService::handleMessageForDevice(std::shared_ptr message) { - LOG(TRACE) << METHOD_INFO; - - const std::string channel = m_gatewayProtocol.routeDeviceToPlatformMessage(message->getChannel(), m_gatewayKey); - if (channel.empty()) - { - LOG(WARN) << "Failed to route device message: " << message->getChannel(); - return; - } - - const std::shared_ptr routedMessage{new Message(message->getContent(), channel)}; - m_outboundPlatformMessageHandler.addMessage(routedMessage); + routePlatformToDeviceMessage(message); } -void DataService::routePlatformToDeviceMessage(std::shared_ptr message) +void InternalDataService::routePlatformToDeviceMessage(std::shared_ptr message) { LOG(TRACE) << METHOD_INFO; @@ -215,23 +158,7 @@ void DataService::routePlatformToDeviceMessage(std::shared_ptr message) } const std::shared_ptr routedMessage{new Message(message->getContent(), channel)}; - m_outboundDeviceMessageHandler.addMessage(routedMessage); -} -void DataService::routeGatewayToPlatformMessage(std::shared_ptr message) -{ - LOG(TRACE) << METHOD_INFO; - - m_outboundPlatformMessageHandler.addMessage(message); -} - -void DataService::routePlatformToGatewayMessage(std::shared_ptr message) -{ - LOG(TRACE) << METHOD_INFO; - - if (m_gatewayDevice) - { - m_gatewayDevice->messageReceived(message); - } + m_outboundDeviceMessageHandler.addMessage(routedMessage); } } // namespace wolkabout diff --git a/src/service/data/InternalDataService.h b/src/service/data/InternalDataService.h new file mode 100644 index 0000000..006d3e8 --- /dev/null +++ b/src/service/data/InternalDataService.h @@ -0,0 +1,52 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WOLKABOUT_INTERNALDATASERVICE_H +#define WOLKABOUT_INTERNALDATASERVICE_H + +#include "DataService.h" +#include "InboundDeviceMessageHandler.h" + +namespace wolkabout +{ +class DeviceRepository; + +class InternalDataService : public DataService, public DeviceMessageListener +{ +public: + InternalDataService(const std::string& gatewayKey, DataProtocol& protocol, GatewayDataProtocol& gatewayProtocol, + DeviceRepository* deviceRepository, OutboundMessageHandler& outboundPlatformMessageHandler, + OutboundMessageHandler& outboundDeviceMessageHandler, MessageListener* gatewayDevice = nullptr); + + const GatewayProtocol& getGatewayProtocol() const override; + + void deviceMessageReceived(std::shared_ptr message) override; + + void requestActuatorStatusesForDevice(const std::string& deviceKey) override; + void requestActuatorStatusesForAllDevices() override; + +private: + void handleMessageForDevice(std::shared_ptr message) override; + + void routePlatformToDeviceMessage(std::shared_ptr message); + + DeviceRepository* m_deviceRepository; + + OutboundMessageHandler& m_outboundDeviceMessageHandler; +}; +} // namespace wolkabout + +#endif // WOLKABOUT_INTERNALDATASERVICE_H diff --git a/src/service/status/DeviceStatusService.cpp b/src/service/status/DeviceStatusService.cpp new file mode 100644 index 0000000..9bb3faa --- /dev/null +++ b/src/service/status/DeviceStatusService.cpp @@ -0,0 +1,97 @@ +/* + * Copyright 2020 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DeviceStatusService.h" + +#include "ConnectionStatusListener.h" +#include "OutboundMessageHandler.h" +#include "core/model/DeviceStatus.h" +#include "core/model/Message.h" +#include "core/protocol/StatusProtocol.h" +#include "core/utilities/Logger.h" +#include "protocol/GatewayStatusProtocol.h" +#include "repository/DeviceRepository.h" + +namespace wolkabout +{ +DeviceStatusService::DeviceStatusService(std::string gatewayKey, StatusProtocol& protocol, + OutboundMessageHandler& outboundPlatformMessageHandler) +: m_gatewayKey{std::move(gatewayKey)} +, m_protocol{protocol} +, m_outboundPlatformMessageHandler{outboundPlatformMessageHandler} +{ +} + +void DeviceStatusService::platformMessageReceived(std::shared_ptr message) +{ + LOG(TRACE) << METHOD_INFO; + + const std::string topic = message->getChannel(); + + if (m_protocol.isStatusRequestMessage(*message)) + { + const std::string deviceKey = m_protocol.extractDeviceKeyFromChannel(topic); + + if (deviceKey.empty()) + { + return; + } + + requestDeviceStatus(deviceKey); + } + else if (m_protocol.isStatusConfirmMessage(*message)) + { + } + else + { + LOG(WARN) << "Message channel not parsed: " << topic; + } +} + +const Protocol& DeviceStatusService::getProtocol() const +{ + return m_protocol; +} + +void DeviceStatusService::sendStatusUpdateForDevice(const std::string& deviceKey, DeviceStatus::Status status) +{ + std::shared_ptr statusMessage = + m_protocol.makeStatusUpdateMessage(m_gatewayKey, DeviceStatus{deviceKey, status}); + + if (!statusMessage) + { + LOG(WARN) << "Failed to create status update message for device: " << deviceKey; + return; + } + + m_outboundPlatformMessageHandler.addMessage(statusMessage); +} + +void DeviceStatusService::sendStatusResponseForDevice(const std::string& deviceKey, DeviceStatus::Status status) +{ + std::shared_ptr statusMessage = + m_protocol.makeStatusResponseMessage(m_gatewayKey, DeviceStatus{deviceKey, status}); + + if (!statusMessage) + { + LOG(WARN) << "Failed to create status response message for device: " << deviceKey; + return; + } + + m_outboundPlatformMessageHandler.addMessage(statusMessage); +} + +} // namespace wolkabout diff --git a/src/service/status/DeviceStatusService.h b/src/service/status/DeviceStatusService.h new file mode 100644 index 0000000..9ed7e38 --- /dev/null +++ b/src/service/status/DeviceStatusService.h @@ -0,0 +1,64 @@ +/* + * Copyright 2018 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DEVICESTATUSSERVICE_H +#define DEVICESTATUSSERVICE_H + +#include "ConnectionStatusListener.h" +#include "InboundDeviceMessageHandler.h" +#include "InboundPlatformMessageHandler.h" +#include "core/model/DeviceStatus.h" +#include "core/utilities/Timer.h" + +#include +#include +#include +#include +#include + +namespace wolkabout +{ +class DeviceRepository; +class ConnectionStatusListener; +class GatewayStatusProtocol; +class OutboundMessageHandler; +class StatusProtocol; + +class DeviceStatusService : public PlatformMessageListener, public ConnectionStatusListener +{ +public: + DeviceStatusService(std::string gatewayKey, StatusProtocol& protocol, + OutboundMessageHandler& outboundPlatformMessageHandler); + + void platformMessageReceived(std::shared_ptr message) override; + + const Protocol& getProtocol() const override; + +protected: + void sendStatusUpdateForDevice(const std::string& deviceKey, DeviceStatus::Status status); + void sendStatusResponseForDevice(const std::string& deviceKey, DeviceStatus::Status status); + + const std::string m_gatewayKey; + StatusProtocol& m_protocol; + +private: + virtual void requestDeviceStatus(const std::string& deviceKey) = 0; + + OutboundMessageHandler& m_outboundPlatformMessageHandler; +}; +} // namespace wolkabout + +#endif // DEVICESTATUSSERVICE_H diff --git a/src/service/status/ExternalDeviceStatusService.cpp b/src/service/status/ExternalDeviceStatusService.cpp new file mode 100644 index 0000000..5a60dd7 --- /dev/null +++ b/src/service/status/ExternalDeviceStatusService.cpp @@ -0,0 +1,37 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ExternalDeviceStatusService.h" + +#include "core/utilities/Logger.h" + +namespace wolkabout +{ +void ExternalDeviceStatusService::connected() {} + +void ExternalDeviceStatusService::disconnected() {} + +void ExternalDeviceStatusService::addDeviceStatus(const DeviceStatus& status) +{ + sendStatusUpdateForDevice(status.getDeviceKey(), status.getStatus()); +} + +void ExternalDeviceStatusService::requestDeviceStatus(const std::string& /*deviceKey*/) +{ + LOG(WARN) << "Not handling device status request"; +} + +} // namespace wolkabout diff --git a/src/service/status/ExternalDeviceStatusService.h b/src/service/status/ExternalDeviceStatusService.h new file mode 100644 index 0000000..ecec5d7 --- /dev/null +++ b/src/service/status/ExternalDeviceStatusService.h @@ -0,0 +1,39 @@ +/* + * Copyright 2021 WolkAbout Technology s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EXTERNALDEVICESTATUSSERVICE_H +#define EXTERNALDEVICESTATUSSERVICE_H + +#include "DeviceStatusService.h" + +namespace wolkabout +{ +class ExternalDeviceStatusService : public DeviceStatusService +{ +public: + using DeviceStatusService::DeviceStatusService; + + void connected() override; + void disconnected() override; + + void addDeviceStatus(const DeviceStatus& status); + +private: + void requestDeviceStatus(const std::string& deviceKey) override; +}; +} // namespace wolkabout + +#endif // EXTERNALDEVICESTATUSSERVICE_H diff --git a/src/service/DeviceStatusService.cpp b/src/service/status/InternalDeviceStatusService.cpp similarity index 68% rename from src/service/DeviceStatusService.cpp rename to src/service/status/InternalDeviceStatusService.cpp index 97794c8..8c19616 100644 --- a/src/service/DeviceStatusService.cpp +++ b/src/service/status/InternalDeviceStatusService.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2020 WolkAbout Technology s.r.o. + * Copyright 2021 WolkAbout Technology s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,16 +14,13 @@ * limitations under the License. */ -#include "service/DeviceStatusService.h" +#include "InternalDeviceStatusService.h" -#include "ConnectionStatusListener.h" #include "OutboundMessageHandler.h" -#include "model/DeviceStatus.h" -#include "model/Message.h" +#include "core/model/Message.h" +#include "core/utilities/Logger.h" #include "protocol/GatewayStatusProtocol.h" -#include "protocol/StatusProtocol.h" #include "repository/DeviceRepository.h" -#include "utilities/Logger.h" namespace { @@ -32,49 +29,22 @@ const std::chrono::seconds STATUS_RESPONSE_TIMEOUT{5}; namespace wolkabout { -DeviceStatusService::DeviceStatusService(std::string gatewayKey, StatusProtocol& protocol, - GatewayStatusProtocol& gatewayProtocol, DeviceRepository* deviceRepository, - OutboundMessageHandler& outboundPlatformMessageHandler, - OutboundMessageHandler& outboundDeviceMessageHandler, - std::chrono::seconds statusRequestInterval) -: m_gatewayKey{std::move(gatewayKey)} -, m_protocol{protocol} +InternalDeviceStatusService::InternalDeviceStatusService(std::string gatewayKey, StatusProtocol& protocol, + GatewayStatusProtocol& gatewayProtocol, + DeviceRepository* deviceRepository, + OutboundMessageHandler& outboundPlatformMessageHandler, + OutboundMessageHandler& outboundDeviceMessageHandler, + std::chrono::seconds statusRequestInterval) +: DeviceStatusService(gatewayKey, protocol, outboundPlatformMessageHandler) , m_gatewayProtocol{gatewayProtocol} , m_deviceRepository{deviceRepository} -, m_outboundPlatformMessageHandler{outboundPlatformMessageHandler} , m_outboundDeviceMessageHandler{outboundDeviceMessageHandler} , m_statusRequestInterval{statusRequestInterval} , m_statusResponseInterval{STATUS_RESPONSE_TIMEOUT} { } -void DeviceStatusService::platformMessageReceived(std::shared_ptr message) -{ - LOG(TRACE) << METHOD_INFO; - - const std::string topic = message->getChannel(); - - if (m_protocol.isStatusRequestMessage(*message)) - { - const std::string deviceKey = m_protocol.extractDeviceKeyFromChannel(topic); - - if (deviceKey.empty()) - { - return; - } - - sendStatusRequestForDevice(deviceKey); - } - else if (m_protocol.isStatusConfirmMessage(*message)) - { - } - else - { - LOG(WARN) << "Message channel not parsed: " << topic; - } -} - -void DeviceStatusService::deviceMessageReceived(std::shared_ptr message) +void InternalDeviceStatusService::deviceMessageReceived(std::shared_ptr message) { LOG(TRACE) << METHOD_INFO; @@ -149,17 +119,26 @@ void DeviceStatusService::deviceMessageReceived(std::shared_ptr message } } -const Protocol& DeviceStatusService::getProtocol() const +const GatewayProtocol& InternalDeviceStatusService::getGatewayProtocol() const { - return m_protocol; + return m_gatewayProtocol; } -const GatewayProtocol& DeviceStatusService::getGatewayProtocol() const +void InternalDeviceStatusService::connected() { - return m_gatewayProtocol; + requestDevicesStatus(); + + m_requestTimer.run(std::chrono::duration_cast(m_statusRequestInterval), + [=] { requestDevicesStatus(); }); +} + +void InternalDeviceStatusService::disconnected() +{ + m_requestTimer.stop(); + m_responseTimer.stop(); } -void DeviceStatusService::sendLastKnownStatusForDevice(const std::string& deviceKey) +void InternalDeviceStatusService::sendLastKnownStatusForDevice(const std::string& deviceKey) { if (containsDeviceStatus(deviceKey)) { @@ -168,21 +147,12 @@ void DeviceStatusService::sendLastKnownStatusForDevice(const std::string& device } } -void DeviceStatusService::connected() -{ - requestDevicesStatus(); - - m_requestTimer.run(std::chrono::duration_cast(m_statusRequestInterval), - [=] { requestDevicesStatus(); }); -} - -void DeviceStatusService::disconnected() +void InternalDeviceStatusService::requestDeviceStatus(const std::string& deviceKey) { - m_requestTimer.stop(); - m_responseTimer.stop(); + sendStatusRequestForDevice(deviceKey); } -void DeviceStatusService::requestDevicesStatus() +void InternalDeviceStatusService::requestDevicesStatus() { if (m_deviceRepository) { @@ -207,7 +177,7 @@ void DeviceStatusService::requestDevicesStatus() } } -void DeviceStatusService::validateDevicesStatus() +void InternalDeviceStatusService::validateDevicesStatus() { if (!m_deviceRepository) { @@ -248,7 +218,7 @@ void DeviceStatusService::validateDevicesStatus() } } -void DeviceStatusService::sendStatusRequestForDevice(const std::string& deviceKey) +void InternalDeviceStatusService::sendStatusRequestForDevice(const std::string& deviceKey) { std::shared_ptr message = m_gatewayProtocol.makeDeviceStatusRequestMessage(deviceKey); if (!message) @@ -260,7 +230,7 @@ void DeviceStatusService::sendStatusRequestForDevice(const std::string& deviceKe m_outboundDeviceMessageHandler.addMessage(message); } -void DeviceStatusService::sendStatusRequestForAllDevices() +void InternalDeviceStatusService::sendStatusRequestForAllDevices() { std::shared_ptr message = m_gatewayProtocol.makeDeviceStatusRequestMessage(""); if (!message) @@ -272,42 +242,14 @@ void DeviceStatusService::sendStatusRequestForAllDevices() m_outboundDeviceMessageHandler.addMessage(message); } -void DeviceStatusService::sendStatusResponseForDevice(const std::string& deviceKey, DeviceStatus::Status status) -{ - std::shared_ptr statusMessage = - m_protocol.makeStatusResponseMessage(m_gatewayKey, DeviceStatus{deviceKey, status}); - - if (!statusMessage) - { - LOG(WARN) << "Failed to create status response message for device: " << deviceKey; - return; - } - - m_outboundPlatformMessageHandler.addMessage(statusMessage); -} - -void DeviceStatusService::sendStatusUpdateForDevice(const std::string& deviceKey, DeviceStatus::Status status) -{ - std::shared_ptr statusMessage = - m_protocol.makeStatusUpdateMessage(m_gatewayKey, DeviceStatus{deviceKey, status}); - - if (!statusMessage) - { - LOG(WARN) << "Failed to create status update message for device: " << deviceKey; - return; - } - - m_outboundPlatformMessageHandler.addMessage(statusMessage); -} - -bool DeviceStatusService::containsDeviceStatus(const std::string& deviceKey) +bool InternalDeviceStatusService::containsDeviceStatus(const std::string& deviceKey) { std::lock_guard lg{m_deviceStatusMutex}; return m_deviceStatuses.find(deviceKey) != m_deviceStatuses.end(); } -std::pair DeviceStatusService::getDeviceStatus(const std::string& deviceKey) +std::pair InternalDeviceStatusService::getDeviceStatus(const std::string& deviceKey) { std::lock_guard lg{m_deviceStatusMutex}; @@ -321,11 +263,10 @@ std::pair DeviceStatusService::getDeviceStatu return std::make_pair(0, DeviceStatus::Status::OFFLINE); } -void DeviceStatusService::logDeviceStatus(const std::string& deviceKey, DeviceStatus::Status status) +void InternalDeviceStatusService::logDeviceStatus(const std::string& deviceKey, DeviceStatus::Status status) { std::lock_guard lg{m_deviceStatusMutex}; m_deviceStatuses[deviceKey] = std::make_pair(std::time(nullptr), status); } - } // namespace wolkabout diff --git a/src/service/DeviceStatusService.h b/src/service/status/InternalDeviceStatusService.h similarity index 53% rename from src/service/DeviceStatusService.h rename to src/service/status/InternalDeviceStatusService.h index 6961d72..0223c62 100644 --- a/src/service/DeviceStatusService.h +++ b/src/service/status/InternalDeviceStatusService.h @@ -1,5 +1,5 @@ /* - * Copyright 2018 WolkAbout Technology s.r.o. + * Copyright 2021 WolkAbout Technology s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,72 +14,48 @@ * limitations under the License. */ -#ifndef DEVICESTATUSSERVICE_H -#define DEVICESTATUSSERVICE_H +#ifndef INTERNALDEVICESTATUSSERVICE_H +#define INTERNALDEVICESTATUSSERVICE_H -#include "ConnectionStatusListener.h" -#include "InboundDeviceMessageHandler.h" -#include "InboundPlatformMessageHandler.h" -#include "model/DeviceStatus.h" -#include "utilities/Timer.h" - -#include -#include -#include -#include -#include +#include "DeviceStatusService.h" namespace wolkabout { -class DeviceRepository; -class ConnectionStatusListener; -class GatewayStatusProtocol; -class OutboundMessageHandler; -class StatusProtocol; - -class DeviceStatusService : public DeviceMessageListener, - public PlatformMessageListener, - public ConnectionStatusListener +class InternalDeviceStatusService : public DeviceStatusService, public DeviceMessageListener { public: - DeviceStatusService(std::string gatewayKey, StatusProtocol& protocol, GatewayStatusProtocol& gatewayProtocol, - DeviceRepository* deviceRepository, OutboundMessageHandler& outboundPlatformMessageHandler, - OutboundMessageHandler& outboundDeviceMessageHandler, - std::chrono::seconds statusRequestInterval); - - void platformMessageReceived(std::shared_ptr message) override; + InternalDeviceStatusService(std::string gatewayKey, StatusProtocol& protocol, + GatewayStatusProtocol& gatewayProtocol, DeviceRepository* deviceRepository, + OutboundMessageHandler& outboundPlatformMessageHandler, + OutboundMessageHandler& outboundDeviceMessageHandler, + std::chrono::seconds statusRequestInterval); void deviceMessageReceived(std::shared_ptr message) override; - const Protocol& getProtocol() const override; - const GatewayProtocol& getGatewayProtocol() const override; - void sendLastKnownStatusForDevice(const std::string& deviceKey); - void connected() override; void disconnected() override; + void sendLastKnownStatusForDevice(const std::string& deviceKey); + private: + void requestDeviceStatus(const std::string& deviceKey) override; + void requestDevicesStatus(); void validateDevicesStatus(); void sendStatusRequestForDevice(const std::string& deviceKey); void sendStatusRequestForAllDevices(); - void sendStatusResponseForDevice(const std::string& deviceKey, DeviceStatus::Status status); - void sendStatusUpdateForDevice(const std::string& deviceKey, DeviceStatus::Status status); bool containsDeviceStatus(const std::string& deviceKey); std::pair getDeviceStatus(const std::string& deviceKey); void logDeviceStatus(const std::string& deviceKey, DeviceStatus::Status status); - const std::string m_gatewayKey; - StatusProtocol& m_protocol; GatewayStatusProtocol& m_gatewayProtocol; DeviceRepository* m_deviceRepository; - OutboundMessageHandler& m_outboundPlatformMessageHandler; OutboundMessageHandler& m_outboundDeviceMessageHandler; const std::chrono::seconds m_statusRequestInterval; @@ -92,4 +68,4 @@ class DeviceStatusService : public DeviceMessageListener, }; } // namespace wolkabout -#endif // DEVICESTATUSSERVICE_H +#endif // INTERNALDEVICESTATUSSERVICE_H diff --git a/tests/DataServiceTests.cpp b/tests/DataServiceTests.cpp index 2ee4198..c28e18e 100644 --- a/tests/DataServiceTests.cpp +++ b/tests/DataServiceTests.cpp @@ -1,10 +1,11 @@ #include "MockRepository.h" #include "OutboundMessageHandler.h" -#include "model/Message.h" +#include "core/model/Message.h" +#include "core/protocol/json/JsonProtocol.h" #include "protocol/json/JsonGatewayDataProtocol.h" -#include "protocol/json/JsonProtocol.h" #include "repository/SQLiteDeviceRepository.h" -#include "service/DataService.h" +#include "service/data/DataService.h" +#include "service/data/InternalDataService.h" #include @@ -49,9 +50,9 @@ class DataService : public ::testing::Test std::unique_ptr(new PlatformOutboundMessageHandler()); deviceOutboundMessageHandler = std::unique_ptr(new DeviceOutboundMessageHandler()); - dataService = std::unique_ptr( - new wolkabout::DataService(GATEWAY_KEY, *protocol, *gateawayProtocol, deviceRepository.get(), - *platformOutboundMessageHandler, *deviceOutboundMessageHandler, nullptr)); + dataService = std::unique_ptr( + new wolkabout::InternalDataService(GATEWAY_KEY, *protocol, *gateawayProtocol, deviceRepository.get(), + *platformOutboundMessageHandler, *deviceOutboundMessageHandler, nullptr)); } void TearDown() override { remove(DEVICE_REPOSITORY_PATH); } @@ -61,7 +62,7 @@ class DataService : public ::testing::Test std::unique_ptr deviceRepository; std::unique_ptr platformOutboundMessageHandler; std::unique_ptr deviceOutboundMessageHandler; - std::unique_ptr dataService; + std::unique_ptr dataService; static constexpr const char* DEVICE_REPOSITORY_PATH = "testsDeviceRepository.db"; static constexpr const char* GATEWAY_KEY = "GATEWAY_KEY"; diff --git a/tests/DeviceRegistrationProtocolTests.cpp b/tests/DeviceRegistrationProtocolTests.cpp index b3668ce..65eb9f8 100644 --- a/tests/DeviceRegistrationProtocolTests.cpp +++ b/tests/DeviceRegistrationProtocolTests.cpp @@ -1,4 +1,4 @@ -#include "model/Message.h" +#include "core/model/Message.h" #define private public #define protected public #include "protocol/json/JsonGatewaySubdeviceRegistrationProtocol.h" diff --git a/tests/DeviceRegistrationServiceTests.cpp b/tests/DeviceRegistrationServiceTests.cpp index 067860a..f0519a1 100644 --- a/tests/DeviceRegistrationServiceTests.cpp +++ b/tests/DeviceRegistrationServiceTests.cpp @@ -1,13 +1,13 @@ #include "OutboundMessageHandler.h" +#include "core/model/GatewayUpdateRequest.h" +#include "core/model/GatewayUpdateResponse.h" +#include "core/model/Message.h" +#include "core/model/SubdeviceRegistrationRequest.h" +#include "core/model/SubdeviceRegistrationResponse.h" +#include "core/protocol/json/JsonRegistrationProtocol.h" #include "model/GatewayDevice.h" -#include "model/GatewayUpdateRequest.h" -#include "model/GatewayUpdateResponse.h" -#include "model/Message.h" #include "model/SubdeviceManagement.h" -#include "model/SubdeviceRegistrationRequest.h" -#include "model/SubdeviceRegistrationResponse.h" #include "protocol/json/JsonGatewaySubdeviceRegistrationProtocol.h" -#include "protocol/json/JsonRegistrationProtocol.h" #include "repository/DeviceRepository.h" #include "repository/SQLiteDeviceRepository.h" #include "service/GatewayUpdateService.h" diff --git a/tests/DeviceStatusServiceTests.cpp b/tests/DeviceStatusServiceTests.cpp index 4f40b46..be578f0 100644 --- a/tests/DeviceStatusServiceTests.cpp +++ b/tests/DeviceStatusServiceTests.cpp @@ -2,11 +2,12 @@ #include "MockConnectionStatusListener.h" #include "MockRepository.h" #include "OutboundMessageHandler.h" -#include "model/Message.h" +#include "core/model/Message.h" +#include "core/protocol/json/JsonStatusProtocol.h" #include "protocol/json/JsonGatewayStatusProtocol.h" -#include "protocol/json/JsonStatusProtocol.h" #include "repository/SQLiteDeviceRepository.h" -#include "service/DeviceStatusService.h" +#include "service/status/DeviceStatusService.h" +#include "service/status/InternalDeviceStatusService.h" #include @@ -51,9 +52,10 @@ class DeviceStatusService : public ::testing::Test std::unique_ptr(new PlatformOutboundMessageHandler()); deviceOutboundMessageHandler = std::unique_ptr(new DeviceOutboundMessageHandler()); - deviceStatusService = std::unique_ptr(new wolkabout::DeviceStatusService( - GATEWAY_KEY, *protocol, *gatewayProtocol, deviceRepository.get(), *platformOutboundMessageHandler, - *deviceOutboundMessageHandler, std::chrono::seconds{60})); + deviceStatusService = + std::unique_ptr(new wolkabout::InternalDeviceStatusService( + GATEWAY_KEY, *protocol, *gatewayProtocol, deviceRepository.get(), *platformOutboundMessageHandler, + *deviceOutboundMessageHandler, std::chrono::seconds{60})); } void TearDown() override { remove(DEVICE_REPOSITORY_PATH); } @@ -64,7 +66,7 @@ class DeviceStatusService : public ::testing::Test std::unique_ptr platformOutboundMessageHandler; std::unique_ptr deviceOutboundMessageHandler; std::shared_ptr connectionStatusListener; - std::unique_ptr deviceStatusService; + std::unique_ptr deviceStatusService; static constexpr const char* DEVICE_REPOSITORY_PATH = "testsDeviceRepository.db"; static constexpr const char* GATEWAY_KEY = "GATEWAY_KEY"; diff --git a/tests/ExtarnalDataServiceTests.cpp b/tests/ExtarnalDataServiceTests.cpp new file mode 100644 index 0000000..eefd834 --- /dev/null +++ b/tests/ExtarnalDataServiceTests.cpp @@ -0,0 +1,133 @@ +#include "MockRepository.h" +#include "OutboundMessageHandler.h" +#include "core/model/Message.h" +#include "core/protocol/json/JsonProtocol.h" +#include "protocol/json/JsonGatewayDataProtocol.h" +#include "repository/SQLiteDeviceRepository.h" +#include "service/data/DataService.h" +#include "service/data/ExternalDataService.h" +#include "service/data/InternalDataService.h" + +#include + +#include +#include +#include + +namespace +{ +class PlatformOutboundMessageHandler : public wolkabout::OutboundMessageHandler +{ +public: + void addMessage(std::shared_ptr message) override { m_messages.push_back(message); } + + const std::vector>& getMessages() const { return m_messages; } + +private: + std::vector> m_messages; +}; + +class ExternalDataService : public ::testing::Test +{ +public: + void SetUp() override + { + protocol.reset(new wolkabout::JsonProtocol(true)); + gateawayProtocol = std::unique_ptr(new wolkabout::JsonGatewayDataProtocol()); + + deviceRepository = std::unique_ptr(new MockRepository()); + platformOutboundMessageHandler = + std::unique_ptr(new PlatformOutboundMessageHandler()); + dataService = std::unique_ptr(new wolkabout::ExternalDataService( + GATEWAY_KEY, *protocol, *gateawayProtocol, *platformOutboundMessageHandler, nullptr)); + } + + void TearDown() override { remove(DEVICE_REPOSITORY_PATH); } + + std::unique_ptr protocol; + std::unique_ptr gateawayProtocol; + std::unique_ptr deviceRepository; + std::unique_ptr platformOutboundMessageHandler; + std::unique_ptr dataService; + + static constexpr const char* DEVICE_REPOSITORY_PATH = "testsDeviceRepository.db"; + static constexpr const char* GATEWAY_KEY = "GATEWAY_KEY"; +}; +} // namespace + +TEST_F(ExternalDataService, Given_When_MessageFromPlatformWithInvalidChannelDirectionIsReceived_Then_MessageIsIgnored) +{ + // Given + // Intentionally left empty + + // When + auto message = std::make_shared("", "d2p/actuator_set/g/GATEWAY_KEY/r/REF"); + dataService->platformMessageReceived(message); + + // Then + ASSERT_TRUE(platformOutboundMessageHandler->getMessages().empty()); +} + +TEST_F(ExternalDataService, Given_When_MessageFromPlatformWithMissingDeviceTypeIsReceived_Then_MessageIsIgnored) +{ + // Given + // Intentionally left empty + + // When + auto message = std::make_shared("", "p2d/actuator_set/GATEWAY_KEY/r/REF"); + dataService->platformMessageReceived(message); + + // Then + ASSERT_TRUE(platformOutboundMessageHandler->getMessages().empty()); +} + +TEST_F(ExternalDataService, DISABLED_Given_When_MessageFromPlatformForDeviceIsReceived_Then_MessageIsSentToDeviceModule) +{ + // Given + // Intentionally left empty + + // When + auto message = std::make_shared("", "p2d/actuator_set/g/GATEWAY_KEY/d/DEVICE_KEY/r/REF"); + dataService->platformMessageReceived(message); + + // Then + ASSERT_TRUE(platformOutboundMessageHandler->getMessages().empty()); +} + +TEST_F(ExternalDataService, + Given_When_MessageFromPlatformForDaviceWithInvalidDeviceTypeIsReceived_Then_MessageSentIsIgnored) +{ + // Given + // Intentionally left empty + + // When + auto message = std::make_shared("", "p2d/actuator_set/g/DEVICE_KEY/r/REF"); + dataService->platformMessageReceived(message); + + // Then + ASSERT_TRUE(platformOutboundMessageHandler->getMessages().empty()); +} + +TEST_F(ExternalDataService, Given_When_EmptyReadingsAreReceived_Then_MessageIsIgnored) +{ + // Given + // Intentionally left empty + + // When + auto message = std::make_shared("", "p2d/sensor_reading/g/GATEWAY_KEY/r/REF"); + dataService->addSensorReadings("DEVICE_KEY", {}); + + // Then + ASSERT_TRUE(platformOutboundMessageHandler->getMessages().empty()); +} + +TEST_F(ExternalDataService, Given_When_MessageFromDeviceIsReceived_Then_MessageIsSentToPlatform) +{ + // When + dataService->addSensorReading("GATEWAY_KEY", {"5", "REF", 0, "DEVICE_KEY"}); + + // Then + ASSERT_EQ(platformOutboundMessageHandler->getMessages().size(), 1); + ASSERT_EQ(platformOutboundMessageHandler->getMessages().front()->getChannel(), + "d2p/sensor_reading/g/GATEWAY_KEY/d/DEVICE_KEY/r/REF"); +} diff --git a/tests/FSFileRepositoryTests.cpp b/tests/FSFileRepositoryTests.cpp new file mode 100644 index 0000000..50e6b40 --- /dev/null +++ b/tests/FSFileRepositoryTests.cpp @@ -0,0 +1,231 @@ +#define private public +#define protected public +#include "repository/FSFileRepository.h" +#undef private +#undef protected + +#include "core/utilities/FileSystemUtils.h" +#include "core/utilities/Logger.h" + +#include + +class FSFileRepositoryTests : public ::testing::Test +{ +public: + const std::string FOLDER_PATH = "./test-folder"; + const std::string NON_EXISTING_FOLDER = "./non-existing-folder"; + const std::string FILE_SYSTEM_DIVIDER = "/"; + const std::string NON_EXISTING_FILE = "non-existing-file"; + const std::string UNREADABLE_FILE = "unreadable-file"; + const std::string TEST_FILE_NAME = "test-file"; + const std::string TEST_FILE_CONTENT = "Hello World!"; + const std::string TEST_FILE_HASH = "7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069"; + + std::unique_ptr fileRepository; + + static void SetUpTestCase() + { + // Create a logger + wolkabout::Logger::init(wolkabout::LogLevel::TRACE, wolkabout::Logger::Type::CONSOLE); + } + + void SetUp() override + { + // Create the folder + wolkabout::FileSystemUtils::createDirectory(FOLDER_PATH); + + // Create the file repository + fileRepository = std::unique_ptr(new wolkabout::FSFileRepository(FOLDER_PATH)); + } + + void TearDown() override + { + // Clean up the repository + fileRepository.reset(); + + // Remove all the files in the folder + for (const auto& fileInFolder : wolkabout::FileSystemUtils::listFiles(FOLDER_PATH)) + { + wolkabout::FileSystemUtils::deleteFile(FOLDER_PATH + FILE_SYSTEM_DIVIDER + fileInFolder); + } + + // Remove the folder + wolkabout::FileSystemUtils::deleteFile(FOLDER_PATH); + } +}; + +TEST_F(FSFileRepositoryTests, ConstructorTest) +{ + // Expect that the constructor will throw an error if the folder does not exist + ASSERT_THROW(wolkabout::FSFileRepository(this->NON_EXISTING_FOLDER), std::runtime_error); + + // And that it won't on the existing one + ASSERT_NO_THROW(wolkabout::FSFileRepository(this->FOLDER_PATH)); +} + +TEST_F(FSFileRepositoryTests, GetFileInfo_NonExistingFile) +{ + // In every case, obtaining info about a non existing file needs to return null. + ASSERT_EQ(fileRepository->getFileInfo(NON_EXISTING_FILE), nullptr); +} + +TEST_F(FSFileRepositoryTests, GetFileInfo_DummyFile) +{ + // Create the file + const auto testFilePath = FOLDER_PATH + FILE_SYSTEM_DIVIDER + TEST_FILE_NAME; + ASSERT_TRUE(wolkabout::FileSystemUtils::createFileWithContent(testFilePath, TEST_FILE_CONTENT)); + + // Make place for the FileInfo object returned + auto fileInfo = std::unique_ptr(); + ASSERT_NO_THROW(fileInfo = fileRepository->getFileInfo(TEST_FILE_NAME)); + + // And now analyze the returned value + ASSERT_NE(fileInfo, nullptr); + EXPECT_EQ(fileInfo->name, TEST_FILE_NAME); + EXPECT_EQ(fileInfo->hash, TEST_FILE_HASH); + EXPECT_EQ(fileInfo->path, testFilePath); +} + +TEST_F(FSFileRepositoryTests, GetAllFileNames_EmptyVector) +{ + // Because the folder is empty, we should get back an vector containing no elements. + ASSERT_EQ(fileRepository->getAllFileNames()->size(), 0); +} + +TEST_F(FSFileRepositoryTests, GetAllFileNames_DummyFiles) +{ + // Create one dummy file + ASSERT_TRUE( + wolkabout::FileSystemUtils::createFileWithContent(FOLDER_PATH + FILE_SYSTEM_DIVIDER + "dummy-file-1", "")); + + // Expect that we would get one file + std::unique_ptr> files; + ASSERT_NO_THROW(files = fileRepository->getAllFileNames()); + EXPECT_EQ(files->size(), 1); + EXPECT_NE(std::find(files->cbegin(), files->cend(), "dummy-file-1"), files->cend()); + + // Create another dummy file + ASSERT_TRUE( + wolkabout::FileSystemUtils::createFileWithContent(FOLDER_PATH + FILE_SYSTEM_DIVIDER + "dummy-file-2", "")); + + // Expect that we would get two files now + ASSERT_NO_THROW(files = fileRepository->getAllFileNames()); + EXPECT_EQ(files->size(), 2); + EXPECT_NE(std::find(files->cbegin(), files->cend(), "dummy-file-2"), files->cend()); + + // And that deleting the first dummy file returns us to 1 + ASSERT_TRUE(wolkabout::FileSystemUtils::deleteFile(FOLDER_PATH + FILE_SYSTEM_DIVIDER + "dummy-file-1")); + + // Expect that we would get one file now + ASSERT_NO_THROW(files = fileRepository->getAllFileNames()); + EXPECT_EQ(files->size(), 1); + EXPECT_NE(std::find(files->cbegin(), files->cend(), "dummy-file-2"), files->cend()); +} + +TEST_F(FSFileRepositoryTests, Remove_NonExistingFile) +{ + // There should be no harm done if we try to remove a file that does not exist + ASSERT_EQ(fileRepository->getAllFileNames()->size(), 0); + ASSERT_NO_THROW(fileRepository->remove(NON_EXISTING_FILE)); +} + +TEST_F(FSFileRepositoryTests, Remove_DummyFile) +{ + // We need to create a file + ASSERT_TRUE(wolkabout::FileSystemUtils::createFileWithContent(FOLDER_PATH + FILE_SYSTEM_DIVIDER + TEST_FILE_NAME, + TEST_FILE_CONTENT)); + + // See that we are actually having some files reported back + ASSERT_EQ(fileRepository->getAllFileNames()->size(), 1); + + // And now remove it + ASSERT_NO_THROW(fileRepository->remove(TEST_FILE_NAME)); + + // Check that the repo is reporting empty again + ASSERT_EQ(fileRepository->getAllFileNames()->size(), 0); +} + +TEST_F(FSFileRepositoryTests, RemoveAll_EmptyDirectory) +{ + // There should be no harm done if we try to purge an empty directory + ASSERT_NO_THROW(fileRepository->removeAll()); +} + +TEST_F(FSFileRepositoryTests, RemoveAll_TenDummyFiles) +{ + // Create ten files + for (uint8_t i = 0; i < 10; ++i) + { + ASSERT_TRUE(wolkabout::FileSystemUtils::createFileWithContent( + FOLDER_PATH + FILE_SYSTEM_DIVIDER + TEST_FILE_NAME + std::to_string(i), TEST_FILE_CONTENT)); + } + + // Check that we are actually reporting ten files + ASSERT_EQ(fileRepository->getAllFileNames()->size(), 10); + + // And now purge them all + ASSERT_NO_THROW(fileRepository->removeAll()); + + // And now we should not have any reported + ASSERT_EQ(fileRepository->getAllFileNames()->size(), 0); +} + +TEST_F(FSFileRepositoryTests, ContainsInfoForFile_NonExistingFile) +{ + // This should just say false for a file that doesn't exist + ASSERT_FALSE(fileRepository->containsInfoForFile(NON_EXISTING_FILE)); +} + +TEST_F(FSFileRepositoryTests, ContainerInfoForFile_DummyFile) +{ + // After we create a dummy file, it should report true + ASSERT_TRUE(wolkabout::FileSystemUtils::createFileWithContent(FOLDER_PATH + FILE_SYSTEM_DIVIDER + TEST_FILE_NAME, + TEST_FILE_CONTENT)); + ASSERT_TRUE(fileRepository->containsInfoForFile(TEST_FILE_NAME)); +} + +TEST_F(FSFileRepositoryTests, CalculateFileHash_FileDoesntExist) +{ + // Expect that it will return an empty string and pop out some messages + ASSERT_EQ(wolkabout::FSFileRepository::calculateFileHash(FOLDER_PATH + FILE_SYSTEM_DIVIDER + NON_EXISTING_FILE), + ""); +} + +TEST_F(FSFileRepositoryTests, DISABLED_CalculateFileHash_UnreadableFile) +{ + /** + * Not for this test, it is disabled, as I can't make a file unreadable. + * Making it unreadable with permissions, just makes it not present too in the eyes of + * FileSystemUtils. + */ + + // Make the path for the file + const auto unreadableFilePath = FOLDER_PATH + FILE_SYSTEM_DIVIDER + UNREADABLE_FILE; + // If there is something there, delete it + wolkabout::FileSystemUtils::deleteFile(unreadableFilePath); + + // Create the file that could not be read + ASSERT_TRUE(wolkabout::FileSystemUtils::createFileWithContent(unreadableFilePath, "")); + + // Make it unreadable + ASSERT_FALSE(chmod(unreadableFilePath.c_str(), 0)); + + // Now attempt to calculate its hash + EXPECT_EQ(wolkabout::FSFileRepository::calculateFileHash(unreadableFilePath), ""); + + // And now delete the file + wolkabout::FileSystemUtils::deleteFile(unreadableFilePath); +} + +TEST_F(FSFileRepositoryTests, CalculateFileHash_RegularSmallFile) +{ + // Create the file that has some small amount of content in it. + const auto testFilePath = FOLDER_PATH + FILE_SYSTEM_DIVIDER + TEST_FILE_NAME; + ASSERT_TRUE(wolkabout::FileSystemUtils::createFileWithContent(testFilePath, TEST_FILE_CONTENT)); + + // Compare the hash values + EXPECT_EQ(wolkabout::FSFileRepository::calculateFileHash(testFilePath), TEST_FILE_HASH); + + // And delete the file + wolkabout::FileSystemUtils::deleteFile(testFilePath); +} diff --git a/tests/JsonProtocolTests.cpp b/tests/JsonProtocolTests.cpp index 6c2db0c..f93fb95 100644 --- a/tests/JsonProtocolTests.cpp +++ b/tests/JsonProtocolTests.cpp @@ -1,7 +1,7 @@ -#include "model/ActuatorGetCommand.h" -#include "model/ActuatorSetCommand.h" -#include "model/ActuatorStatus.h" -#include "model/Message.h" +#include "core/model/ActuatorGetCommand.h" +#include "core/model/ActuatorSetCommand.h" +#include "core/model/ActuatorStatus.h" +#include "core/model/Message.h" #define private public #define protected public #include "protocol/json/JsonGatewayDataProtocol.h" diff --git a/tests/KeepAliveServiceTest.cpp b/tests/KeepAliveServiceTest.cpp index 78a2dc6..3ec28ae 100644 --- a/tests/KeepAliveServiceTest.cpp +++ b/tests/KeepAliveServiceTest.cpp @@ -15,7 +15,7 @@ */ #include "OutboundMessageHandler.h" -#include "protocol/json/JsonStatusProtocol.h" +#include "core/protocol/json/JsonStatusProtocol.h" #include "service/KeepAliveService.h" #include diff --git a/tests/MockConnectivityService.h b/tests/MockConnectivityService.h index 478495a..4dd34d5 100644 --- a/tests/MockConnectivityService.h +++ b/tests/MockConnectivityService.h @@ -16,7 +16,7 @@ #ifndef MOCKCONNECTIVITYSERVICE_H #define MOCKCONNECTIVITYSERVICE_H -#include "connectivity/ConnectivityService.h" +#include "core/connectivity/ConnectivityService.h" #include diff --git a/tests/MockRepository.h b/tests/MockRepository.h index 40eb167..ce90b93 100644 --- a/tests/MockRepository.h +++ b/tests/MockRepository.h @@ -1,7 +1,7 @@ #ifndef MOCKREPOSITORY_H #define MOCKREPOSITORY_H -#include "model/DetailedDevice.h" +#include "core/model/DetailedDevice.h" #include "repository/DeviceRepository.h" #include "repository/ExistingDevicesRepository.h" #include "repository/FileRepository.h" diff --git a/tests/StatusProtocolTests.cpp b/tests/StatusProtocolTests.cpp index fb6df9b..b1d546b 100644 --- a/tests/StatusProtocolTests.cpp +++ b/tests/StatusProtocolTests.cpp @@ -1,5 +1,5 @@ -#include "model/DeviceStatus.h" -#include "model/Message.h" +#include "core/model/DeviceStatus.h" +#include "core/model/Message.h" #include "protocol/json/JsonGatewayStatusProtocol.h" #include diff --git a/tests/WolkBuilderTest.cpp b/tests/WolkBuilderTest.cpp index c3d9ce1..ade6621 100644 --- a/tests/WolkBuilderTest.cpp +++ b/tests/WolkBuilderTest.cpp @@ -4,6 +4,7 @@ #include "WolkBuilder.h" #undef protected #undef private +#include "api/DataProvider.h" #include "model/GatewayDevice.h" #include "model/SubdeviceManagement.h" @@ -23,6 +24,12 @@ class WolkBuilder : public ::testing::Test static constexpr const char* GATEWAY_KEY = "gateway_key"; static constexpr const char* GATEWAY_PASSWORD = "gateway_password"; }; + +class ExternalDataProvider : public wolkabout::DataProvider +{ +public: + void setDataHandler(wolkabout::DataHandler* handler, const std::string& gateway) override {} +}; } // namespace TEST_F(WolkBuilder, GivenGatewayManagesSubdevices_When_ConstructingWolkInstance_Then_RegistrationServiceIsSetup) @@ -50,3 +57,19 @@ TEST_F(WolkBuilder, GivenPlatformManagesSubdevices_When_ConstructingWolkInstance // Then ASSERT_EQ(nullptr, wolk->m_subdeviceRegistrationService); } + +TEST_F(WolkBuilder, GivenExternalDataProvider_When_ConstructingWolkInstance_Then_RegistrationServiceIsNotSetup) +{ + // Given + wolkabout::GatewayDevice device(GATEWAY_KEY, GATEWAY_PASSWORD, wolkabout::SubdeviceManagement::PLATFORM); + auto provider = std::make_shared(); + + wolkabout::WolkBuilder builder = wolkabout::Wolk::newBuilder(device); + + // When + builder.withExternalDataProvider(provider.get()); + wolk = builder.build(); + + // Then + ASSERT_EQ(nullptr, wolk->m_subdeviceRegistrationService); +} diff --git a/tests/WolkTest.cpp b/tests/WolkTest.cpp index c5915e4..f2ab8d9 100644 --- a/tests/WolkTest.cpp +++ b/tests/WolkTest.cpp @@ -17,6 +17,7 @@ #define private public #define protected public #include "Wolk.h" +#include "WolkDefault.h" #undef protected #undef private @@ -24,24 +25,25 @@ #include "GatewayInboundPlatformMessageHandler.h" #include "MockConnectivityService.h" #include "MockRepository.h" +#include "core/protocol/json/JsonDFUProtocol.h" +#include "core/protocol/json/JsonDownloadProtocol.h" +#include "core/protocol/json/JsonProtocol.h" +#include "core/protocol/json/JsonRegistrationProtocol.h" +#include "core/protocol/json/JsonStatusProtocol.h" #include "model/SubdeviceManagement.h" -#include "protocol/json/JsonDFUProtocol.h" -#include "protocol/json/JsonDownloadProtocol.h" #include "protocol/json/JsonGatewayDFUProtocol.h" #include "protocol/json/JsonGatewayDataProtocol.h" #include "protocol/json/JsonGatewaySubdeviceRegistrationProtocol.h" -#include "protocol/json/JsonProtocol.h" -#include "protocol/json/JsonRegistrationProtocol.h" -#include "service/DataService.h" #include "service/FileDownloadService.h" #include "service/FirmwareUpdateService.h" #include "service/GatewayUpdateService.h" #include "service/KeepAliveService.h" #include "service/PublishingService.h" #include "service/SubdeviceRegistrationService.h" +#include "service/data/DataService.h" +#include "service/data/InternalDataService.h" #include -#include namespace { @@ -54,15 +56,17 @@ class Publisher : public wolkabout::PublishingService void disconnected() override {} }; -class MockDataService : public wolkabout::DataService +class MockDataService : public wolkabout::InternalDataService { public: - using wolkabout::DataService::DataService; + using wolkabout::InternalDataService::InternalDataService; MOCK_METHOD1(requestActuatorStatusesForDevice, void(const std::string&)); MOCK_METHOD0(requestActuatorStatusesForAllDevices, void()); private: + MOCK_METHOD1(handleMessageForDevice, void(std::shared_ptr)); + GTEST_DISALLOW_COPY_AND_ASSIGN_(MockDataService); }; @@ -128,13 +132,14 @@ class MockSubdeviceRegistrationService : public wolkabout::SubdeviceRegistration class Wolk : public ::testing::Test { public: + using Test::SetUp; void SetUp(wolkabout::SubdeviceManagement control) { platformConnectivityService = new MockConnectivityService(); deviceConnectivityService = new MockConnectivityService(); - wolk = std::unique_ptr( - new wolkabout::Wolk(wolkabout::GatewayDevice{GATEWAY_KEY, "password", control, true, true})); + wolk = std::unique_ptr( + new wolkabout::WolkDefault(wolkabout::GatewayDevice{GATEWAY_KEY, "password", control, true, true})); wolk->m_platformConnectivityService.reset(platformConnectivityService); wolk->m_deviceConnectivityService.reset(deviceConnectivityService); wolk->m_platformPublisher.reset(new Publisher(*platformConnectivityService, nullptr)); @@ -211,7 +216,7 @@ class Wolk : public ::testing::Test std::shared_ptr statusProtocol; std::shared_ptr gatewayRegistrationProtocol; - std::unique_ptr wolk; + std::unique_ptr wolk; std::mutex mutex; std::condition_variable cv;