diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index b88ee5ad48..c3deeb1cd2 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -6,6 +6,7 @@ add_subdirectory(${LIBS_DIR}/BatteryKit) add_subdirectory(${LIBS_DIR}/BehaviorKit) add_subdirectory(${LIBS_DIR}/BLEKit) add_subdirectory(${LIBS_DIR}/ColorKit) +add_subdirectory(${LIBS_DIR}/ConfigKit) add_subdirectory(${LIBS_DIR}/ContainerKit) add_subdirectory(${LIBS_DIR}/FileSystemKit) add_subdirectory(${LIBS_DIR}/FirmwareKit) diff --git a/libs/ConfigKit/CMakeLists.txt b/libs/ConfigKit/CMakeLists.txt new file mode 100644 index 0000000000..1ca31f4148 --- /dev/null +++ b/libs/ConfigKit/CMakeLists.txt @@ -0,0 +1,28 @@ +# Leka - LekaOS +# Copyright 2021 APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +add_library(ConfigKit STATIC) + +target_include_directories(ConfigKit + PUBLIC + include +) + +target_sources(ConfigKit + PRIVATE + source/Config.cpp +) + +target_link_libraries(ConfigKit + mbed-os + FileSystemKit +) + +if (${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") + + leka_unit_tests_sources( + tests/Config_test.cpp + ) + +endif() diff --git a/libs/ConfigKit/include/ConfigKit.h b/libs/ConfigKit/include/ConfigKit.h new file mode 100644 index 0000000000..87a7d4cce0 --- /dev/null +++ b/libs/ConfigKit/include/ConfigKit.h @@ -0,0 +1,40 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include +#include + +#include "FileSystemKit.h" +#include "interface/platform/File.h" + +namespace leka { + +struct Config { + public: + Config(const std::filesystem::path &path) : _path(path) {}; + + auto path() -> std::filesystem::path { return _path; } + + private: + const std::filesystem::path &_path; +}; + +class ConfigKit +{ + public: + explicit ConfigKit() = default; + auto read(Config &_config) -> uint8_t; + auto write(uint8_t data, Config &_config) -> bool; + + static constexpr uint8_t default_content_value = 0xFF; + + private: + FileSystemKit::File _configuration_file {}; +}; + +} // namespace leka diff --git a/libs/ConfigKit/include/ConfigList.h b/libs/ConfigKit/include/ConfigList.h new file mode 100644 index 0000000000..7aec704ead --- /dev/null +++ b/libs/ConfigKit/include/ConfigList.h @@ -0,0 +1,21 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +#include "ConfigKit.h" + +namespace leka::config { + +namespace format { + const std::filesystem::path parent_path = "/fs/conf"; +} // namespace format + +namespace list { + const Config _bootloader_battery_hysteresis(format::parent_path / "bootloader_battery_hysteresis.conf"); +} + +} // namespace leka::config diff --git a/libs/ConfigKit/source/Config.cpp b/libs/ConfigKit/source/Config.cpp new file mode 100644 index 0000000000..fc3258e157 --- /dev/null +++ b/libs/ConfigKit/source/Config.cpp @@ -0,0 +1,38 @@ +// Leka - LekaOS +// Copyright 2021 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include + +#include "ConfigKit.h" +#include "LogKit.h" + +using namespace leka; + +auto ConfigKit::read(Config &_config) -> uint8_t +{ + _configuration_file.open(_config.path().c_str(), "r"); + if (!_configuration_file.is_open()) { + return ConfigKit::default_content_value; + } + auto input = std::array {}; + _configuration_file.read(input); + _configuration_file.close(); + auto data = input.front(); + return data; +} + +auto ConfigKit::write(uint8_t data, Config &_config) -> bool +{ + _configuration_file.open(_config.path().c_str(), "r+"); + if (!_configuration_file.is_open()) { + return false; + } + auto output = std::array {data}; + _configuration_file.write(output); + _configuration_file.close(); + return true; +} diff --git a/libs/ConfigKit/tests/Config_test.cpp b/libs/ConfigKit/tests/Config_test.cpp new file mode 100644 index 0000000000..d21c5fb381 --- /dev/null +++ b/libs/ConfigKit/tests/Config_test.cpp @@ -0,0 +1,100 @@ +// Leka - LekaOS +// Copyright 2021 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +#include + +#include "ConfigKit.h" +#include "FileSystemKit.h" +#include "LogKit.h" +#include "gtest/gtest.h" +#include "mocks/leka/File.h" + +using namespace leka; + +class ConfigTest : public ::testing::Test +{ + protected: + void SetUp() override {} + + auto readFirstValueTempFile() -> uint8_t + { + auto input_data = std::array {}; + FileSystemKit::File _configuration_temp_file {temp_path.c_str()}; + _configuration_temp_file.read(input_data); + _configuration_temp_file.close(); + return input_data.front(); + } + + void writeFirstValueTempFile(uint8_t data) + { + auto output_data = std::array {data}; + FileSystemKit::File _configuration_temp_file {temp_path.c_str(), "r+"}; + _configuration_temp_file.write(output_data); + _configuration_temp_file.close(); + } + + void touchTempFile() + { + FileSystemKit::File _configuration_temp_file {temp_path.c_str(), "w"}; + _configuration_temp_file.close(); + } + void removeTempFile() { std::remove(temp_path.c_str()); } + + const std::filesystem::path temp_path = "/tmp/temp.conf"; + + Config temp {temp_path}; +}; + +TEST_F(ConfigTest, initializationWithPath) +{ + auto _config_manager = ConfigKit(); + ASSERT_NE(&_config_manager, nullptr); +} + +TEST_F(ConfigTest, readNotOpenFile) +{ + auto _config_manager = ConfigKit(); + removeTempFile(); + auto data = _config_manager.read(temp); + ASSERT_EQ(ConfigKit::default_content_value, data); +} + +TEST_F(ConfigTest, readEmptyFile) +{ + auto _config_manager = ConfigKit(); + removeTempFile(); + touchTempFile(); + auto data = _config_manager.read(temp); + ASSERT_EQ(0, data); +} + +TEST_F(ConfigTest, read) +{ + auto _config_manager = ConfigKit(); + writeFirstValueTempFile(5); + auto data = _config_manager.read(temp); + ASSERT_EQ(5, data); +} + +TEST_F(ConfigTest, writeNotOpenFile) +{ + auto _config_manager = ConfigKit(); + removeTempFile(); + auto write = _config_manager.write(5, temp); + ASSERT_FALSE(write); +} + +TEST_F(ConfigTest, write) +{ + auto _config_manager = ConfigKit(); + touchTempFile(); + auto write = _config_manager.write(5, temp); + ASSERT_TRUE(write); + auto data = readFirstValueTempFile(); + ASSERT_EQ(5, data); +} diff --git a/libs/FileSystemKit/include/FileSystemKit.h b/libs/FileSystemKit/include/FileSystemKit.h index 9235e6c4ac..5a31089e60 100644 --- a/libs/FileSystemKit/include/FileSystemKit.h +++ b/libs/FileSystemKit/include/FileSystemKit.h @@ -11,50 +11,46 @@ #include "interface/platform/File.h" -namespace leka { +namespace leka::FileSystemKit { -class FileSystemKit -{ - public: - struct File : public interface::File, public mbed::NonCopyable { - File(const char *path = nullptr, const char *mode = "r"); +struct File : public interface::File, public mbed::NonCopyable { + File(const char *path = nullptr, const char *mode = "r"); - auto open(const char *path, const char *mode = "r") -> bool final; - void close() final; + auto open(const char *path, const char *mode = "r") -> bool final; + void close() final; - auto read(std::span buffer) -> size_t final; - auto write(std::span data) -> size_t final; + auto read(std::span buffer) -> size_t final; + auto write(std::span data) -> size_t final; - auto read(std::span buffer) -> size_t final; - auto write(std::span data) -> size_t final; + auto read(std::span buffer) -> size_t final; + auto write(std::span data) -> size_t final; - auto read(uint8_t *buffer, uint32_t size) -> size_t final; - auto write(const uint8_t *data, uint32_t size) -> size_t final; + auto read(uint8_t *buffer, uint32_t size) -> size_t final; + auto write(const uint8_t *data, uint32_t size) -> size_t final; - auto read(char *buffer, uint32_t size) -> size_t final; - auto write(const char *data, uint32_t size) -> size_t final; + auto read(char *buffer, uint32_t size) -> size_t final; + auto write(const char *data, uint32_t size) -> size_t final; - void seek(size_t pos, int origin = SEEK_SET) final; - void rewind() final; - auto size() -> size_t final; + void seek(size_t pos, int origin = SEEK_SET) final; + void rewind() final; + auto size() -> size_t final; - auto tell() -> size_t final; + auto tell() -> size_t final; - auto reopen(const char *path, const char *mode = "r") -> bool final; + auto reopen(const char *path, const char *mode = "r") -> bool final; - auto setBuffer(std::span buffer, int mode = _IOFBF) -> bool final; - auto setBuffer(char *buffer, uint32_t size = BUFSIZ, int mode = _IOFBF) -> bool final; - auto unsetBuffer() -> bool final; - auto flush() -> bool final; + auto setBuffer(std::span buffer, int mode = _IOFBF) -> bool final; + auto setBuffer(char *buffer, uint32_t size = BUFSIZ, int mode = _IOFBF) -> bool final; + auto unsetBuffer() -> bool final; + auto flush() -> bool final; - auto error() -> bool final; - void clearerr() final; + auto error() -> bool final; + void clearerr() final; - [[nodiscard]] auto is_open() const -> bool final; + [[nodiscard]] auto is_open() const -> bool final; - private: - std::unique_ptr _file {nullptr, fclose}; - }; + private: + std::unique_ptr _file {nullptr, fclose}; }; -} // namespace leka +} // namespace leka::FileSystemKit diff --git a/spikes/CMakeLists.txt b/spikes/CMakeLists.txt index 725a8a8d7d..194373a142 100644 --- a/spikes/CMakeLists.txt +++ b/spikes/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory(${SPIKES_DIR}/lk_ble) add_subdirectory(${SPIKES_DIR}/lk_bluetooth) add_subdirectory(${SPIKES_DIR}/lk_cg_animations) add_subdirectory(${SPIKES_DIR}/lk_color_kit) +add_subdirectory(${SPIKES_DIR}/lk_config_kit) add_subdirectory(${SPIKES_DIR}/lk_coreled) add_subdirectory(${SPIKES_DIR}/lk_event_queue) add_subdirectory(${SPIKES_DIR}/lk_file_reception) diff --git a/spikes/lk_config_kit/CMakeLists.txt b/spikes/lk_config_kit/CMakeLists.txt new file mode 100644 index 0000000000..cf8092e34c --- /dev/null +++ b/spikes/lk_config_kit/CMakeLists.txt @@ -0,0 +1,22 @@ +# Leka - LekaOS +# Copyright 2022 APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +add_mbed_executable(spike_lk_config_kit) + +target_include_directories(spike_lk_config_kit + PRIVATE + . +) + +target_sources(spike_lk_config_kit + PRIVATE + main.cpp +) + +target_link_libraries(spike_lk_config_kit + FileSystemKit + ConfigKit +) + +target_link_custom_leka_targets(spike_lk_config_kit) diff --git a/spikes/lk_config_kit/main.cpp b/spikes/lk_config_kit/main.cpp new file mode 100644 index 0000000000..f3fd7f5987 --- /dev/null +++ b/spikes/lk_config_kit/main.cpp @@ -0,0 +1,72 @@ +// Leka - LekaOS +// Copyright 2020 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include "drivers/BufferedSerial.h" +#include "rtos/ThisThread.h" + +#include "ConfigKit.h" +#include "FATFileSystem.h" +#include "FileSystemKit.h" +#include "HelloWorld.h" +#include "LogKit.h" +#include "SDBlockDevice.h" + +using namespace leka; +using namespace std::chrono_literals; + +SDBlockDevice sd_blockdevice(SD_SPI_MOSI, SD_SPI_MISO, SD_SPI_SCK); +FATFileSystem fatfs("fs"); + +auto _configuration_file = FileSystemKit::File {}; +const std::filesystem::path bootloader_battery_hysteresis_path = "/fs/conf/bootloader_battery_hysteresis.conf"; +auto bootloader_battery_level_hysteresis = Config(bootloader_battery_hysteresis_path); +auto _config_manager = ConfigKit(); + +void initializeSD() +{ + constexpr auto default_sd_blockdevice_frequency = uint64_t {25'000'000}; + + sd_blockdevice.init(); + sd_blockdevice.frequency(default_sd_blockdevice_frequency); + + fatfs.mount(&sd_blockdevice); +} + +auto main() -> int +{ + logger::init(); + + log_info("Hello, World!\n\n"); + + auto start = rtos::Kernel::Clock::now(); + + auto hello = HelloWorld(); + + rtos::ThisThread::sleep_for(1s); + + hello.start(); + + initializeSD(); + + auto i = 1; + + rtos::ThisThread::sleep_for(1s); + + while (true) { + auto t = rtos::Kernel::Clock::now() - start; + log_info("A message from your board %s --> \"%s\" at %i s", MBED_CONF_APP_TARGET_NAME, hello.world, + int(t.count() / 1000)); + + if (auto write = _config_manager.write(i++, bootloader_battery_level_hysteresis); !write) { + log_error("Fail to write in hysteresis config file"); + return EXIT_FAILURE; + } + auto battery_level_hysteresis_data = _config_manager.read(bootloader_battery_level_hysteresis); + log_info("Battery level hysteresis : %d", battery_level_hysteresis_data); + rtos::ThisThread::sleep_for(10s); + } +} diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index cac0324229..0200cf79a7 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -241,6 +241,7 @@ leka_register_unit_tests_for_library(BatteryKit) leka_register_unit_tests_for_library(BehaviorKit) leka_register_unit_tests_for_library(BLEKit) leka_register_unit_tests_for_library(ColorKit) +leka_register_unit_tests_for_library(ConfigKit) leka_register_unit_tests_for_library(ContainerKit) leka_register_unit_tests_for_library(FileSystemKit) leka_register_unit_tests_for_library(FirmwareKit)