From 869e6e86791816021d00214e2e0b47844a6e6954 Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Wed, 6 Jul 2022 11:13:40 +0200 Subject: [PATCH 001/130] :hammer: (firmware): Move firmware.hex into _release folder --- tools/firmware/build_firmware.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/firmware/build_firmware.sh b/tools/firmware/build_firmware.sh index 58948379f1..96e0beddec 100755 --- a/tools/firmware/build_firmware.sh +++ b/tools/firmware/build_firmware.sh @@ -18,7 +18,7 @@ APPLICATION_HEX_SOURCE="_build/LEKA_V1_2_DEV/app/os/LekaOS.hex" APPLICATION_VERSION="1.1.0+0" APPLICATION_SIGNED_HEX="_tmp/application-signed.hex" -FIRMWARE_HEX="_tmp/firmware.hex" +FIRMWARE_HEX="_release/firmware.hex" FIRMWARE_RELEASE="_release/firmware.bin" # Get bootloader From 7bfc65f267445a7b2f5ff1fd56b76fa1bdb68ccd Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Wed, 6 Jul 2022 11:15:32 +0200 Subject: [PATCH 002/130] :construction_worker: (firmware): Update uploaded artifacts firmware.hex for factory and application-signed.bin for SD --- .github/workflows/ci-create_release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-create_release.yml b/.github/workflows/ci-create_release.yml index b26ec49538..36e94ce4cd 100644 --- a/.github/workflows/ci-create_release.yml +++ b/.github/workflows/ci-create_release.yml @@ -53,4 +53,5 @@ jobs: name: bootloader_os_firmware retention-days: 7 path: | - _release/firmware.bin + _release/firmware.hex + _release/application-signed.bin From a5bd949d05ba55573c5e1188387e7460c2d36b69 Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Mon, 27 Jun 2022 14:17:19 +0200 Subject: [PATCH 003/130] :sparkles: (bluetooth): Enable audio to play from bluetooth --- spikes/lk_bluetooth/main.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/spikes/lk_bluetooth/main.cpp b/spikes/lk_bluetooth/main.cpp index b7541fa272..85c5fe8d33 100644 --- a/spikes/lk_bluetooth/main.cpp +++ b/spikes/lk_bluetooth/main.cpp @@ -2,9 +2,8 @@ // Copyright 2020 APF France handicap // SPDX-License-Identifier: Apache-2.0 -#include "rtos/Kernel.h" +#include "drivers/DigitalOut.h" #include "rtos/ThisThread.h" -#include "rtos/Thread.h" #include "HelloWorld.h" #include "LekaBluetooth.h" @@ -13,27 +12,24 @@ using namespace leka; using namespace std::chrono; +auto audio_enable = mbed::DigitalOut {SOUND_ENABLE, 1}; + auto main() -> int { logger::init(); - auto start = rtos::Kernel::Clock::now(); - log_info("Hello, World!\n\n"); Bluetooth bluetooth; - rtos::Thread bluetooth_thread; - bluetooth_thread.start({&bluetooth, &Bluetooth::start}); rtos::ThisThread::sleep_for(2s); HelloWorld hello; hello.start(); + bluetooth.start(); + while (true) { - auto t = rtos::Kernel::Clock::now() - start; - log_info("A message from your board %s --> \"%s\" at %i s\n", MBED_CONF_APP_TARGET_NAME, hello.world, - int(t.count() / 1000)); - rtos::ThisThread::sleep_for(1s); + rtos::ThisThread::sleep_for(10s); } } From 49a6c442ce3ec925521b9efbcdbe32fb6d0c198a Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Tue, 5 Jul 2022 16:38:57 +0200 Subject: [PATCH 004/130] :children_crossing: (bluetooth): Repairing if bluetooth not used Use a counter reset on message received --- .../LekaBluetooth/include/LekaBluetooth.h | 8 ++--- .../LekaBluetooth/source/LekaBluetooth.cpp | 30 ++++++++++++++----- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/libs/InvestigationDay/LekaBluetooth/include/LekaBluetooth.h b/libs/InvestigationDay/LekaBluetooth/include/LekaBluetooth.h index ae2938aba0..0a9c0bdccd 100644 --- a/libs/InvestigationDay/LekaBluetooth/include/LekaBluetooth.h +++ b/libs/InvestigationDay/LekaBluetooth/include/LekaBluetooth.h @@ -26,12 +26,12 @@ class Bluetooth void start(void); void pairing(); void playPause(); - void checkResponse(bool printResponse = false); + auto checkResponse(bool printResponse = false) -> bool; - bool isPaired(); + auto isPaired() -> bool; void sendMessage(char *msg, size_t msg_length); - size_t getMessage(char *buffer); - bool checkNewMessage(); + auto getMessage(char *buffer) -> size_t; + auto checkNewMessage() -> bool; private: mbed::BufferedSerial _interface; diff --git a/libs/InvestigationDay/LekaBluetooth/source/LekaBluetooth.cpp b/libs/InvestigationDay/LekaBluetooth/source/LekaBluetooth.cpp index 2dee9e9058..61e14b843e 100644 --- a/libs/InvestigationDay/LekaBluetooth/source/LekaBluetooth.cpp +++ b/libs/InvestigationDay/LekaBluetooth/source/LekaBluetooth.cpp @@ -43,12 +43,14 @@ void Bluetooth::pairing() return; } -void Bluetooth::checkResponse(bool printResponse) +auto Bluetooth::checkResponse(bool printResponse) -> bool { uint8_t attempts = 0; uint8_t max_attempts = 100; _buffer_length = 0; + bool still_alive = false; + while (attempts < max_attempts) { rtos::ThisThread::sleep_for(10ms); while (_interface.readable()) { @@ -59,8 +61,9 @@ void Bluetooth::checkResponse(bool printResponse) rtos::ThisThread::sleep_for(10ms); // Delay to catch length if (_buffer_length + 1 + 2 > max_buffer_size) { - return; + return still_alive; } + still_alive = true; _interface.read(_buffer + 1 + 2, _buffer_length); _buffer_length += 1 + 2; // 0xAA + 2 bytes for length @@ -90,7 +93,7 @@ void Bluetooth::checkResponse(bool printResponse) attempts++; } - return; + return still_alive; } void Bluetooth::playPause() @@ -106,7 +109,7 @@ void Bluetooth::playPause() return; } -bool Bluetooth::isPaired() +auto Bluetooth::isPaired() -> bool { return _paired; } @@ -134,7 +137,7 @@ void Bluetooth::sendMessage(char *msg, size_t msg_length) _interface.write(_buffer, _buffer_length); } -size_t Bluetooth::getMessage(char *buffer) +auto Bluetooth::getMessage(char *buffer) -> size_t { for (uint16_t i = 0; i < _msg_length; i++) { buffer[i] = _msg_rcv[i]; @@ -144,7 +147,7 @@ size_t Bluetooth::getMessage(char *buffer) return _msg_length; } -bool Bluetooth::checkNewMessage() +auto Bluetooth::checkNewMessage() -> bool { return _new_message; } @@ -154,6 +157,9 @@ void Bluetooth::start() char text[max_buffer_size] = {0}; uint16_t text_length; + auto counter = 0; + constexpr auto counter_limit = 5; + printf("Bluetooth example\n\n"); // Enable sound output @@ -172,13 +178,21 @@ void Bluetooth::start() sendMessage(text, text_length); while (true) { - checkResponse(true); + printf("Counter: %d/%d\n", counter, counter_limit); + if (checkResponse(true)) { + counter = 0; + } if (checkNewMessage()) { text_length = getMessage(text); rtos::ThisThread::sleep_for(2s); // Delay of echo sendMessage(text, text_length); // ECHO + + } else if (counter > counter_limit) { + printf("Bluetooth is trying to pair...\n"); + pairing(); } - rtos::ThisThread::sleep_for(800ms); + counter++; + rtos::ThisThread::sleep_for(1s); } printf("End of Bluetooth example\n\n"); From fb159209c8b14461de63d9c1411a1f8a98293c11 Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Tue, 12 Jul 2022 14:43:05 +0200 Subject: [PATCH 005/130] :truck: (bluetooth): Move LekaBluetooth from InvestigationDay lib to spike --- libs/CMakeLists.txt | 1 - .../LekaBluetooth/CMakeLists.txt | 19 ------------------- .../lk_bluetooth}/BM64Converter.cpp | 0 .../lk_bluetooth}/BM64Converter.h | 0 spikes/lk_bluetooth/CMakeLists.txt | 6 ++---- .../lk_bluetooth}/LekaBluetooth.cpp | 0 .../lk_bluetooth}/LekaBluetooth.h | 0 7 files changed, 2 insertions(+), 24 deletions(-) delete mode 100644 libs/InvestigationDay/LekaBluetooth/CMakeLists.txt rename {libs/InvestigationDay/LekaBluetooth/source => spikes/lk_bluetooth}/BM64Converter.cpp (100%) rename {libs/InvestigationDay/LekaBluetooth/include/internal => spikes/lk_bluetooth}/BM64Converter.h (100%) rename {libs/InvestigationDay/LekaBluetooth/source => spikes/lk_bluetooth}/LekaBluetooth.cpp (100%) rename {libs/InvestigationDay/LekaBluetooth/include => spikes/lk_bluetooth}/LekaBluetooth.h (100%) diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index cfb2e3c623..58b3e88ff9 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -24,7 +24,6 @@ add_subdirectory(${LIBS_DIR}/WebKit) add_subdirectory(${LIBS_DIR}/PrettyPrinter) add_subdirectory(${LIBS_DIR}/InvestigationDay/BLE) -add_subdirectory(${LIBS_DIR}/InvestigationDay/LekaBluetooth) add_subdirectory(${LIBS_DIR}/InvestigationDay/LekaRFID) add_subdirectory(${LIBS_DIR}/InvestigationDay/LekaScreen) add_subdirectory(${LIBS_DIR}/InvestigationDay/LekaTouch) diff --git a/libs/InvestigationDay/LekaBluetooth/CMakeLists.txt b/libs/InvestigationDay/LekaBluetooth/CMakeLists.txt deleted file mode 100644 index 564c96c3e4..0000000000 --- a/libs/InvestigationDay/LekaBluetooth/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -# Leka - LekaOS -# Copyright 2020 APF France handicap -# SPDX-License-Identifier: Apache-2.0 - -add_library(lib_LekaBluetooth STATIC) - -target_include_directories(lib_LekaBluetooth - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_CURRENT_SOURCE_DIR}/include/internal -) - -target_sources(lib_LekaBluetooth - PRIVATE - source/BM64Converter.cpp - source/LekaBluetooth.cpp -) - -target_link_libraries(lib_LekaBluetooth mbed-os) diff --git a/libs/InvestigationDay/LekaBluetooth/source/BM64Converter.cpp b/spikes/lk_bluetooth/BM64Converter.cpp similarity index 100% rename from libs/InvestigationDay/LekaBluetooth/source/BM64Converter.cpp rename to spikes/lk_bluetooth/BM64Converter.cpp diff --git a/libs/InvestigationDay/LekaBluetooth/include/internal/BM64Converter.h b/spikes/lk_bluetooth/BM64Converter.h similarity index 100% rename from libs/InvestigationDay/LekaBluetooth/include/internal/BM64Converter.h rename to spikes/lk_bluetooth/BM64Converter.h diff --git a/spikes/lk_bluetooth/CMakeLists.txt b/spikes/lk_bluetooth/CMakeLists.txt index 932d2dd5b0..a2354d6764 100644 --- a/spikes/lk_bluetooth/CMakeLists.txt +++ b/spikes/lk_bluetooth/CMakeLists.txt @@ -11,11 +11,9 @@ target_include_directories(spike_lk_bluetooth target_sources(spike_lk_bluetooth PRIVATE + BM64Converter.cpp + LekaBluetooth.cpp main.cpp ) -target_link_libraries(spike_lk_bluetooth - lib_LekaBluetooth -) - target_link_custom_leka_targets(spike_lk_bluetooth) diff --git a/libs/InvestigationDay/LekaBluetooth/source/LekaBluetooth.cpp b/spikes/lk_bluetooth/LekaBluetooth.cpp similarity index 100% rename from libs/InvestigationDay/LekaBluetooth/source/LekaBluetooth.cpp rename to spikes/lk_bluetooth/LekaBluetooth.cpp diff --git a/libs/InvestigationDay/LekaBluetooth/include/LekaBluetooth.h b/spikes/lk_bluetooth/LekaBluetooth.h similarity index 100% rename from libs/InvestigationDay/LekaBluetooth/include/LekaBluetooth.h rename to spikes/lk_bluetooth/LekaBluetooth.h From adec66782fef4a8c23f1098031d2aae0d5713bcb Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Tue, 12 Jul 2022 15:54:30 +0200 Subject: [PATCH 006/130] :recycle: (bluetooth): clang-tidy warnings --- spikes/lk_bluetooth/BM64Converter.cpp | 8 +- spikes/lk_bluetooth/BM64Converter.h | 27 +++-- spikes/lk_bluetooth/LekaBluetooth.cpp | 136 +++++++++++++------------- spikes/lk_bluetooth/LekaBluetooth.h | 24 ++--- 4 files changed, 108 insertions(+), 87 deletions(-) diff --git a/spikes/lk_bluetooth/BM64Converter.cpp b/spikes/lk_bluetooth/BM64Converter.cpp index c6377ad511..96b022da6d 100644 --- a/spikes/lk_bluetooth/BM64Converter.cpp +++ b/spikes/lk_bluetooth/BM64Converter.cpp @@ -4,17 +4,17 @@ #include "BM64Converter.h" -size_t BM64::getCommand(const uint8_t *cmd, const size_t cmd_length, uint8_t *buffer) +auto BM64::getCommand(std::span cmd, size_t cmd_length, std::span buffer) -> size_t { size_t frame_length = 3 + cmd_length + 1; uint8_t checksum = 0x00; /* HEAD - START */ - buffer[0] = 0xAA; // Not included in checksum + buffer[0] = BM64::Command::SINC_WORD; // Not included in checksum /* HEAD - LENGTH (2 BYTES) */ - buffer[1] = (uint8_t)((uint16_t)cmd_length >> 8); - buffer[2] = (uint8_t)cmd_length; + buffer[1] = static_cast((static_cast(cmd_length >> 8))); + buffer[2] = static_cast(cmd_length); checksum -= (buffer[1] + buffer[2]); /* MID - OP Code & DATA - PARAMETER */ diff --git a/spikes/lk_bluetooth/BM64Converter.h b/spikes/lk_bluetooth/BM64Converter.h index 980d5489e5..440beadcf6 100644 --- a/spikes/lk_bluetooth/BM64Converter.h +++ b/spikes/lk_bluetooth/BM64Converter.h @@ -4,33 +4,48 @@ #pragma once +#include #include #include +#include namespace BM64 { namespace Command { - const uint8_t pairing[3] = {0x02, 0x00, 0x50}; + inline constexpr uint8_t SINC_WORD = 0xAA; + + inline constexpr uint8_t SEND_DATA = 0x12; + inline constexpr uint8_t DATA_INCOMING = 0x22; + + inline constexpr uint8_t BTM_STATUS = 0x01; + inline constexpr uint8_t A2DP_LINK_ESTABLISHED = 0x06; + +}; // namespace Command + +namespace ComplexCommand { + + inline static auto pairing = std::to_array({0x02, 0x00, 0x50}); inline constexpr size_t pairing_length = 3; - const uint8_t play_pause[3] = {0x02, 0x00, 0x32}; + inline static auto play_pause = std::to_array({0x02, 0x00, 0x32}); inline constexpr size_t play_pause_length = 3; - const uint8_t eq_soft_mode[3] = {0x1C, 0x01, 0x00}; + inline static auto eq_soft_mode = std::to_array({0x1C, 0x01, 0x00}); inline constexpr size_t eq_soft_mode_length = 3; -}; // namespace Command +} // namespace ComplexCommand + // using command_t = Command; namespace Response { - const uint8_t acknowledge[2] = {0xFF, 0xFF}; // Not done yet + inline static auto acknowledge = std::to_array({0xFF, 0xFF}); // Not done yet inline constexpr size_t acknowledge_length = 2; }; // namespace Response // using response_t = Response; -size_t getCommand(const uint8_t *cmd, const size_t cmd_length, uint8_t *buffer); +auto getCommand(std::span cmd, size_t cmd_length, std::span buffer) -> size_t; // void convertResponse(); // Events in datasheet }; // namespace BM64 diff --git a/spikes/lk_bluetooth/LekaBluetooth.cpp b/spikes/lk_bluetooth/LekaBluetooth.cpp index 61e14b843e..3c77ca73bb 100644 --- a/spikes/lk_bluetooth/LekaBluetooth.cpp +++ b/spikes/lk_bluetooth/LekaBluetooth.cpp @@ -16,66 +16,68 @@ Bluetooth::Bluetooth() _bluetooth_reset = 1; rtos::ThisThread::sleep_for(450ms); // Delay recommanded in datasheet - _buffer_length = BM64::getCommand(BM64::Command::eq_soft_mode, BM64::Command::eq_soft_mode_length, _buffer); - _interface.write(_buffer, _buffer_length); + _buffer_length = + BM64::getCommand(BM64::ComplexCommand::eq_soft_mode, BM64::ComplexCommand::eq_soft_mode_length, _buffer); + _interface.write(_buffer.data(), _buffer_length); rtos::ThisThread::sleep_for(100ms); } void Bluetooth::pairing() { - uint8_t max_attempts = 10; - uint8_t attempts = 0; - _buffer_length = 0; + auto max_attempts = 10; + auto attempts = 0; + _buffer_length = 0; - _buffer_length = BM64::getCommand(BM64::Command::pairing, BM64::Command::pairing_length, _buffer); + _buffer_length = BM64::getCommand(BM64::ComplexCommand::pairing, BM64::ComplexCommand::pairing_length, _buffer); while (attempts < max_attempts) { - _interface.write(_buffer, _buffer_length); + _interface.write(_buffer.data(), _buffer_length); checkResponse(); if (_paired) { break; - } else { - rtos::ThisThread::sleep_for(10s); } + + rtos::ThisThread::sleep_for(10s); + attempts++; } - - return; } auto Bluetooth::checkResponse(bool printResponse) -> bool { - uint8_t attempts = 0; - uint8_t max_attempts = 100; - _buffer_length = 0; + auto attempts = 0; + auto max_attempts = 100; + _buffer_length = 0; - bool still_alive = false; + auto still_alive = false; while (attempts < max_attempts) { rtos::ThisThread::sleep_for(10ms); while (_interface.readable()) { - _interface.read(_buffer, 1); - if (_buffer[0] == 0xAA) { - _interface.read(_buffer + 1, 2); - _buffer_length = ((uint16_t)_buffer[1] << 8) + (uint16_t)_buffer[2] + 1; // +1 for checksum + _interface.read(_buffer.data(), 1); + if (_buffer.at(0) == BM64::Command::SINC_WORD) { + _interface.read(_buffer.data() + 1, 2); + _buffer_length = (static_cast(_buffer.at(1) << 8)) + static_cast(_buffer.at(2)) + + 1; // +1 for checksum rtos::ThisThread::sleep_for(10ms); // Delay to catch length - if (_buffer_length + 1 + 2 > max_buffer_size) { + if (_buffer_length + 1 + 2 > MAX_BUFFER_SIZE) { return still_alive; } still_alive = true; - _interface.read(_buffer + 1 + 2, _buffer_length); - _buffer_length += 1 + 2; // 0xAA + 2 bytes for length + _interface.read(_buffer.data() + 1 + 2, _buffer_length); + _buffer_length += 1 + 2; // SINC_WORD + 2 bytes for length - if (_buffer[3] == 0x01 && _buffer[4] == 0x06) { + if (_buffer.at(3) == BM64::Command::BTM_STATUS && + _buffer.at(4) == BM64::Command::A2DP_LINK_ESTABLISHED) { _paired = true; - } else if (_buffer[3] == 0x22) { // Value for message - _msg_length = _buffer_length > max_buffer_size - ? max_buffer_size - 11 - : _buffer_length - 11; // 11 characters wrap the message - for (uint16_t i = 0; i < _msg_length; i++) { - _msg_rcv[i] = _buffer[i + 10]; + } else if (_buffer.at(3) == BM64::Command::DATA_INCOMING) { // Value for message + const auto n_characters_wrapper = 11; + _msg_length = _buffer_length > MAX_BUFFER_SIZE ? MAX_BUFFER_SIZE - n_characters_wrapper + : _buffer_length - n_characters_wrapper; + for (auto i = 0; i < _msg_length; i++) { + _received_message.at(i) = _buffer.at(i + 10); } _new_message = true; } @@ -83,7 +85,7 @@ auto Bluetooth::checkResponse(bool printResponse) -> bool if (printResponse) { printf("Frame received = "); for (int i = 0; i < _buffer_length; i++) { - printf("%X ", _buffer[i]); + printf("%X ", _buffer.at(i)); } printf("\n"); } @@ -102,98 +104,100 @@ void Bluetooth::playPause() _buffer_length = 0; - _buffer_length = BM64::getCommand(BM64::Command::play_pause, BM64::Command::play_pause_length, _buffer); - - _interface.write(_buffer, _buffer_length); + _buffer_length = + BM64::getCommand(BM64::ComplexCommand::play_pause, BM64::ComplexCommand::play_pause_length, _buffer); - return; + _interface.write(_buffer.data(), _buffer_length); } -auto Bluetooth::isPaired() -> bool +auto Bluetooth::isPaired() const -> bool { return _paired; } -void Bluetooth::sendMessage(char *msg, size_t msg_length) +void Bluetooth::sendMessage(const char *msg, size_t msg_length) { - _buffer_length = 0; - const uint16_t msg_cmd_length = 1 + 1 + 1 + 2 + 2 + msg_length; - uint8_t msg_cmd[msg_cmd_length] = { - 0x12, + _buffer_length = 0; + const auto command_header = 1 + 1 + 1 + 2 + 2; + const uint16_t msg_cmd_length = command_header + msg_length; + std::array msg_cmd { + BM64::Command::SEND_DATA, 0x00, 0x00, - (uint8_t)((uint16_t)msg_length >> 8), - (uint8_t)msg_length, - (uint8_t)((uint16_t)msg_length >> 8), - (uint8_t)msg_length}; // Header of the command, check BM64 docs for more details + static_cast((static_cast(msg_length >> 8))), + static_cast(msg_length), + static_cast((static_cast(msg_length >> 8))), + static_cast(msg_length)}; // Header of the command, check BM64 docs for more details // Copy message for (size_t i = 0; i < msg_length; i++) { - msg_cmd[i + 7] = msg[i]; + msg_cmd.at(i + command_header) = msg[i]; } _buffer_length = BM64::getCommand(msg_cmd, msg_cmd_length, _buffer); - _interface.write(_buffer, _buffer_length); + _interface.write(_buffer.data(), _buffer_length); } -auto Bluetooth::getMessage(char *buffer) -> size_t +auto Bluetooth::getMessage(std::span buffer) -> int { - for (uint16_t i = 0; i < _msg_length; i++) { - buffer[i] = _msg_rcv[i]; - _msg_rcv[i] = 0; + for (auto i = 0; i < _msg_length; i++) { + buffer[i] = _received_message.at(i); + _received_message.at(i) = 0; } _new_message = false; return _msg_length; } -auto Bluetooth::checkNewMessage() -> bool +auto Bluetooth::checkNewMessage() const -> bool { return _new_message; } void Bluetooth::start() { - char text[max_buffer_size] = {0}; - uint16_t text_length; + std::array text {}; + auto text_length = 0; auto counter = 0; constexpr auto counter_limit = 5; - printf("Bluetooth example\n\n"); + log_info("Bluetooth example\n"); // Enable sound output - _buffer_length = BM64::getCommand(BM64::Command::eq_soft_mode, BM64::Command::eq_soft_mode_length, _buffer); - _interface.write(_buffer, _buffer_length); + _buffer_length = + BM64::getCommand(BM64::ComplexCommand::eq_soft_mode, BM64::ComplexCommand::eq_soft_mode_length, _buffer); + _interface.write(_buffer.data(), _buffer_length); rtos::ThisThread::sleep_for(100ms); while (!isPaired()) { - printf("Bluetooth is trying to pair...\n"); + log_info("Bluetooth is trying to pair..."); pairing(); rtos::ThisThread::sleep_for(1s); } - text_length = sprintf(text, "This spike do an echo via bluetooth communication.\n"); - sendMessage(text, text_length); - text_length = sprintf(text, "You also can connect with your phone and play songs via the robot!\nEnjoy!\n\n"); - sendMessage(text, text_length); + text_length = sprintf(text.data(), "This spike do an echo via bluetooth communication.\n"); + sendMessage(text.data(), text_length); + text_length = + sprintf(text.data(), "You also can connect with your phone and play songs via the robot!\nEnjoy!\n\n"); + sendMessage(text.data(), text_length); while (true) { - printf("Counter: %d/%d\n", counter, counter_limit); + log_info("Counter: %d/%d", counter, counter_limit); if (checkResponse(true)) { counter = 0; } if (checkNewMessage()) { text_length = getMessage(text); - rtos::ThisThread::sleep_for(2s); // Delay of echo - sendMessage(text, text_length); // ECHO + rtos::ThisThread::sleep_for(2s); // Delay of echo + sendMessage(text.data(), text_length); // ECHO } else if (counter > counter_limit) { - printf("Bluetooth is trying to pair...\n"); + log_info("Bluetooth is trying to pair..."); pairing(); } counter++; rtos::ThisThread::sleep_for(1s); } - printf("End of Bluetooth example\n\n"); + log_info("End of Bluetooth example\n"); } diff --git a/spikes/lk_bluetooth/LekaBluetooth.h b/spikes/lk_bluetooth/LekaBluetooth.h index 0a9c0bdccd..0adb7fdcba 100644 --- a/spikes/lk_bluetooth/LekaBluetooth.h +++ b/spikes/lk_bluetooth/LekaBluetooth.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "PinNames.h" @@ -14,34 +15,35 @@ #include "rtos/Thread.h" #include "BM64Converter.h" +#include "LogKit.h" -inline constexpr uint8_t max_buffer_size = 128; +inline constexpr uint8_t MAX_BUFFER_SIZE = 128; class Bluetooth { public: Bluetooth(); - ~Bluetooth() {}; + ~Bluetooth() = default; - void start(void); + void start(); void pairing(); void playPause(); auto checkResponse(bool printResponse = false) -> bool; - auto isPaired() -> bool; - void sendMessage(char *msg, size_t msg_length); - auto getMessage(char *buffer) -> size_t; - auto checkNewMessage() -> bool; + [[nodiscard]] auto isPaired() const -> bool; + void sendMessage(const char *msg, size_t msg_length); + auto getMessage(std::span buffer) -> int; + [[nodiscard]] auto checkNewMessage() const -> bool; private: mbed::BufferedSerial _interface; mbed::DigitalOut _bluetooth_reset; mbed::DigitalOut _bluetooth_wake_up; - uint8_t _buffer[max_buffer_size] = {0}; - uint16_t _buffer_length = 0; - char _msg_rcv[max_buffer_size]; - size_t _msg_length = 0; + std::array _buffer; + uint16_t _buffer_length = 0; + std::array _received_message; + int _msg_length = 0; bool _paired = false; // Check simultaneous pairing is possible bool _new_message = true; From 0aa32de1d87c6b9ea8be100ae72c1e3c5f831059 Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Tue, 12 Jul 2022 17:48:40 +0200 Subject: [PATCH 007/130] :heavy_plus_sign: (extern): Add semver header file for v0.3.0 https://github.com/Neargye/semver Author: Neargye --- include/semver/semver.hpp | 903 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 903 insertions(+) create mode 100644 include/semver/semver.hpp diff --git a/include/semver/semver.hpp b/include/semver/semver.hpp new file mode 100644 index 0000000000..f2b43c7b8d --- /dev/null +++ b/include/semver/semver.hpp @@ -0,0 +1,903 @@ +// _____ _ _ +// / ____| | | (_) +// | (___ ___ _ __ ___ __ _ _ __ | |_ _ ___ +// \___ \ / _ \ '_ ` _ \ / _` | '_ \| __| |/ __| +// ____) | __/ | | | | | (_| | | | | |_| | (__ +// |_____/ \___|_| |_| |_|\__,_|_| |_|\__|_|\___| +// __ __ _ _ _____ +// \ \ / / (_) (_) / ____|_ _ +// \ \ / /__ _ __ ___ _ ___ _ __ _ _ __ __ _ | | _| |_ _| |_ +// \ \/ / _ \ '__/ __| |/ _ \| '_ \| | '_ \ / _` | | | |_ _|_ _| +// \ / __/ | \__ \ | (_) | | | | | | | | (_| | | |____|_| |_| +// \/ \___|_| |___/_|\___/|_| |_|_|_| |_|\__, | \_____| +// https://github.com/Neargye/semver __/ | +// version 0.3.0 |___/ +// +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2018 - 2021 Daniil Goncharov . +// Copyright (c) 2020 - 2021 Alexander Gorbunov . +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef NEARGYE_SEMANTIC_VERSIONING_HPP +#define NEARGYE_SEMANTIC_VERSIONING_HPP + +#define SEMVER_VERSION_MAJOR 0 +#define SEMVER_VERSION_MINOR 3 +#define SEMVER_VERSION_PATCH 0 + +#include +#include +#include +#include +#include +#include +#include +#if __has_include() + #include +#else + #include +#endif + +#if defined(SEMVER_CONFIG_FILE) + #include SEMVER_CONFIG_FILE +#endif + +#if defined(SEMVER_THROW) +// define SEMVER_THROW(msg) to override semver throw behavior. +#elif defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) + #include + #define SEMVER_THROW(msg) (throw std::invalid_argument {msg}) +#else + #include + #include + #define SEMVER_THROW(msg) (assert(!msg), std::abort()) +#endif + +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wmissing-braces" // Ignore warning: suggest braces around initialization of + // subobject 'return {first, std::errc::invalid_argument};'. +#endif + +namespace semver { + +enum struct prerelease : std::uint8_t +{ + alpha = 0, + beta = 1, + rc = 2, + none = 3 +}; + +#if __has_include() +struct from_chars_result : std::from_chars_result { + [[nodiscard]] constexpr operator bool() const noexcept { return ec == std::errc {}; } +}; + +struct to_chars_result : std::to_chars_result { + [[nodiscard]] constexpr operator bool() const noexcept { return ec == std::errc {}; } +}; +#else +struct from_chars_result { + const char *ptr; + std::errc ec; + + [[nodiscard]] constexpr operator bool() const noexcept { return ec == std::errc {}; } +}; + +struct to_chars_result { + char *ptr; + std::errc ec; + + [[nodiscard]] constexpr operator bool() const noexcept { return ec == std::errc {}; } +}; +#endif + +// Max version string length = 3() + 1(.) + 3() + 1(.) + 3() + 1(-) + 5() + 1(.) + +// 3() = 21. +inline constexpr auto max_version_string_length = std::size_t {21}; + +namespace detail { + + inline constexpr auto alpha = std::string_view {"alpha", 5}; + inline constexpr auto beta = std::string_view {"beta", 4}; + inline constexpr auto rc = std::string_view {"rc", 2}; + + // Min version string length = 1() + 1(.) + 1() + 1(.) + 1() = 5. + inline constexpr auto min_version_string_length = 5; + + constexpr char to_lower(char c) noexcept + { + return (c >= 'A' && c <= 'Z') ? static_cast(c + ('a' - 'A')) : c; + } + + constexpr bool is_digit(char c) noexcept + { + return c >= '0' && c <= '9'; + } + + constexpr bool is_space(char c) noexcept + { + return c == ' '; + } + + constexpr bool is_operator(char c) noexcept + { + return c == '<' || c == '>' || c == '='; + } + + constexpr bool is_dot(char c) noexcept + { + return c == '.'; + } + + constexpr bool is_logical_or(char c) noexcept + { + return c == '|'; + } + + constexpr bool is_hyphen(char c) noexcept + { + return c == '-'; + } + + constexpr bool is_letter(char c) noexcept + { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); + } + + constexpr std::uint8_t to_digit(char c) noexcept + { + return static_cast(c - '0'); + } + + constexpr std::uint8_t length(std::uint8_t x) noexcept + { + return x < 10 ? 1 : (x < 100 ? 2 : 3); + } + + constexpr std::uint8_t length(prerelease t) noexcept + { + if (t == prerelease::alpha) { + return static_cast(alpha.length()); + } else if (t == prerelease::beta) { + return static_cast(beta.length()); + } else if (t == prerelease::rc) { + return static_cast(rc.length()); + } + + return 0; + } + + constexpr bool equals(const char *first, const char *last, std::string_view str) noexcept + { + for (std::size_t i = 0; first != last && i < str.length(); ++i, ++first) { + if (to_lower(*first) != to_lower(str[i])) { + return false; + } + } + + return true; + } + + constexpr char *to_chars(char *str, std::uint8_t x, bool dot = true) noexcept + { + do { + *(--str) = static_cast('0' + (x % 10)); + x /= 10; + } while (x != 0); + + if (dot) { + *(--str) = '.'; + } + + return str; + } + + constexpr char *to_chars(char *str, prerelease t) noexcept + { + const auto p = t == prerelease::alpha ? alpha + : t == prerelease::beta ? beta + : t == prerelease::rc ? rc + : std::string_view {}; + + if (p.size() > 0) { + for (auto it = p.rbegin(); it != p.rend(); ++it) { + *(--str) = *it; + } + *(--str) = '-'; + } + + return str; + } + + constexpr const char *from_chars(const char *first, const char *last, std::uint8_t &d) noexcept + { + if (first != last && is_digit(*first)) { + std::int32_t t = 0; + for (; first != last && is_digit(*first); ++first) { + t = t * 10 + to_digit(*first); + } + if (t <= (std::numeric_limits::max)()) { + d = static_cast(t); + return first; + } + } + + return nullptr; + } + + constexpr const char *from_chars(const char *first, const char *last, prerelease &p) noexcept + { + if (is_hyphen(*first)) { + ++first; + } + + if (equals(first, last, alpha)) { + p = prerelease::alpha; + return first + alpha.length(); + } else if (equals(first, last, beta)) { + p = prerelease::beta; + return first + beta.length(); + } else if (equals(first, last, rc)) { + p = prerelease::rc; + return first + rc.length(); + } + + return nullptr; + } + + constexpr bool check_delimiter(const char *first, const char *last, char d) noexcept + { + return first != last && first != nullptr && *first == d; + } + + template + struct resize_uninitialized { + static auto resize(T &str, std::size_t size) -> std::void_t { str.resize(size); } + }; + + template + struct resize_uninitialized().__resize_default_init(42))>> { + static void resize(T &str, std::size_t size) { str.__resize_default_init(size); } + }; + +} // namespace detail + +struct version { + std::uint8_t major = 0; + std::uint8_t minor = 1; + std::uint8_t patch = 0; + prerelease prerelease_type = prerelease::none; + std::uint8_t prerelease_number = 0; + + constexpr version(std::uint8_t mj, std::uint8_t mn, std::uint8_t pt, prerelease prt = prerelease::none, + std::uint8_t prn = 0) noexcept + : major {mj}, + minor {mn}, + patch {pt}, + prerelease_type {prt}, + prerelease_number {prt == prerelease::none ? static_cast(0) : prn} + { + } + + explicit constexpr version(std::string_view str) : version(0, 0, 0, prerelease::none, 0) { from_string(str); } + + constexpr version() = + default; // https://semver.org/#how-should-i-deal-with-revisions-in-the-0yz-initial-development-phase + + constexpr version(const version &) = default; + + constexpr version(version &&) = default; + + ~version() = default; + + version &operator=(const version &) = default; + + version &operator=(version &&) = default; + + [[nodiscard]] constexpr from_chars_result from_chars(const char *first, const char *last) noexcept + { + if (first == nullptr || last == nullptr || (last - first) < detail::min_version_string_length) { + return {first, std::errc::invalid_argument}; + } + + auto next = first; + if (next = detail::from_chars(next, last, major); detail::check_delimiter(next, last, '.')) { + if (next = detail::from_chars(++next, last, minor); detail::check_delimiter(next, last, '.')) { + if (next = detail::from_chars(++next, last, patch); next == last) { + prerelease_type = prerelease::none; + prerelease_number = 0; + return {next, std::errc {}}; + } else if (detail::check_delimiter(next, last, '-')) { + if (next = detail::from_chars(next, last, prerelease_type); next == last) { + prerelease_number = 0; + return {next, std::errc {}}; + } else if (detail::check_delimiter(next, last, '.')) { + if (next = detail::from_chars(++next, last, prerelease_number); next == last) { + return {next, std::errc {}}; + } + } + } + } + } + + return {first, std::errc::invalid_argument}; + } + + [[nodiscard]] constexpr to_chars_result to_chars(char *first, char *last) const noexcept + { + const auto length = string_length(); + if (first == nullptr || last == nullptr || (last - first) < length) { + return {last, std::errc::value_too_large}; + } + + auto next = first + length; + if (prerelease_type != prerelease::none) { + if (prerelease_number != 0) { + next = detail::to_chars(next, prerelease_number); + } + next = detail::to_chars(next, prerelease_type); + } + next = detail::to_chars(next, patch); + next = detail::to_chars(next, minor); + next = detail::to_chars(next, major, false); + + return {first + length, std::errc {}}; + } + + [[nodiscard]] constexpr bool from_string_noexcept(std::string_view str) noexcept + { + return from_chars(str.data(), str.data() + str.length()); + } + + constexpr version &from_string(std::string_view str) + { + if (!from_string_noexcept(str)) { + SEMVER_THROW("semver::version::from_string invalid version."); + } + + return *this; + } + + [[nodiscard]] std::string to_string() const + { + auto str = std::string {}; + detail::resize_uninitialized::resize(str, string_length()); + if (!to_chars(str.data(), str.data() + str.length())) { + SEMVER_THROW("semver::version::to_string invalid version."); + } + + return str; + } + + [[nodiscard]] constexpr std::uint8_t string_length() const noexcept + { + // () + 1(.) + () + 1(.) + () + auto length = detail::length(major) + detail::length(minor) + detail::length(patch) + 2; + if (prerelease_type != prerelease::none) { + // + 1(-) + () + length += detail::length(prerelease_type) + 1; + if (prerelease_number != 0) { + // + 1(.) + () + length += detail::length(prerelease_number) + 1; + } + } + + return static_cast(length); + } + + [[nodiscard]] constexpr int compare(const version &other) const noexcept + { + if (major != other.major) { + return major - other.major; + } + + if (minor != other.minor) { + return minor - other.minor; + } + + if (patch != other.patch) { + return patch - other.patch; + } + + if (prerelease_type != other.prerelease_type) { + return static_cast(prerelease_type) - static_cast(other.prerelease_type); + } + + if (prerelease_number != other.prerelease_number) { + return prerelease_number - other.prerelease_number; + } + + return 0; + } +}; + +[[nodiscard]] constexpr bool operator==(const version &lhs, const version &rhs) noexcept +{ + return lhs.compare(rhs) == 0; +} + +[[nodiscard]] constexpr bool operator!=(const version &lhs, const version &rhs) noexcept +{ + return lhs.compare(rhs) != 0; +} + +[[nodiscard]] constexpr bool operator>(const version &lhs, const version &rhs) noexcept +{ + return lhs.compare(rhs) > 0; +} + +[[nodiscard]] constexpr bool operator>=(const version &lhs, const version &rhs) noexcept +{ + return lhs.compare(rhs) >= 0; +} + +[[nodiscard]] constexpr bool operator<(const version &lhs, const version &rhs) noexcept +{ + return lhs.compare(rhs) < 0; +} + +[[nodiscard]] constexpr bool operator<=(const version &lhs, const version &rhs) noexcept +{ + return lhs.compare(rhs) <= 0; +} + +[[nodiscard]] constexpr version operator""_version(const char *str, std::size_t length) +{ + return version {std::string_view {str, length}}; +} + +[[nodiscard]] constexpr bool valid(std::string_view str) noexcept +{ + return version {}.from_string_noexcept(str); +} + +[[nodiscard]] constexpr from_chars_result from_chars(const char *first, const char *last, version &v) noexcept +{ + return v.from_chars(first, last); +} + +[[nodiscard]] constexpr to_chars_result to_chars(char *first, char *last, const version &v) noexcept +{ + return v.to_chars(first, last); +} + +[[nodiscard]] constexpr std::optional from_string_noexcept(std::string_view str) noexcept +{ + if (version v {}; v.from_string_noexcept(str)) { + return v; + } + + return std::nullopt; +} + +[[nodiscard]] constexpr version from_string(std::string_view str) +{ + return version {str}; +} + +[[nodiscard]] inline std::string to_string(const version &v) +{ + return v.to_string(); +} + +template +inline std::basic_ostream &operator<<(std::basic_ostream &os, const version &v) +{ + for (const auto c: v.to_string()) { + os.put(c); + } + + return os; +} + +inline namespace comparators { + + enum struct comparators_option : std::uint8_t + { + exclude_prerelease, + include_prerelease + }; + + [[nodiscard]] constexpr int compare(const version &lhs, const version &rhs, + comparators_option option = comparators_option::include_prerelease) noexcept + { + if (option == comparators_option::exclude_prerelease) { + return version {lhs.major, lhs.minor, lhs.patch}.compare(version {rhs.major, rhs.minor, rhs.patch}); + } + return lhs.compare(rhs); + } + + [[nodiscard]] constexpr bool equal_to(const version &lhs, const version &rhs, + comparators_option option = comparators_option::include_prerelease) noexcept + { + return compare(lhs, rhs, option) == 0; + } + + [[nodiscard]] constexpr bool not_equal_to( + const version &lhs, const version &rhs, + comparators_option option = comparators_option::include_prerelease) noexcept + { + return compare(lhs, rhs, option) != 0; + } + + [[nodiscard]] constexpr bool greater(const version &lhs, const version &rhs, + comparators_option option = comparators_option::include_prerelease) noexcept + { + return compare(lhs, rhs, option) > 0; + } + + [[nodiscard]] constexpr bool greater_equal( + const version &lhs, const version &rhs, + comparators_option option = comparators_option::include_prerelease) noexcept + { + return compare(lhs, rhs, option) >= 0; + } + + [[nodiscard]] constexpr bool less(const version &lhs, const version &rhs, + comparators_option option = comparators_option::include_prerelease) noexcept + { + return compare(lhs, rhs, option) < 0; + } + + [[nodiscard]] constexpr bool less_equal(const version &lhs, const version &rhs, + comparators_option option = comparators_option::include_prerelease) noexcept + { + return compare(lhs, rhs, option) <= 0; + } + +} // namespace comparators + +namespace range { + + namespace detail { + + using namespace semver::detail; + + class range + { + public: + constexpr explicit range(std::string_view str) noexcept : parser {str} {} + + constexpr bool satisfies(const version &ver, bool include_prerelease) + { + const bool has_prerelease = ver.prerelease_type != prerelease::none; + + do { + if (is_logical_or_token()) { + parser.advance_token(range_token_type::logical_or); + } + + bool contains = true; + bool allow_compare = include_prerelease; + + while (is_operator_token() || is_number_token()) { + const auto range = parser.parse_range(); + const bool equal_without_tags = + equal_to(range.ver, ver, comparators_option::exclude_prerelease); + + if (has_prerelease && equal_without_tags) { + allow_compare = true; + } + + if (!range.satisfies(ver)) { + contains = false; + break; + } + } + + if (has_prerelease) { + if (allow_compare && contains) { + return true; + } + } else if (contains) { + return true; + } + + } while (is_logical_or_token()); + + return false; + } + + private: + enum struct range_operator : std::uint8_t + { + less, + less_or_equal, + greater, + greater_or_equal, + equal + }; + + struct range_comparator { + range_operator op; + version ver; + + constexpr bool satisfies(const version &version) const + { + switch (op) { + case range_operator::equal: + return version == ver; + case range_operator::greater: + return version > ver; + case range_operator::greater_or_equal: + return version >= ver; + case range_operator::less: + return version < ver; + case range_operator::less_or_equal: + return version <= ver; + default: + SEMVER_THROW("semver::range unexpected operator."); + } + } + }; + + enum struct range_token_type : std::uint8_t + { + none, + number, + range_operator, + dot, + logical_or, + hyphen, + prerelease, + end_of_line + }; + + struct range_token { + range_token_type type = range_token_type::none; + std::uint8_t number = 0; + range_operator op = range_operator::equal; + prerelease prerelease_type = prerelease::none; + }; + + struct range_lexer { + std::string_view text; + std::size_t pos; + + constexpr explicit range_lexer(std::string_view text) noexcept : text {text}, pos {0} {} + + constexpr range_token get_next_token() noexcept + { + while (!end_of_line()) { + if (is_space(text[pos])) { + advance(1); + continue; + } + + if (is_logical_or(text[pos])) { + advance(2); + return {range_token_type::logical_or}; + } + + if (is_operator(text[pos])) { + const auto op = get_operator(); + return {range_token_type::range_operator, 0, op}; + } + + if (is_digit(text[pos])) { + const auto number = get_number(); + return {range_token_type::number, number}; + } + + if (is_dot(text[pos])) { + advance(1); + return {range_token_type::dot}; + } + + if (is_hyphen(text[pos])) { + advance(1); + return {range_token_type::hyphen}; + } + + if (is_letter(text[pos])) { + const auto prerelease = get_prerelease(); + return {range_token_type::prerelease, 0, range_operator::equal, prerelease}; + } + } + + return {range_token_type::end_of_line}; + } + + constexpr bool end_of_line() const noexcept { return pos >= text.length(); } + + constexpr void advance(std::size_t i) noexcept { pos += i; } + + constexpr range_operator get_operator() noexcept + { + if (text[pos] == '<') { + advance(1); + if (text[pos] == '=') { + advance(1); + return range_operator::less_or_equal; + } + return range_operator::less; + } else if (text[pos] == '>') { + advance(1); + if (text[pos] == '=') { + advance(1); + return range_operator::greater_or_equal; + } + return range_operator::greater; + } else if (text[pos] == '=') { + advance(1); + return range_operator::equal; + } + + return range_operator::equal; + } + + constexpr std::uint8_t get_number() noexcept + { + const auto first = text.data() + pos; + const auto last = text.data() + text.length(); + if (std::uint8_t n {}; from_chars(first, last, n) != nullptr) { + advance(length(n)); + return n; + } + + return 0; + } + + constexpr prerelease get_prerelease() noexcept + { + const auto first = text.data() + pos; + const auto last = text.data() + text.length(); + if (first > last) { + advance(1); + return prerelease::none; + } + + if (prerelease p {}; from_chars(first, last, p) != nullptr) { + advance(length(p)); + return p; + } + + advance(1); + + return prerelease::none; + } + }; + + struct range_parser { + range_lexer lexer; + range_token current_token; + + constexpr explicit range_parser(std::string_view str) + : lexer {str}, current_token {range_token_type::none} + { + advance_token(range_token_type::none); + } + + constexpr void advance_token(range_token_type token_type) + { + if (current_token.type != token_type) { + SEMVER_THROW("semver::range unexpected token."); + } + current_token = lexer.get_next_token(); + } + + constexpr range_comparator parse_range() + { + if (current_token.type == range_token_type::number) { + const auto version = parse_version(); + return {range_operator::equal, version}; + } else if (current_token.type == range_token_type::range_operator) { + const auto range_operator = current_token.op; + advance_token(range_token_type::range_operator); + const auto version = parse_version(); + return {range_operator, version}; + } + + return {range_operator::equal, version {}}; + } + + constexpr version parse_version() + { + const auto major = parse_number(); + + advance_token(range_token_type::dot); + const auto minor = parse_number(); + + advance_token(range_token_type::dot); + const auto patch = parse_number(); + + prerelease prerelease = prerelease::none; + std::uint8_t prerelease_number = 0; + + if (current_token.type == range_token_type::hyphen) { + advance_token(range_token_type::hyphen); + prerelease = parse_prerelease(); + advance_token(range_token_type::dot); + prerelease_number = parse_number(); + } + + return {major, minor, patch, prerelease, prerelease_number}; + } + + constexpr std::uint8_t parse_number() + { + const auto token = current_token; + advance_token(range_token_type::number); + + return token.number; + } + + constexpr prerelease parse_prerelease() + { + const auto token = current_token; + advance_token(range_token_type::prerelease); + + return token.prerelease_type; + } + }; + + [[nodiscard]] constexpr bool is_logical_or_token() const noexcept + { + return parser.current_token.type == range_token_type::logical_or; + } + [[nodiscard]] constexpr bool is_operator_token() const noexcept + { + return parser.current_token.type == range_token_type::range_operator; + } + + [[nodiscard]] constexpr bool is_number_token() const noexcept + { + return parser.current_token.type == range_token_type::number; + } + + range_parser parser; + }; + + } // namespace detail + + enum struct satisfies_option : std::uint8_t + { + exclude_prerelease, + include_prerelease + }; + + constexpr bool satisfies(const version &ver, std::string_view str, + satisfies_option option = satisfies_option::exclude_prerelease) + { + switch (option) { + case satisfies_option::exclude_prerelease: + return detail::range {str}.satisfies(ver, false); + case satisfies_option::include_prerelease: + return detail::range {str}.satisfies(ver, true); + default: + SEMVER_THROW("semver::range unexpected satisfies_option."); + } + } + +} // namespace range + +// Version lib semver. +inline constexpr auto semver_version = version {SEMVER_VERSION_MAJOR, SEMVER_VERSION_MINOR, SEMVER_VERSION_PATCH}; + +} // namespace semver + +#if defined(__clang__) + #pragma clang diagnostic pop +#endif + +#endif // NEARGYE_SEMANTIC_VERSIONING_HPP From bc7aa017ddf4a1eebe7b8c2e0ec80e9a84210d16 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Tue, 26 Jul 2022 16:49:53 +0200 Subject: [PATCH 008/130] :sparkles: (spikes): Show MagicCard ID in lk_rfid --- spikes/lk_rfid/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/spikes/lk_rfid/main.cpp b/spikes/lk_rfid/main.cpp index 976d67b4cd..860e539ab5 100644 --- a/spikes/lk_rfid/main.cpp +++ b/spikes/lk_rfid/main.cpp @@ -29,6 +29,7 @@ auto main() -> int rtos::ThisThread::sleep_for(2s); rfidkit.init(); + rfidkit.onTagActivated([](const MagicCard &card) { log_debug("Card id: %i", card.getId()); }); HelloWorld hello; hello.start(); From 8bdc5e67515ea3956fa57382cb28d76628b2694b Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Tue, 26 Jul 2022 17:25:01 +0200 Subject: [PATCH 009/130] :clown_face: (mock): CoreBufferedSerial - Fix mock return type --- tests/unit/mocks/mocks/leka/CoreBufferedSerial.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/mocks/mocks/leka/CoreBufferedSerial.h b/tests/unit/mocks/mocks/leka/CoreBufferedSerial.h index 0dca32674b..cd26950e36 100644 --- a/tests/unit/mocks/mocks/leka/CoreBufferedSerial.h +++ b/tests/unit/mocks/mocks/leka/CoreBufferedSerial.h @@ -12,8 +12,8 @@ namespace leka::mock { class CoreBufferedSerial : public interface::BufferedSerial { public: - MOCK_METHOD(ssize_t, read, (uint8_t *, std::size_t), (override)); - MOCK_METHOD(ssize_t, write, (const uint8_t *, std::size_t), (override)); + MOCK_METHOD(std::size_t, read, (uint8_t *, std::size_t), (override)); + MOCK_METHOD(std::size_t, write, (const uint8_t *, std::size_t), (override)); MOCK_METHOD(bool, readable, (), (override)); MOCK_METHOD(void, sigio, (mbed::Callback), (override)); }; From cd4b61c9d7a50dcb0738e131834bd6240067355a Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Thu, 21 Jul 2022 12:40:45 +0200 Subject: [PATCH 010/130] :wrench: (fs): Add /fs/sys/hardware-version --- fs/sys/hardware-version | 1 + 1 file changed, 1 insertion(+) create mode 100755 fs/sys/hardware-version diff --git a/fs/sys/hardware-version b/fs/sys/hardware-version new file mode 100755 index 0000000000..d3827e75a5 --- /dev/null +++ b/fs/sys/hardware-version @@ -0,0 +1 @@ +1.0 From 7302d0adbbd2c168a958bc5a39626545f92ff29e Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Thu, 28 Jul 2022 18:02:59 +0200 Subject: [PATCH 011/130] :bento: (Magic Cards): Add missing cards --- libs/RFIDKit/include/MagicCard.h | 110 +++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/libs/RFIDKit/include/MagicCard.h b/libs/RFIDKit/include/MagicCard.h index 28f13bda53..f8da96417b 100644 --- a/libs/RFIDKit/include/MagicCard.h +++ b/libs/RFIDKit/include/MagicCard.h @@ -44,6 +44,14 @@ struct MagicCard { static const MagicCard emergency_stop; static const MagicCard dice_roll; + static const MagicCard color_purple; + static const MagicCard color_indigo; + static const MagicCard color_blue; + static const MagicCard color_green; + static const MagicCard color_yellow; + static const MagicCard color_orange; + static const MagicCard color_red; + static const MagicCard number_0; static const MagicCard number_1; static const MagicCard number_2; @@ -56,6 +64,53 @@ struct MagicCard { static const MagicCard number_9; static const MagicCard number_10; + static const MagicCard shape_square; + static const MagicCard shape_circle; + static const MagicCard shape_triangle; + static const MagicCard shape_star; + + static const MagicCard activity_music_quest; + static const MagicCard activity_super_simon; + static const MagicCard activity_colored_quest; + static const MagicCard activity_music_colored_board; + static const MagicCard activity_hide_and_seek; + static const MagicCard activity_colors_and_sounds; + static const MagicCard activity_magic_objects; + static const MagicCard activity_dance_freeze; + + static const MagicCard remote_standard; + static const MagicCard remote_colored_arrows; + + static const MagicCard reinforcer_1_blink_green; + static const MagicCard reinforcer_2_spin_blink; + static const MagicCard reinforcer_3_fire; + static const MagicCard reinforcer_4_sprinkles; + static const MagicCard reinforcer_5_rainbow; + + static const MagicCard emotion_fear_child; + static const MagicCard emotion_disgust_child; + static const MagicCard emotion_anger_child; + static const MagicCard emotion_joy_child; + static const MagicCard emotion_sadness_child; + static const MagicCard emotion_fear_leka; + static const MagicCard emotion_disgust_leka; + static const MagicCard emotion_anger_leka; + static const MagicCard emotion_joy_leka; + static const MagicCard emotion_sadness_leka; + + static const MagicCard vegetable_carrot_orange; + static const MagicCard vegetable_potato_yellow; + static const MagicCard vegetable_salad_green; + static const MagicCard vegetable_mushroom_grey; + static const MagicCard fruit_strawberry_red; + static const MagicCard fruit_cherry_pink; + static const MagicCard fruit_apple_green; + static const MagicCard fruit_banana_yellow; + static const MagicCard fruit_grapes_black; + + static const MagicCard math_arithmetic_substraction_sign_minus; + static const MagicCard math_arithmetic_addition_sign_plus; + private: static constexpr auto id_high_byte_index = 4; static constexpr auto id_low_byte_index = 5; @@ -67,6 +122,14 @@ constexpr MagicCard MagicCard::none = MagicCard {0x00'00}; constexpr MagicCard MagicCard::emergency_stop = MagicCard {0x00'01}; constexpr MagicCard MagicCard::dice_roll = MagicCard {0x00'02}; +constexpr MagicCard MagicCard::color_purple = MagicCard {0x00'03}; +constexpr MagicCard MagicCard::color_indigo = MagicCard {0x00'04}; +constexpr MagicCard MagicCard::color_blue = MagicCard {0x00'05}; +constexpr MagicCard MagicCard::color_green = MagicCard {0x00'06}; +constexpr MagicCard MagicCard::color_yellow = MagicCard {0x00'07}; +constexpr MagicCard MagicCard::color_orange = MagicCard {0x00'08}; +constexpr MagicCard MagicCard::color_red = MagicCard {0x00'09}; + constexpr MagicCard MagicCard::number_0 = MagicCard {0x00'0A}; constexpr MagicCard MagicCard::number_1 = MagicCard {0x00'0B}; constexpr MagicCard MagicCard::number_2 = MagicCard {0x00'0C}; @@ -79,4 +142,51 @@ constexpr MagicCard MagicCard::number_8 = MagicCard {0x00'12}; constexpr MagicCard MagicCard::number_9 = MagicCard {0x00'13}; constexpr MagicCard MagicCard::number_10 = MagicCard {0x00'14}; +constexpr MagicCard MagicCard::shape_square = MagicCard {0x00'15}; +constexpr MagicCard MagicCard::shape_circle = MagicCard {0x00'16}; +constexpr MagicCard MagicCard::shape_triangle = MagicCard {0x00'17}; +constexpr MagicCard MagicCard::shape_star = MagicCard {0x00'18}; + +constexpr MagicCard MagicCard::activity_music_quest = MagicCard {0x00'19}; +constexpr MagicCard MagicCard::activity_super_simon = MagicCard {0x00'1A}; +constexpr MagicCard MagicCard::activity_colored_quest = MagicCard {0x00'1B}; +constexpr MagicCard MagicCard::activity_music_colored_board = MagicCard {0x00'1C}; +constexpr MagicCard MagicCard::activity_hide_and_seek = MagicCard {0x00'1D}; +constexpr MagicCard MagicCard::activity_colors_and_sounds = MagicCard {0x00'1E}; +constexpr MagicCard MagicCard::activity_magic_objects = MagicCard {0x00'1F}; +constexpr MagicCard MagicCard::activity_dance_freeze = MagicCard {0x00'20}; + +constexpr MagicCard MagicCard::activity_magic_objects = MagicCard {0x00'21}; +constexpr MagicCard MagicCard::activity_dance_freeze = MagicCard {0x00'22}; + +constexpr MagicCard MagicCard::reinforcer_1_blink_green = MagicCard {0x00'23}; +constexpr MagicCard MagicCard::reinforcer_2_spin_blink = MagicCard {0x00'24}; +constexpr MagicCard MagicCard::reinforcer_3_fire = MagicCard {0x00'25}; +constexpr MagicCard MagicCard::reinforcer_4_sprinkles = MagicCard {0x00'26}; +constexpr MagicCard MagicCard::reinforcer_5_rainbow = MagicCard {0x00'27}; + +constexpr MagicCard MagicCard::emotion_fear_child = MagicCard {0x00'28}; +constexpr MagicCard MagicCard::emotion_disgust_child = MagicCard {0x00'29}; +constexpr MagicCard MagicCard::emotion_anger_child = MagicCard {0x00'2A}; +constexpr MagicCard MagicCard::emotion_joy_child = MagicCard {0x00'2B}; +constexpr MagicCard MagicCard::emotion_sadness_child = MagicCard {0x00'2C}; +constexpr MagicCard MagicCard::emotion_fear_leka = MagicCard {0x00'2D}; +constexpr MagicCard MagicCard::emotion_disgust_leka = MagicCard {0x00'2E}; +constexpr MagicCard MagicCard::emotion_anger_leka = MagicCard {0x00'2F}; +constexpr MagicCard MagicCard::emotion_joy_leka = MagicCard {0x00'30}; +constexpr MagicCard MagicCard::emotion_sadness_leka = MagicCard {0x00'31}; + +constexpr MagicCard MagicCard::vegetable_carrot_orange = MagicCard {0x00'32}; +constexpr MagicCard MagicCard::vegetable_potato_yellow = MagicCard {0x00'33}; +constexpr MagicCard MagicCard::vegetable_salad_green = MagicCard {0x00'34}; +constexpr MagicCard MagicCard::vegetable_mushroom_grey = MagicCard {0x00'35}; +constexpr MagicCard MagicCard::fruit_strawberry_red = MagicCard {0x00'36}; +constexpr MagicCard MagicCard::fruit_cherry_pink = MagicCard {0x00'37}; +constexpr MagicCard MagicCard::fruit_apple_green = MagicCard {0x00'38}; +constexpr MagicCard MagicCard::fruit_banana_yellow = MagicCard {0x00'39}; +constexpr MagicCard MagicCard::fruit_grapes_black = MagicCard {0x00'3A}; + +constexpr MagicCard MagicCard::math_arithmetic_substraction_sign_minus = MagicCard {0x00'3B}; +constexpr MagicCard MagicCard::math_arithmetic_addition_sign_plus = MagicCard {0x00'3C}; + } // namespace leka From b2bf395ba1e839fe6694a8e6b02d9509f88e0a1b Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Tue, 5 Jul 2022 18:44:56 +0200 Subject: [PATCH 012/130] :building_construction: (interface): Refactor & clean up interface RFIDReader --- include/interface/drivers/RFIDReader.h | 71 +++++--------------------- 1 file changed, 12 insertions(+), 59 deletions(-) diff --git a/include/interface/drivers/RFIDReader.h b/include/interface/drivers/RFIDReader.h index 4d0130af0a..e87d8d1753 100644 --- a/include/interface/drivers/RFIDReader.h +++ b/include/interface/drivers/RFIDReader.h @@ -10,9 +10,8 @@ #include "platform/Callback.h" -using tag_available_callback_t = mbed::Callback; - #include "BufferedSerial.h" +#include "boost/sml.hpp" namespace leka { @@ -54,63 +53,17 @@ namespace interface { public: virtual ~RFIDReader() = default; - virtual void init() = 0; - - virtual void registerTagAvailableCallback(tag_available_callback_t callback) = 0; - virtual void onDataAvailable() = 0; - - virtual auto setBaudrate(uint8_t baudrate) -> bool = 0; - - virtual auto setCommunicationProtocol(rfid::Protocol protocol) -> bool = 0; - - virtual void sendCommandToTag(std::span cmd) = 0; - - virtual auto receiveDataFromTag(std::span data) -> bool = 0; - - virtual void setTagDetectionMode() = 0; - - virtual auto isTagDetected() -> bool = 0; - - class ISO14443 - { - public: - virtual ~ISO14443() = default; - - virtual void init() = 0; - - virtual void runStateMachine() = 0; - - template - struct Command { - const std::array data; - const leka::rfid::Flag flags; - - [[nodiscard]] inline auto getArray() const -> std::array - { - auto cmd = std::array {}; - - for (int i = 0; i < SIZE; ++i) { - cmd[i] = data[i]; - } - - cmd[SIZE] = static_cast(flags); - - return cmd; - } - }; - - Command<1> command_requestA = {.data = {0x26}, .flags = leka::rfid::Flag::sb_7}; - Command<2> command_read_register_0 = {.data = {0x30, 0x00}, - .flags = leka::rfid::Flag::crc | leka::rfid::Flag::sb_8}; - Command<2> command_read_register_4 = {.data = {0x30, 0x04}, - .flags = leka::rfid::Flag::crc | leka::rfid::Flag::sb_8}; - Command<2> command_read_register_6 = {.data = {0x30, 0x06}, - .flags = leka::rfid::Flag::crc | leka::rfid::Flag::sb_8}; - - std::array ATQA_answer {0x44, 0x00}; - - std::array _tag_data {0}; - }; + virtual void init() = 0; + virtual void setTagDetectionMode() = 0; + virtual auto isTagDetected() -> bool = 0; + virtual void onTagValid() = 0; + virtual auto getTagData() -> rfid::Tag = 0; + virtual void getDataFromTag(std::span data) = 0; + virtual auto didTagCommunicationSucceed(size_t sizeTagData) -> bool = 0; + virtual void sendCommandToTag(std::span cmd) = 0; + virtual void setCommunicationProtocol(rfid::Protocol protocol) = 0; + virtual void registerOnTagDetectedCallback(mbed::Callback callback) = 0; + virtual void registerOnTagValidCallback(mbed::Callback callback) = 0; }; } // namespace interface From 0c9da67d7a51b02c12a05cfab949208796ae364f Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Tue, 5 Jul 2022 18:53:08 +0200 Subject: [PATCH 013/130] :recycle: (drivers): Refactor & clean up CoreRFIDReader --- .../CoreRFIDReader/include/CoreRFIDReader.h | 47 +++----- .../CoreRFIDReader/source/CoreRFIDReader.cpp | 101 ++++++------------ 2 files changed, 52 insertions(+), 96 deletions(-) diff --git a/drivers/CoreRFIDReader/include/CoreRFIDReader.h b/drivers/CoreRFIDReader/include/CoreRFIDReader.h index 53cbb30a01..3289fddaa1 100644 --- a/drivers/CoreRFIDReader/include/CoreRFIDReader.h +++ b/drivers/CoreRFIDReader/include/CoreRFIDReader.h @@ -4,9 +4,6 @@ #pragma once -#include -#include - #include "events/EventQueue.h" #include "rtos/Thread.h" @@ -177,48 +174,38 @@ class CoreRFIDReader : public interface::RFIDReader explicit CoreRFIDReader(interface::BufferedSerial &serial) : _serial(serial) {}; void init() final; - - void registerTagAvailableCallback(tag_available_callback_t callback) final; - void onDataAvailable() final; - - auto setBaudrate(uint8_t baudrate) -> bool final; - - auto setCommunicationProtocol(rfid::Protocol protocol) -> bool final; - - void sendCommandToTag(std::span cmd) final; - auto receiveDataFromTag(std::span data) -> bool final; - void setTagDetectionMode() final; - auto isTagDetected() -> bool final; + void onTagValid() final; + auto getTagData() -> rfid::Tag final; + void getDataFromTag(std::span data) final; + auto didTagCommunicationSucceed(size_t sizeTagData) -> bool final; + void sendCommandToTag(std::span cmd) final; + void setCommunicationProtocol(rfid::Protocol protocol) final; + void registerOnTagDetectedCallback(mbed::Callback callback) final; + void registerOnTagValidCallback(mbed::Callback callback) final; private: - void registerCallback(); - - auto setBaudrateDidSucceed() -> bool; + void registerOnDataAvailableCallback(); + void onDataAvailable(); - auto setProtocolISO14443A() -> bool; - auto setGainAndModulationISO14443A() -> bool; + void setProtocolISO14443A(); + void setGainAndModulationISO14443A(); - auto didsetCommunicationProtocolSucceed() -> bool; void read(); auto formatCommand(std::span cmd) -> size_t; - auto DataFromTagIsCorrect(size_t sizeTagData) -> bool; - void copyTagDataToSpan(std::span data); - - tag_available_callback_t _tagAvailableCallback; - - interface::BufferedSerial &_serial; + rfid::Tag _tag {}; + size_t _anwser_size {0}; rtos::Thread _thread {}; events::EventQueue _event_queue {}; + interface::BufferedSerial &_serial; + mbed::Callback _on_tag_detected; + mbed::Callback _on_tag_valid; - size_t _anwser_size {0}; std::array _tx_buf {}; std::array _rx_buf {}; - - rfid::Tag _tag {}; }; } // namespace leka diff --git a/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp b/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp index 2b2bfbee4d..fac570f14e 100644 --- a/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp +++ b/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp @@ -3,10 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 #include "CoreRFIDReader.h" -#include -#include -#include -#include #include "rtos/ThisThread.h" using namespace std::chrono; @@ -16,15 +12,20 @@ namespace leka { void CoreRFIDReader::init() { _thread.start({&_event_queue, &events::EventQueue::dispatch_forever}); - registerCallback(); + registerOnDataAvailableCallback(); } -void CoreRFIDReader::registerTagAvailableCallback(tag_available_callback_t callback) +void CoreRFIDReader::registerOnTagDetectedCallback(mbed::Callback callback) { - _tagAvailableCallback = callback; + _on_tag_detected = callback; }; -void CoreRFIDReader::registerCallback() +void CoreRFIDReader::registerOnTagValidCallback(mbed::Callback callback) +{ + _on_tag_valid = callback; +}; + +void CoreRFIDReader::registerOnDataAvailableCallback() { auto func = [this]() { this->onDataAvailable(); }; auto callback = [this, func] { _event_queue.call(func); }; @@ -34,7 +35,7 @@ void CoreRFIDReader::registerCallback() void CoreRFIDReader::onDataAvailable() { read(); - _tagAvailableCallback(); + _on_tag_detected(); } void CoreRFIDReader::read() @@ -64,61 +65,24 @@ void CoreRFIDReader::setTagDetectionMode() rfid::command::frame::set_tag_detection_mode.size()); } -auto CoreRFIDReader::setBaudrate(uint8_t baudrate) -> bool -{ - std::array set_baudrate_frame = {rfid::command::set_baudrate::id, rfid::command::set_baudrate::length, - baudrate}; - - _serial.write(set_baudrate_frame.data(), set_baudrate_frame.size()); - - return (setBaudrateDidSucceed()); -} - -auto CoreRFIDReader::setBaudrateDidSucceed() -> bool -{ - read(); - if (_anwser_size != rfid::expected_answer_size::set_baudrate) { - return false; - } - - return _rx_buf[0] == 0x55; -} - -auto CoreRFIDReader::setCommunicationProtocol(rfid::Protocol protocol) -> bool +void CoreRFIDReader::setCommunicationProtocol(rfid::Protocol protocol) { - auto setCommunicationProtocol = bool {false}; if (protocol == rfid::Protocol::ISO14443A) { - setCommunicationProtocol = setProtocolISO14443A() && setGainAndModulationISO14443A(); + setProtocolISO14443A(); + setGainAndModulationISO14443A(); } - - return setCommunicationProtocol; } -auto CoreRFIDReader::setProtocolISO14443A() -> bool +void CoreRFIDReader::setProtocolISO14443A() { _serial.write(rfid::command::frame::set_protocol_iso14443.data(), rfid::command::frame::set_protocol_iso14443.size()); - - return didsetCommunicationProtocolSucceed(); } -auto CoreRFIDReader::setGainAndModulationISO14443A() -> bool +void CoreRFIDReader::setGainAndModulationISO14443A() { _serial.write(rfid::command::frame::set_gain_and_modulation.data(), rfid::command::frame::set_gain_and_modulation.size()); - - return didsetCommunicationProtocolSucceed(); -} - -auto CoreRFIDReader::didsetCommunicationProtocolSucceed() -> bool -{ - if (_anwser_size != rfid::expected_answer_size::set_communication_protocol) { - return false; - } - - std::array buffer {_rx_buf[0], _rx_buf[1]}; - - return buffer == rfid::status::setup_success; } void CoreRFIDReader::sendCommandToTag(std::span cmd) @@ -134,36 +98,41 @@ auto CoreRFIDReader::formatCommand(std::span cmd) -> size_t _tx_buf[1] = static_cast(cmd.size()); for (auto i = 0; i < cmd.size(); ++i) { - _tx_buf[i + rfid::tag_answer::heading_size] = cmd[i]; + _tx_buf.at(i + rfid::tag_answer::heading_size) = cmd[i]; } return cmd.size() + rfid::tag_answer::heading_size; } -auto CoreRFIDReader::receiveDataFromTag(std::span data) -> bool -{ - if (!DataFromTagIsCorrect(data.size())) { - return false; - } - - copyTagDataToSpan(data); - - return true; -} - -auto CoreRFIDReader::DataFromTagIsCorrect(size_t sizeTagData) -> bool +auto CoreRFIDReader::didTagCommunicationSucceed(size_t sizeTagData) -> bool { uint8_t status = _rx_buf[0]; uint8_t length = _rx_buf[1]; - return (status == rfid::status::communication_succeed && sizeTagData == length - rfid::tag_answer::flag_size); + auto didCommunicationSucceed = status == rfid::status::communication_succeed; + auto didCommandSameSize = sizeTagData == length - rfid::tag_answer::flag_size; + + return (didCommunicationSucceed && didCommandSameSize); } -void CoreRFIDReader::copyTagDataToSpan(std::span data) +void CoreRFIDReader::getDataFromTag(std::span data) { for (auto i = 0; i < data.size(); ++i) { data[i] = _rx_buf[i + rfid::tag_answer::heading_size]; } } +auto CoreRFIDReader::getTagData() -> rfid::Tag +{ + for (size_t i = 0; i < 18; ++i) { + _tag.data[i] = _rx_buf[i + rfid::tag_answer::heading_size]; + } + return _tag; +} + +void CoreRFIDReader::onTagValid() +{ + _on_tag_valid(); +} + } // namespace leka From ecb47431abf101348fbc3d230db4a3e35d90ea43 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Tue, 5 Jul 2022 18:58:39 +0200 Subject: [PATCH 014/130] :recycle: (protocol): Add Protocol ISO14443 as an SM --- libs/RFIDKit/include/ISO14443A.h | 187 +++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 libs/RFIDKit/include/ISO14443A.h diff --git a/libs/RFIDKit/include/ISO14443A.h b/libs/RFIDKit/include/ISO14443A.h new file mode 100644 index 0000000000..b404404612 --- /dev/null +++ b/libs/RFIDKit/include/ISO14443A.h @@ -0,0 +1,187 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "boost/sml.hpp" +#include "interface/drivers/RFIDReader.h" + +namespace leka::rfid { + +template +struct Command { + const std::array data; + const leka::rfid::Flag flags; + + [[nodiscard]] inline auto getArray() const -> std::array + { + auto cmd = std::array {}; + + for (int i = 0; i < SIZE; ++i) { + cmd[i] = data[i]; + } + + cmd[SIZE] = static_cast(flags); + + return cmd; + } +}; + +constexpr Command<1> command_requestA = {.data = {0x26}, .flags = leka::rfid::Flag::sb_7}; +constexpr Command<2> command_read_register_0 = {.data = {0x30, 0x00}, + .flags = leka::rfid::Flag::crc | leka::rfid::Flag::sb_8}; +constexpr Command<2> command_read_register_4 = {.data = {0x30, 0x04}, + .flags = leka::rfid::Flag::crc | leka::rfid::Flag::sb_8}; +constexpr Command<2> command_read_register_6 = {.data = {0x30, 0x06}, + .flags = leka::rfid::Flag::crc | leka::rfid::Flag::sb_8}; + +constexpr size_t ATQA_answer_size = 2; +constexpr size_t register_answer_size = 18; +constexpr std::array expected_ATQA_answer {0x44, 0x00}; + +inline auto computeCRC(uint8_t const *data) -> std::array +{ + uint32_t wCrc = 0x6363; + size_t size = 16; + + do { + std::byte bt; + bt = static_cast(*data++); + bt = (bt ^ static_cast(wCrc & 0x00FF)); + bt = (bt ^ (bt << 4)); + wCrc = (wCrc >> 8) ^ (static_cast(bt) << 8) ^ (static_cast(bt) << 3) ^ + (static_cast(bt) >> 4); + } while (--size); + + std::array pbtCrc = {static_cast(wCrc & 0xFF), static_cast((wCrc >> 8) & 0xFF)}; + return pbtCrc; +} + +inline auto receiveAtqa(interface::RFIDReader &rfidreader) -> bool +{ + std::array atqa_answer {}; + + if (rfidreader.didTagCommunicationSucceed(ATQA_answer_size)) { + rfidreader.getDataFromTag(atqa_answer); + } + + return atqa_answer[0] == expected_ATQA_answer[0] && atqa_answer[1] == expected_ATQA_answer[1]; +} + +inline auto receiveRegister(interface::RFIDReader &rfidreader) -> bool +{ + std::array register_answer {}; + + if (rfidreader.didTagCommunicationSucceed(register_answer_size)) { + rfidreader.getDataFromTag(register_answer); + } + + std::array received_crc = {register_answer[16], register_answer[17]}; + + return received_crc == computeCRC(register_answer.data()); +} + +namespace sm::event { + + struct tag_detected { + }; + +} // namespace sm::event + +namespace sm::state { + + inline auto idle = boost::sml::state; + inline auto send_reqa = boost::sml::state; + inline auto requesting_atqa = boost::sml::state; + inline auto identifying_tag = boost::sml::state; + inline auto validating_tag = boost::sml::state; + +} // namespace sm::state + +namespace sm::guard { + + using irfidreader = interface::RFIDReader; + + struct is_tag_detected { + auto operator()(irfidreader &rfidreader) const { return rfidreader.isTagDetected(); } + }; + + struct atqa_received { + auto operator()(irfidreader &rfidreader) const { return receiveAtqa(rfidreader); } + }; + + struct register_received { + auto operator()(irfidreader &rfidreader) const { return receiveRegister(rfidreader); } + }; + +} // namespace sm::guard + +namespace sm::action { + + using irfidreader = interface::RFIDReader; + + struct set_tag_detection_mode { + auto operator()(irfidreader &rfidreader) const { rfidreader.setTagDetectionMode(); } + }; + + struct set_communication_protocol { + auto operator()(irfidreader &rfidreader) const + { + rfidreader.setCommunicationProtocol(rfid::Protocol::ISO14443A); + } + }; + + struct send_request_A { + auto operator()(irfidreader &rfidreader) const { rfidreader.sendCommandToTag(command_requestA.getArray()); } + }; + + struct send_register_0 { + auto operator()(irfidreader &rfidreader) const + { + rfidreader.sendCommandToTag(command_read_register_0.getArray()); + } + }; + + struct send_register_4 { + auto operator()(irfidreader &rfidreader) const + { + rfidreader.sendCommandToTag(command_read_register_4.getArray()); + } + }; + + struct on_tag_valid { + auto operator()(irfidreader &rfidreader) const { rfidreader.onTagValid(); } + }; + +} // namespace sm::action + +struct ISO14443A { + auto operator()() const + { + using namespace boost::sml; + + return make_transition_table( + // clang-format off + * sm::state::idle + event [sm::guard::is_tag_detected {} ] / (sm::action::set_communication_protocol {}) = sm::state::send_reqa + , sm::state::idle + event [!sm::guard::is_tag_detected {}] / (sm::action::set_tag_detection_mode {}) = sm::state::idle + + , sm::state::send_reqa + event / (sm::action::send_request_A {}) = sm::state::requesting_atqa + + , sm::state::requesting_atqa + event [sm::guard::atqa_received {}] / (sm::action::send_register_0 {}) = sm::state::identifying_tag + , sm::state::requesting_atqa + event [!sm::guard::atqa_received {}] / (sm::action::set_tag_detection_mode {}) = sm::state::idle + + , sm::state::identifying_tag + event [sm::guard::register_received {}] / (sm::action::send_register_4 {}) = sm::state::validating_tag + , sm::state::identifying_tag + event [!sm::guard::register_received {}] / (sm::action::set_tag_detection_mode {}) = sm::state::idle + + , sm::state::validating_tag + event [sm::guard::register_received {}] / (sm::action::set_tag_detection_mode {}) = sm::state::idle + , sm::state::validating_tag + event [!sm::guard::register_received {}] / (sm::action::set_tag_detection_mode {}) = sm::state::idle + + , sm::state::validating_tag + boost::sml::on_exit<_> / ( sm::action::on_tag_valid {}) //TODO (@Hugo) Find a way to trigger set_tag_detection_mode before on_tag_valid instead of using it even if register4 isn't received + + // clang-format on + ); + } +}; + +} // namespace leka::rfid From 5f3c43327970b22be86140cc0bcfc8acd1d6ffcc Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Tue, 12 Jul 2022 14:22:09 +0200 Subject: [PATCH 015/130] :sparkles: (libs): Clean up & refactor RFIDKit --- libs/RFIDKit/include/RFIDKit.h | 61 +++++++------- libs/RFIDKit/source/RFIDKit.cpp | 136 -------------------------------- 2 files changed, 34 insertions(+), 163 deletions(-) diff --git a/libs/RFIDKit/include/RFIDKit.h b/libs/RFIDKit/include/RFIDKit.h index 6c5b21daf7..6f3c1f5c4f 100644 --- a/libs/RFIDKit/include/RFIDKit.h +++ b/libs/RFIDKit/include/RFIDKit.h @@ -4,53 +4,60 @@ #pragma once -#include -#include +#include +#include "ISO14443A.h" #include "MagicCard.h" #include "interface/drivers/RFIDReader.h" namespace leka { -enum class state : uint8_t -{ - - WAITING_FOR_TAG = 0x00, - TAG_COMMUNICATION_PROTOCOL_SET = 0x01, - WAIT_FOR_ATQA_RESPONSE = 0x02, - TAG_IDENTIFIED = 0x03, - TAG_AVAILABLE = 0x04, - -}; - -class RFIDKit : public interface::RFIDReader::ISO14443 +class RFIDKit { public: explicit RFIDKit(interface::RFIDReader &rfid_reader) : _rfid_reader(rfid_reader) {}; - void init() final; + void init() + { + using namespace rfid::sm; - void runStateMachine() final; + static auto tagDetectedCallback = [this]() { state_machine.process_event(event::tag_detected {}); }; - void onTagActivated(std::function callback); + _rfid_reader.registerOnTagDetectedCallback(tagDetectedCallback); + registerMagicCard(); - private: - void sendREQA(); - void sendReadRegister0(); - void sendReadRegister4(); + _rfid_reader.init(); + _rfid_reader.setTagDetectionMode(); + } + + void registerMagicCard() + { + auto on_magic_card_readable_callback = [this]() { + rfid::Tag tag = _rfid_reader.getTagData(); + if (isTagSignatureValid(tag)) { + _card = MagicCard {tag.data[5]}; - auto isTagSignatureValid() -> bool; + _on_tag_available_callback(_card); + } + }; + _rfid_reader.registerOnTagValidCallback(on_magic_card_readable_callback); + } - auto receiveATQA() -> bool; - auto receiveReadTagData() -> bool; + auto isTagSignatureValid(rfid::Tag tag) -> bool + { + return (tag.data[0] == leka_tag_header[0] && tag.data[1] == leka_tag_header[1] && + tag.data[2] == leka_tag_header[2] && tag.data[3] == leka_tag_header[3]); + } - auto computeCRC(uint8_t const *data) const -> std::array; + void onTagActivated(std::function callback) { _on_tag_available_callback = callback; } + private: interface::RFIDReader &_rfid_reader; - rfid::Tag _tag {}; MagicCard _card = MagicCard::none; std::function _on_tag_available_callback; - state _state = state::WAITING_FOR_TAG; + boost::sml::sm state_machine {_rfid_reader}; + + static constexpr std::array leka_tag_header = {0x4C, 0x45, 0x4B, 0x41}; }; } // namespace leka diff --git a/libs/RFIDKit/source/RFIDKit.cpp b/libs/RFIDKit/source/RFIDKit.cpp index a5d05ace1d..8a46958a57 100644 --- a/libs/RFIDKit/source/RFIDKit.cpp +++ b/libs/RFIDKit/source/RFIDKit.cpp @@ -3,139 +3,3 @@ // SPDX-License-Identifier: Apache-2.0 #include "RFIDKit.h" - -#include "LogKit.h" - -namespace leka { -void RFIDKit::init() -{ - static auto getTagDataCallback = [this]() { this->runStateMachine(); }; - - _rfid_reader.registerTagAvailableCallback(getTagDataCallback); - _rfid_reader.init(); - _rfid_reader.setTagDetectionMode(); -} - -void RFIDKit::runStateMachine() -{ - switch (_state) { - case state::WAITING_FOR_TAG: { - if (_rfid_reader.isTagDetected()) { - _rfid_reader.setCommunicationProtocol(rfid::Protocol::ISO14443A); - _state = state::TAG_COMMUNICATION_PROTOCOL_SET; - } else { - _rfid_reader.setTagDetectionMode(); - } - - } break; - - case state::TAG_COMMUNICATION_PROTOCOL_SET: { - sendREQA(); - _state = state::WAIT_FOR_ATQA_RESPONSE; - - } break; - - case state::WAIT_FOR_ATQA_RESPONSE: { - if (receiveATQA()) { - sendReadRegister0(); - _state = state::TAG_IDENTIFIED; - } else { - _rfid_reader.setTagDetectionMode(); - _state = state::WAITING_FOR_TAG; - } - } break; - - case state::TAG_IDENTIFIED: { - if (receiveReadTagData()) { - sendReadRegister4(); - _state = state::TAG_AVAILABLE; - } else { - _rfid_reader.setTagDetectionMode(); - _state = state::WAITING_FOR_TAG; - } - } break; - case state::TAG_AVAILABLE: { - if (receiveReadTagData() && isTagSignatureValid()) { - _card = MagicCard {_tag}; - log_info("Card available: 0x%04X", _card.getId()); - _on_tag_available_callback(_card); - } - _rfid_reader.setTagDetectionMode(); - _state = state::WAITING_FOR_TAG; - - } break; - default: { - break; - } - } -} - -void RFIDKit::onTagActivated(std::function callback) -{ - _on_tag_available_callback = callback; -} - -void RFIDKit::sendREQA() -{ - _rfid_reader.sendCommandToTag(command_requestA.getArray()); -} - -void RFIDKit::sendReadRegister0() -{ - _rfid_reader.sendCommandToTag(command_read_register_0.getArray()); -} - -void RFIDKit::sendReadRegister4() -{ - _rfid_reader.sendCommandToTag(command_read_register_4.getArray()); -} - -auto RFIDKit::isTagSignatureValid() -> bool -{ - return (_tag.data[0] == 0x4C && _tag.data[1] == 0x45 && _tag.data[2] == 0x4B && _tag.data[3] == 0x41); -} - -auto RFIDKit::receiveATQA() -> bool -{ - std::array ATQA_answer {}; - std::span span = {ATQA_answer}; - - _rfid_reader.receiveDataFromTag(span); - - return (span[0] == interface::RFIDReader::ISO14443::ATQA_answer[0] && - span[1] == interface::RFIDReader::ISO14443::ATQA_answer[1]); -} - -auto RFIDKit::receiveReadTagData() -> bool -{ - std::span span = {_tag_data}; - _rfid_reader.receiveDataFromTag(span); - - for (size_t i = 0; i < span.size(); ++i) { - _tag.data[i] = span.data()[i]; - } - - std::array received_crc = {span[16], span[17]}; - - return received_crc == computeCRC(span.data()); -} - -auto RFIDKit::computeCRC(uint8_t const *data) const -> std::array -{ - uint32_t wCrc = 0x6363; - size_t size = 16; - - do { - std::byte bt; - bt = static_cast(*data++); - bt = (bt ^ static_cast(wCrc & 0x00FF)); - bt = (bt ^ (bt << 4)); - wCrc = (wCrc >> 8) ^ (static_cast(bt) << 8) ^ (static_cast(bt) << 3) ^ - (static_cast(bt) >> 4); - } while (--size); - - std::array pbtCrc = {static_cast(wCrc & 0xFF), static_cast((wCrc >> 8) & 0xFF)}; - return pbtCrc; -} - -} // namespace leka From 1ca12af9292ca05d4a654ce5c99a5bc1114c848b Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 20 Jul 2022 11:58:10 +0200 Subject: [PATCH 016/130] :fire: (sm iso): Remove register0 request --- libs/RFIDKit/include/ISO14443A.h | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/libs/RFIDKit/include/ISO14443A.h b/libs/RFIDKit/include/ISO14443A.h index b404404612..fe44624ac6 100644 --- a/libs/RFIDKit/include/ISO14443A.h +++ b/libs/RFIDKit/include/ISO14443A.h @@ -29,8 +29,6 @@ struct Command { }; constexpr Command<1> command_requestA = {.data = {0x26}, .flags = leka::rfid::Flag::sb_7}; -constexpr Command<2> command_read_register_0 = {.data = {0x30, 0x00}, - .flags = leka::rfid::Flag::crc | leka::rfid::Flag::sb_8}; constexpr Command<2> command_read_register_4 = {.data = {0x30, 0x04}, .flags = leka::rfid::Flag::crc | leka::rfid::Flag::sb_8}; constexpr Command<2> command_read_register_6 = {.data = {0x30, 0x06}, @@ -91,11 +89,10 @@ namespace sm::event { namespace sm::state { - inline auto idle = boost::sml::state; - inline auto send_reqa = boost::sml::state; - inline auto requesting_atqa = boost::sml::state; - inline auto identifying_tag = boost::sml::state; - inline auto validating_tag = boost::sml::state; + inline auto idle = boost::sml::state; + inline auto send_reqa = boost::sml::state; + inline auto requesting_atqa = boost::sml::state; + inline auto requesting_tag_data = boost::sml::state; } // namespace sm::state @@ -136,12 +133,6 @@ namespace sm::action { auto operator()(irfidreader &rfidreader) const { rfidreader.sendCommandToTag(command_requestA.getArray()); } }; - struct send_register_0 { - auto operator()(irfidreader &rfidreader) const - { - rfidreader.sendCommandToTag(command_read_register_0.getArray()); - } - }; struct send_register_4 { auto operator()(irfidreader &rfidreader) const @@ -150,7 +141,7 @@ namespace sm::action { } }; - struct on_tag_valid { + struct on_tag_data_received { auto operator()(irfidreader &rfidreader) const { rfidreader.onTagValid(); } }; @@ -168,16 +159,13 @@ struct ISO14443A { , sm::state::send_reqa + event / (sm::action::send_request_A {}) = sm::state::requesting_atqa - , sm::state::requesting_atqa + event [sm::guard::atqa_received {}] / (sm::action::send_register_0 {}) = sm::state::identifying_tag + , sm::state::requesting_atqa + event [sm::guard::atqa_received {}] / (sm::action::send_register_4 {}) = sm::state::requesting_tag_data , sm::state::requesting_atqa + event [!sm::guard::atqa_received {}] / (sm::action::set_tag_detection_mode {}) = sm::state::idle - , sm::state::identifying_tag + event [sm::guard::register_received {}] / (sm::action::send_register_4 {}) = sm::state::validating_tag - , sm::state::identifying_tag + event [!sm::guard::register_received {}] / (sm::action::set_tag_detection_mode {}) = sm::state::idle - - , sm::state::validating_tag + event [sm::guard::register_received {}] / (sm::action::set_tag_detection_mode {}) = sm::state::idle - , sm::state::validating_tag + event [!sm::guard::register_received {}] / (sm::action::set_tag_detection_mode {}) = sm::state::idle + , sm::state::requesting_tag_data + event [sm::guard::register_received {}] / (sm::action::set_tag_detection_mode {}) = sm::state::idle + , sm::state::requesting_tag_data + event [!sm::guard::register_received {}] / (sm::action::set_tag_detection_mode {}) = sm::state::idle - , sm::state::validating_tag + boost::sml::on_exit<_> / ( sm::action::on_tag_valid {}) //TODO (@Hugo) Find a way to trigger set_tag_detection_mode before on_tag_valid instead of using it even if register4 isn't received + , sm::state::requesting_tag_data + boost::sml::on_exit<_> / ( sm::action::on_tag_data_received {}) //TODO (@Hugo) Find a way to trigger set_tag_detection_mode before on_tag_data_received instead of using it even if register4 isn't received // clang-format on ); From 993fee533be2291c4e0f7bf294b112a7e1dac0c5 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 20 Jul 2022 12:06:31 +0200 Subject: [PATCH 017/130] :art: (rfid): Fix code smells --- .../CoreRFIDReader/include/CoreRFIDReader.h | 28 ++++++------- .../CoreRFIDReader/source/CoreRFIDReader.cpp | 39 ++++++++----------- include/interface/drivers/RFIDReader.h | 27 ++++++------- libs/RFIDKit/include/ISO14443A.h | 16 +++----- libs/RFIDKit/include/RFIDKit.h | 15 ++++--- tests/unit/mocks/mocks/leka/CoreRFIDReader.h | 2 +- 6 files changed, 56 insertions(+), 71 deletions(-) diff --git a/drivers/CoreRFIDReader/include/CoreRFIDReader.h b/drivers/CoreRFIDReader/include/CoreRFIDReader.h index 3289fddaa1..547807f21d 100644 --- a/drivers/CoreRFIDReader/include/CoreRFIDReader.h +++ b/drivers/CoreRFIDReader/include/CoreRFIDReader.h @@ -174,35 +174,35 @@ class CoreRFIDReader : public interface::RFIDReader explicit CoreRFIDReader(interface::BufferedSerial &serial) : _serial(serial) {}; void init() final; - void setTagDetectionMode() final; + + void registerOnTagDetectedCallback(const std::function &callback) final; + void registerOnTagValidCallback(const std::function &callback) final; + auto isTagDetected() -> bool final; - void onTagValid() final; - auto getTagData() -> rfid::Tag final; - void getDataFromTag(std::span data) final; - auto didTagCommunicationSucceed(size_t sizeTagData) -> bool final; - void sendCommandToTag(std::span cmd) final; + void setTagDetectionMode() final; void setCommunicationProtocol(rfid::Protocol protocol) final; - void registerOnTagDetectedCallback(mbed::Callback callback) final; - void registerOnTagValidCallback(mbed::Callback callback) final; + void sendToTag(std::span data) final; + auto didTagCommunicationSucceed(size_t sizeTagData) -> bool final; + void getDataFromTag(std::span data) final; + auto getTag() -> rfid::Tag final; + void onTagDataReceived() final; private: void registerOnDataAvailableCallback(); void onDataAvailable(); - void setProtocolISO14443A(); - void setGainAndModulationISO14443A(); - void read(); - auto formatCommand(std::span cmd) -> size_t; + void setProtocolISO14443A(); + void setGainAndModulationISO14443A(); rfid::Tag _tag {}; size_t _anwser_size {0}; rtos::Thread _thread {}; events::EventQueue _event_queue {}; interface::BufferedSerial &_serial; - mbed::Callback _on_tag_detected; - mbed::Callback _on_tag_valid; + std::function _on_tag_detected; + std::function _on_tag_valid; std::array _tx_buf {}; std::array _rx_buf {}; diff --git a/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp b/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp index fac570f14e..fd1f2163a4 100644 --- a/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp +++ b/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp @@ -15,12 +15,12 @@ void CoreRFIDReader::init() registerOnDataAvailableCallback(); } -void CoreRFIDReader::registerOnTagDetectedCallback(mbed::Callback callback) +void CoreRFIDReader::registerOnTagDetectedCallback(const std::function &callback) { _on_tag_detected = callback; }; -void CoreRFIDReader::registerOnTagValidCallback(mbed::Callback callback) +void CoreRFIDReader::registerOnTagValidCallback(const std::function &callback) { _on_tag_valid = callback; }; @@ -63,6 +63,7 @@ void CoreRFIDReader::setTagDetectionMode() { _serial.write(rfid::command::frame::set_tag_detection_mode.data(), rfid::command::frame::set_tag_detection_mode.size()); + rtos::ThisThread::sleep_for(10ms); } void CoreRFIDReader::setCommunicationProtocol(rfid::Protocol protocol) @@ -85,23 +86,16 @@ void CoreRFIDReader::setGainAndModulationISO14443A() rfid::command::frame::set_gain_and_modulation.size()); } -void CoreRFIDReader::sendCommandToTag(std::span cmd) -{ - auto command_size = formatCommand(cmd); - - _serial.write(_tx_buf.data(), command_size); -} - -auto CoreRFIDReader::formatCommand(std::span cmd) -> size_t +void CoreRFIDReader::sendToTag(std::span data) { _tx_buf[0] = rfid::command::send_receive; - _tx_buf[1] = static_cast(cmd.size()); + _tx_buf[1] = static_cast(data.size()); - for (auto i = 0; i < cmd.size(); ++i) { - _tx_buf.at(i + rfid::tag_answer::heading_size) = cmd[i]; + for (auto i = 0; i < data.size(); ++i) { + _tx_buf.at(i + rfid::tag_answer::heading_size) = data[i]; } - return cmd.size() + rfid::tag_answer::heading_size; + _serial.write(_tx_buf.data(), data.size() + rfid::tag_answer::heading_size); } auto CoreRFIDReader::didTagCommunicationSucceed(size_t sizeTagData) -> bool @@ -109,10 +103,10 @@ auto CoreRFIDReader::didTagCommunicationSucceed(size_t sizeTagData) -> bool uint8_t status = _rx_buf[0]; uint8_t length = _rx_buf[1]; - auto didCommunicationSucceed = status == rfid::status::communication_succeed; - auto didCommandSameSize = sizeTagData == length - rfid::tag_answer::flag_size; + auto did_communication_succeed = status == rfid::status::communication_succeed; + auto did_command_same_size = sizeTagData == length - rfid::tag_answer::flag_size; - return (didCommunicationSucceed && didCommandSameSize); + return (did_communication_succeed && did_command_same_size); } void CoreRFIDReader::getDataFromTag(std::span data) @@ -122,16 +116,17 @@ void CoreRFIDReader::getDataFromTag(std::span data) } } -auto CoreRFIDReader::getTagData() -> rfid::Tag +auto CoreRFIDReader::getTag() -> rfid::Tag { - for (size_t i = 0; i < 18; ++i) { - _tag.data[i] = _rx_buf[i + rfid::tag_answer::heading_size]; - } return _tag; } -void CoreRFIDReader::onTagValid() +void CoreRFIDReader::onTagDataReceived() { + for (size_t i = 0; i < 18; ++i) { + _tag.data[i] = _rx_buf[i + rfid::tag_answer::heading_size]; + } + _on_tag_valid(); } diff --git a/include/interface/drivers/RFIDReader.h b/include/interface/drivers/RFIDReader.h index e87d8d1753..3abb3e3c14 100644 --- a/include/interface/drivers/RFIDReader.h +++ b/include/interface/drivers/RFIDReader.h @@ -8,11 +8,6 @@ #include #include -#include "platform/Callback.h" - -#include "BufferedSerial.h" -#include "boost/sml.hpp" - namespace leka { namespace rfid { @@ -53,17 +48,17 @@ namespace interface { public: virtual ~RFIDReader() = default; - virtual void init() = 0; - virtual void setTagDetectionMode() = 0; - virtual auto isTagDetected() -> bool = 0; - virtual void onTagValid() = 0; - virtual auto getTagData() -> rfid::Tag = 0; - virtual void getDataFromTag(std::span data) = 0; - virtual auto didTagCommunicationSucceed(size_t sizeTagData) -> bool = 0; - virtual void sendCommandToTag(std::span cmd) = 0; - virtual void setCommunicationProtocol(rfid::Protocol protocol) = 0; - virtual void registerOnTagDetectedCallback(mbed::Callback callback) = 0; - virtual void registerOnTagValidCallback(mbed::Callback callback) = 0; + virtual void init() = 0; + virtual void setTagDetectionMode() = 0; + virtual auto isTagDetected() -> bool = 0; + virtual void onTagDataReceived() = 0; + virtual auto getTag() -> rfid::Tag = 0; + virtual void getDataFromTag(std::span data) = 0; + virtual auto didTagCommunicationSucceed(size_t sizeTagData) -> bool = 0; + virtual void sendToTag(std::span data) = 0; + virtual void setCommunicationProtocol(rfid::Protocol protocol) = 0; + virtual void registerOnTagDetectedCallback(const std::function &callback) = 0; + virtual void registerOnTagValidCallback(const std::function &callback) = 0; }; } // namespace interface diff --git a/libs/RFIDKit/include/ISO14443A.h b/libs/RFIDKit/include/ISO14443A.h index fe44624ac6..34df37e415 100644 --- a/libs/RFIDKit/include/ISO14443A.h +++ b/libs/RFIDKit/include/ISO14443A.h @@ -4,6 +4,8 @@ #pragma once +#include + #include "boost/sml.hpp" #include "interface/drivers/RFIDReader.h" @@ -31,8 +33,6 @@ struct Command { constexpr Command<1> command_requestA = {.data = {0x26}, .flags = leka::rfid::Flag::sb_7}; constexpr Command<2> command_read_register_4 = {.data = {0x30, 0x04}, .flags = leka::rfid::Flag::crc | leka::rfid::Flag::sb_8}; -constexpr Command<2> command_read_register_6 = {.data = {0x30, 0x06}, - .flags = leka::rfid::Flag::crc | leka::rfid::Flag::sb_8}; constexpr size_t ATQA_answer_size = 2; constexpr size_t register_answer_size = 18; @@ -64,7 +64,7 @@ inline auto receiveAtqa(interface::RFIDReader &rfidreader) -> bool rfidreader.getDataFromTag(atqa_answer); } - return atqa_answer[0] == expected_ATQA_answer[0] && atqa_answer[1] == expected_ATQA_answer[1]; + return atqa_answer == expected_ATQA_answer; } inline auto receiveRegister(interface::RFIDReader &rfidreader) -> bool @@ -130,19 +130,15 @@ namespace sm::action { }; struct send_request_A { - auto operator()(irfidreader &rfidreader) const { rfidreader.sendCommandToTag(command_requestA.getArray()); } + auto operator()(irfidreader &rfidreader) const { rfidreader.sendToTag(command_requestA.getArray()); } }; - struct send_register_4 { - auto operator()(irfidreader &rfidreader) const - { - rfidreader.sendCommandToTag(command_read_register_4.getArray()); - } + auto operator()(irfidreader &rfidreader) const { rfidreader.sendToTag(command_read_register_4.getArray()); } }; struct on_tag_data_received { - auto operator()(irfidreader &rfidreader) const { rfidreader.onTagValid(); } + auto operator()(irfidreader &rfidreader) const { rfidreader.onTagDataReceived(); } }; } // namespace sm::action diff --git a/libs/RFIDKit/include/RFIDKit.h b/libs/RFIDKit/include/RFIDKit.h index 6f3c1f5c4f..9b488df289 100644 --- a/libs/RFIDKit/include/RFIDKit.h +++ b/libs/RFIDKit/include/RFIDKit.h @@ -4,8 +4,6 @@ #pragma once -#include - #include "ISO14443A.h" #include "MagicCard.h" #include "interface/drivers/RFIDReader.h" @@ -21,9 +19,9 @@ class RFIDKit { using namespace rfid::sm; - static auto tagDetectedCallback = [this]() { state_machine.process_event(event::tag_detected {}); }; + static auto tag_detected_callback = [this]() { state_machine.process_event(event::tag_detected {}); }; - _rfid_reader.registerOnTagDetectedCallback(tagDetectedCallback); + _rfid_reader.registerOnTagDetectedCallback(tag_detected_callback); registerMagicCard(); _rfid_reader.init(); @@ -33,11 +31,13 @@ class RFIDKit void registerMagicCard() { auto on_magic_card_readable_callback = [this]() { - rfid::Tag tag = _rfid_reader.getTagData(); + rfid::Tag tag = _rfid_reader.getTag(); if (isTagSignatureValid(tag)) { _card = MagicCard {tag.data[5]}; - _on_tag_available_callback(_card); + if (_on_tag_available_callback != nullptr) { + _on_tag_available_callback(_card); + } } }; _rfid_reader.registerOnTagValidCallback(on_magic_card_readable_callback); @@ -45,8 +45,7 @@ class RFIDKit auto isTagSignatureValid(rfid::Tag tag) -> bool { - return (tag.data[0] == leka_tag_header[0] && tag.data[1] == leka_tag_header[1] && - tag.data[2] == leka_tag_header[2] && tag.data[3] == leka_tag_header[3]); + return std::equal(std::begin(leka_tag_header), std::end(leka_tag_header), std::begin(tag.data)); } void onTagActivated(std::function callback) { _on_tag_available_callback = callback; } diff --git a/tests/unit/mocks/mocks/leka/CoreRFIDReader.h b/tests/unit/mocks/mocks/leka/CoreRFIDReader.h index 6723be17fb..33d22193a7 100644 --- a/tests/unit/mocks/mocks/leka/CoreRFIDReader.h +++ b/tests/unit/mocks/mocks/leka/CoreRFIDReader.h @@ -17,7 +17,7 @@ class CoreRFIDReader : public interface::RFIDReader MOCK_METHOD(void, onDataAvailable, (), (override)); MOCK_METHOD(bool, setBaudrate, (uint8_t), (override)); MOCK_METHOD(bool, setCommunicationProtocol, (rfid::Protocol), (override)); - MOCK_METHOD(void, sendCommandToTag, (std::span), (override)); + MOCK_METHOD(void, sendToTag, (std::span), (override)); MOCK_METHOD(bool, receiveDataFromTag, (std::span data), (override)); MOCK_METHOD(void, setModeTagDetection, (), (override)); MOCK_METHOD(bool, isTagDetected, (), (override)); From 0e85f6fc48a147090487b488ea759cf5e0b1a698 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 20 Jul 2022 12:26:03 +0200 Subject: [PATCH 018/130] :bug: (sm iso): Fix exit condition --- libs/RFIDKit/include/ISO14443A.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libs/RFIDKit/include/ISO14443A.h b/libs/RFIDKit/include/ISO14443A.h index 34df37e415..56190595d6 100644 --- a/libs/RFIDKit/include/ISO14443A.h +++ b/libs/RFIDKit/include/ISO14443A.h @@ -150,18 +150,18 @@ struct ISO14443A { return make_transition_table( // clang-format off - * sm::state::idle + event [sm::guard::is_tag_detected {} ] / (sm::action::set_communication_protocol {}) = sm::state::send_reqa - , sm::state::idle + event [!sm::guard::is_tag_detected {}] / (sm::action::set_tag_detection_mode {}) = sm::state::idle + * sm::state::idle + event [sm::guard::is_tag_detected {} ] / (sm::action::set_communication_protocol {}) = sm::state::send_reqa + , sm::state::idle + event [!sm::guard::is_tag_detected {}] / (sm::action::set_tag_detection_mode {}) = sm::state::idle - , sm::state::send_reqa + event / (sm::action::send_request_A {}) = sm::state::requesting_atqa + , sm::state::send_reqa + event / (sm::action::send_request_A {}) = sm::state::requesting_atqa - , sm::state::requesting_atqa + event [sm::guard::atqa_received {}] / (sm::action::send_register_4 {}) = sm::state::requesting_tag_data - , sm::state::requesting_atqa + event [!sm::guard::atqa_received {}] / (sm::action::set_tag_detection_mode {}) = sm::state::idle + , sm::state::requesting_atqa + event [sm::guard::atqa_received {}] / (sm::action::send_register_4 {}) = sm::state::requesting_tag_data + , sm::state::requesting_atqa + event [!sm::guard::atqa_received {}] / (sm::action::set_tag_detection_mode {}) = sm::state::idle - , sm::state::requesting_tag_data + event [sm::guard::register_received {}] / (sm::action::set_tag_detection_mode {}) = sm::state::idle - , sm::state::requesting_tag_data + event [!sm::guard::register_received {}] / (sm::action::set_tag_detection_mode {}) = sm::state::idle + , sm::state::requesting_tag_data + event [sm::guard::register_received {}] / (sm::action::on_tag_data_received {}) = sm::state::idle + , sm::state::requesting_tag_data + event [!sm::guard::register_received {}] = sm::state::idle - , sm::state::requesting_tag_data + boost::sml::on_exit<_> / ( sm::action::on_tag_data_received {}) //TODO (@Hugo) Find a way to trigger set_tag_detection_mode before on_tag_data_received instead of using it even if register4 isn't received + , sm::state::requesting_tag_data + boost::sml::on_exit<_> / ( sm::action::set_tag_detection_mode {}) // clang-format on ); From d82fda18e4da5eb23b36539415bba3ee119f19d7 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 20 Jul 2022 12:50:08 +0200 Subject: [PATCH 019/130] :art: (libs): Stop using RFIDKit.h as a template --- libs/RFIDKit/include/RFIDKit.h | 38 ++++------------------------- libs/RFIDKit/source/RFIDKit.cpp | 42 +++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/libs/RFIDKit/include/RFIDKit.h b/libs/RFIDKit/include/RFIDKit.h index 9b488df289..f839f2c290 100644 --- a/libs/RFIDKit/include/RFIDKit.h +++ b/libs/RFIDKit/include/RFIDKit.h @@ -15,40 +15,10 @@ class RFIDKit public: explicit RFIDKit(interface::RFIDReader &rfid_reader) : _rfid_reader(rfid_reader) {}; - void init() - { - using namespace rfid::sm; - - static auto tag_detected_callback = [this]() { state_machine.process_event(event::tag_detected {}); }; - - _rfid_reader.registerOnTagDetectedCallback(tag_detected_callback); - registerMagicCard(); - - _rfid_reader.init(); - _rfid_reader.setTagDetectionMode(); - } - - void registerMagicCard() - { - auto on_magic_card_readable_callback = [this]() { - rfid::Tag tag = _rfid_reader.getTag(); - if (isTagSignatureValid(tag)) { - _card = MagicCard {tag.data[5]}; - - if (_on_tag_available_callback != nullptr) { - _on_tag_available_callback(_card); - } - } - }; - _rfid_reader.registerOnTagValidCallback(on_magic_card_readable_callback); - } - - auto isTagSignatureValid(rfid::Tag tag) -> bool - { - return std::equal(std::begin(leka_tag_header), std::end(leka_tag_header), std::begin(tag.data)); - } - - void onTagActivated(std::function callback) { _on_tag_available_callback = callback; } + void init(); + void registerMagicCard(); + auto isTagSignatureValid(rfid::Tag tag) -> bool; + void onTagActivated(std::function callback); private: interface::RFIDReader &_rfid_reader; diff --git a/libs/RFIDKit/source/RFIDKit.cpp b/libs/RFIDKit/source/RFIDKit.cpp index 8a46958a57..f436577425 100644 --- a/libs/RFIDKit/source/RFIDKit.cpp +++ b/libs/RFIDKit/source/RFIDKit.cpp @@ -3,3 +3,45 @@ // SPDX-License-Identifier: Apache-2.0 #include "RFIDKit.h" + +namespace leka { + +void RFIDKit::init() +{ + using namespace rfid::sm; + + static auto tag_detected_callback = [this]() { state_machine.process_event(event::tag_detected {}); }; + + _rfid_reader.registerOnTagDetectedCallback(tag_detected_callback); + registerMagicCard(); + + _rfid_reader.init(); + _rfid_reader.setTagDetectionMode(); +} + +void RFIDKit::registerMagicCard() +{ + auto on_magic_card_readable_callback = [this]() { + rfid::Tag tag = _rfid_reader.getTag(); + if (isTagSignatureValid(tag)) { + _card = MagicCard {tag.data[5]}; + + if (_on_tag_available_callback != nullptr) { + _on_tag_available_callback(_card); + } + } + }; + _rfid_reader.registerOnTagValidCallback(on_magic_card_readable_callback); +} + +auto RFIDKit::isTagSignatureValid(rfid::Tag tag) -> bool +{ + return std::equal(std::begin(leka_tag_header), std::end(leka_tag_header), std::begin(tag.data)); +} + +void RFIDKit::onTagActivated(std::function callback) +{ + _on_tag_available_callback = callback; +} + +} // namespace leka From 25bb5fd882b32b80c3449238d6bfd20f08e0b48a Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 20 Jul 2022 12:56:36 +0200 Subject: [PATCH 020/130] :art: (libs): Please SonarCloud and resolve screen bug --- libs/RFIDKit/include/RFIDKit.h | 4 ++-- libs/RFIDKit/source/RFIDKit.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/RFIDKit/include/RFIDKit.h b/libs/RFIDKit/include/RFIDKit.h index f839f2c290..49b37bf3a5 100644 --- a/libs/RFIDKit/include/RFIDKit.h +++ b/libs/RFIDKit/include/RFIDKit.h @@ -17,8 +17,8 @@ class RFIDKit void init(); void registerMagicCard(); - auto isTagSignatureValid(rfid::Tag tag) -> bool; - void onTagActivated(std::function callback); + [[nodiscard]] auto isTagSignatureValid(rfid::Tag tag) const -> bool; + void onTagActivated(std::function const &callback); private: interface::RFIDReader &_rfid_reader; diff --git a/libs/RFIDKit/source/RFIDKit.cpp b/libs/RFIDKit/source/RFIDKit.cpp index f436577425..5a12e0880f 100644 --- a/libs/RFIDKit/source/RFIDKit.cpp +++ b/libs/RFIDKit/source/RFIDKit.cpp @@ -34,12 +34,12 @@ void RFIDKit::registerMagicCard() _rfid_reader.registerOnTagValidCallback(on_magic_card_readable_callback); } -auto RFIDKit::isTagSignatureValid(rfid::Tag tag) -> bool +auto RFIDKit::isTagSignatureValid(rfid::Tag tag) const -> bool { return std::equal(std::begin(leka_tag_header), std::end(leka_tag_header), std::begin(tag.data)); } -void RFIDKit::onTagActivated(std::function callback) +void RFIDKit::onTagActivated(std::function const &callback) { _on_tag_available_callback = callback; } From 7ed39484475ab12f034913594333ae40847e66d5 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Thu, 21 Jul 2022 11:46:38 +0200 Subject: [PATCH 021/130] :truck: (rfid): Rename tagValid to tagReadable --- drivers/CoreRFIDReader/include/CoreRFIDReader.h | 2 +- drivers/CoreRFIDReader/source/CoreRFIDReader.cpp | 2 +- include/interface/drivers/RFIDReader.h | 2 +- libs/RFIDKit/source/RFIDKit.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/CoreRFIDReader/include/CoreRFIDReader.h b/drivers/CoreRFIDReader/include/CoreRFIDReader.h index 547807f21d..7d72359063 100644 --- a/drivers/CoreRFIDReader/include/CoreRFIDReader.h +++ b/drivers/CoreRFIDReader/include/CoreRFIDReader.h @@ -176,7 +176,7 @@ class CoreRFIDReader : public interface::RFIDReader void init() final; void registerOnTagDetectedCallback(const std::function &callback) final; - void registerOnTagValidCallback(const std::function &callback) final; + void registerOnTagReadableCallback(const std::function &callback) final; auto isTagDetected() -> bool final; void setTagDetectionMode() final; diff --git a/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp b/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp index fd1f2163a4..6d1d586c9a 100644 --- a/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp +++ b/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp @@ -20,7 +20,7 @@ void CoreRFIDReader::registerOnTagDetectedCallback(const std::function & _on_tag_detected = callback; }; -void CoreRFIDReader::registerOnTagValidCallback(const std::function &callback) +void CoreRFIDReader::registerOnTagReadableCallback(const std::function &callback) { _on_tag_valid = callback; }; diff --git a/include/interface/drivers/RFIDReader.h b/include/interface/drivers/RFIDReader.h index 3abb3e3c14..21d9e42f0a 100644 --- a/include/interface/drivers/RFIDReader.h +++ b/include/interface/drivers/RFIDReader.h @@ -58,7 +58,7 @@ namespace interface { virtual void sendToTag(std::span data) = 0; virtual void setCommunicationProtocol(rfid::Protocol protocol) = 0; virtual void registerOnTagDetectedCallback(const std::function &callback) = 0; - virtual void registerOnTagValidCallback(const std::function &callback) = 0; + virtual void registerOnTagReadableCallback(const std::function &callback) = 0; }; } // namespace interface diff --git a/libs/RFIDKit/source/RFIDKit.cpp b/libs/RFIDKit/source/RFIDKit.cpp index 5a12e0880f..eddf70a6e5 100644 --- a/libs/RFIDKit/source/RFIDKit.cpp +++ b/libs/RFIDKit/source/RFIDKit.cpp @@ -31,7 +31,7 @@ void RFIDKit::registerMagicCard() } } }; - _rfid_reader.registerOnTagValidCallback(on_magic_card_readable_callback); + _rfid_reader.registerOnTagReadableCallback(on_magic_card_readable_callback); } auto RFIDKit::isTagSignatureValid(rfid::Tag tag) const -> bool From 57b0cb996a4fce197045fcd290335cbd2e85dd11 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Thu, 21 Jul 2022 14:13:30 +0200 Subject: [PATCH 022/130] :art: (os) Remove lambda expression in OnTagActivated --- app/os/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/os/main.cpp b/app/os/main.cpp index e785d91483..664ab89195 100644 --- a/app/os/main.cpp +++ b/app/os/main.cpp @@ -488,7 +488,7 @@ auto main() -> int robot::controller.registerOnFactoryResetNotificationCallback(factory_reset::set); robot::controller.registerEvents(); - rfidkit.onTagActivated([](const MagicCard &card) { robot::emergencyStop(card); }); + rfidkit.onTagActivated(robot::emergencyStop); // TODO(@team): Add functional test prior confirming the firmware firmware::confirmFirmware(); From ecc81cf13930597afe4d35f6361a3b0f290955a7 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Fri, 22 Jul 2022 14:14:56 +0200 Subject: [PATCH 023/130] :art: (drivers) Fix minor bugs/wrong names --- drivers/CoreRFIDReader/include/CoreRFIDReader.h | 4 ++-- drivers/CoreRFIDReader/source/CoreRFIDReader.cpp | 11 ++++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/CoreRFIDReader/include/CoreRFIDReader.h b/drivers/CoreRFIDReader/include/CoreRFIDReader.h index 7d72359063..c8505744d4 100644 --- a/drivers/CoreRFIDReader/include/CoreRFIDReader.h +++ b/drivers/CoreRFIDReader/include/CoreRFIDReader.h @@ -186,10 +186,10 @@ class CoreRFIDReader : public interface::RFIDReader void getDataFromTag(std::span data) final; auto getTag() -> rfid::Tag final; void onTagDataReceived() final; + void onDataAvailable(); private: void registerOnDataAvailableCallback(); - void onDataAvailable(); void read(); @@ -197,7 +197,7 @@ class CoreRFIDReader : public interface::RFIDReader void setGainAndModulationISO14443A(); rfid::Tag _tag {}; - size_t _anwser_size {0}; + size_t _answer_size {0}; rtos::Thread _thread {}; events::EventQueue _event_queue {}; interface::BufferedSerial &_serial; diff --git a/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp b/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp index 6d1d586c9a..d6c6b175ae 100644 --- a/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp +++ b/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp @@ -42,7 +42,7 @@ void CoreRFIDReader::read() { rtos::ThisThread::sleep_for(10ms); if (_serial.readable()) { - _anwser_size = _serial.read(_rx_buf.data(), _rx_buf.size()); + _answer_size = _serial.read(_rx_buf.data(), _rx_buf.size()); } } @@ -50,7 +50,7 @@ auto CoreRFIDReader::isTagDetected() -> bool { std::array buffer {}; - if (_anwser_size != rfid::expected_answer_size::tag_detection) { + if (_answer_size != rfid::expected_answer_size::tag_detection) { return false; } @@ -112,7 +112,8 @@ auto CoreRFIDReader::didTagCommunicationSucceed(size_t sizeTagData) -> bool void CoreRFIDReader::getDataFromTag(std::span data) { for (auto i = 0; i < data.size(); ++i) { - data[i] = _rx_buf[i + rfid::tag_answer::heading_size]; + data[i] = _rx_buf[i + rfid::tag_answer::heading_size]; + _tag.data[i] = data[i]; } } @@ -123,10 +124,6 @@ auto CoreRFIDReader::getTag() -> rfid::Tag void CoreRFIDReader::onTagDataReceived() { - for (size_t i = 0; i < 18; ++i) { - _tag.data[i] = _rx_buf[i + rfid::tag_answer::heading_size]; - } - _on_tag_valid(); } From bed1eef0b10ad41ab4fde7db5e7cc633542bcfea Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Fri, 22 Jul 2022 14:15:45 +0200 Subject: [PATCH 024/130] :clown_face: (mock): Adapt RFIDReader mock --- tests/unit/mocks/mocks/leka/CoreRFIDReader.h | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/unit/mocks/mocks/leka/CoreRFIDReader.h b/tests/unit/mocks/mocks/leka/CoreRFIDReader.h index 33d22193a7..97637a3455 100644 --- a/tests/unit/mocks/mocks/leka/CoreRFIDReader.h +++ b/tests/unit/mocks/mocks/leka/CoreRFIDReader.h @@ -13,14 +13,16 @@ class CoreRFIDReader : public interface::RFIDReader { public: MOCK_METHOD(void, init, (), (override)); - MOCK_METHOD(void, registerTagAvailableCallback, (tagAvailableCallback), (override)); - MOCK_METHOD(void, onDataAvailable, (), (override)); - MOCK_METHOD(bool, setBaudrate, (uint8_t), (override)); - MOCK_METHOD(bool, setCommunicationProtocol, (rfid::Protocol), (override)); - MOCK_METHOD(void, sendToTag, (std::span), (override)); - MOCK_METHOD(bool, receiveDataFromTag, (std::span data), (override)); - MOCK_METHOD(void, setModeTagDetection, (), (override)); MOCK_METHOD(bool, isTagDetected, (), (override)); + MOCK_METHOD(void, setTagDetectionMode, (), (override)); + MOCK_METHOD(void, setCommunicationProtocol, (rfid::Protocol), (override)); + MOCK_METHOD(void, sendToTag, (std::span), (override)); + MOCK_METHOD(bool, didTagCommunicationSucceed, (size_t), (override)); + MOCK_METHOD(void, getDataFromTag, (std::span data), (override)); + MOCK_METHOD(rfid::Tag, getTag, (), (override)); + MOCK_METHOD(void, onTagDataReceived, (), (override)); + MOCK_METHOD(void, registerOnTagDetectedCallback, (const std::function &), (override)); + MOCK_METHOD(void, registerOnTagReadableCallback, (const std::function &), (override)); }; } // namespace leka::mock From 90bb9430582a92293f53302a7689071cc35a01a3 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Fri, 22 Jul 2022 14:16:36 +0200 Subject: [PATCH 025/130] :white_check_mark: (tests): Add CoreRFIDReader test file --- drivers/CoreRFIDReader/CMakeLists.txt | 6 + .../tests/CoreRFIDReader_test.cpp | 304 ++++++++++++++++++ 2 files changed, 310 insertions(+) create mode 100644 drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp diff --git a/drivers/CoreRFIDReader/CMakeLists.txt b/drivers/CoreRFIDReader/CMakeLists.txt index e2ac91eaad..37ef3877bb 100644 --- a/drivers/CoreRFIDReader/CMakeLists.txt +++ b/drivers/CoreRFIDReader/CMakeLists.txt @@ -18,3 +18,9 @@ target_link_libraries(CoreRFIDReader mbed-os CoreBufferedSerial ) + +if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") + leka_unit_tests_sources( + tests/CoreRFIDReader_test.cpp + ) +endif() diff --git a/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp b/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp new file mode 100644 index 0000000000..e334c2f8e0 --- /dev/null +++ b/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp @@ -0,0 +1,304 @@ +// Leka - LekaOS +// Copyright 2021 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include "CoreRFIDReader.h" +#include "gtest/gtest.h" +#include "mocks/leka/CoreBufferedSerial.h" + +using namespace leka; +using namespace interface; + +using ::testing::Args; +using ::testing::DoAll; +using ::testing::ElementsAre; +using ::testing::InSequence; +using ::testing::Return; +using ::testing::SetArrayArgument; + +class CoreRFIDReaderTest : public ::testing::Test +{ + protected: + CoreRFIDReaderTest() : reader(mockBufferedSerial) {}; + + // void SetUp() override {} + // void TearDown() override {} + + mock::CoreBufferedSerial mockBufferedSerial; + CoreRFIDReader reader; + + bool _callbackCalled {false}; + + static void func() {}; + std::function callbackFunction = func; + + void isCallbackCalled() { _callbackCalled = true; }; + + void setFakeCallback() + { + _callbackCalled = false; + auto callback = [this]() { this->isCallbackCalled(); }; + reader.registerOnTagDetectedCallback(callback); + } + + void sendSetModeTagDetection() + { + const auto expected_values = ElementsAre( + rfid::command::frame::set_tag_detection_mode[0], rfid::command::frame::set_tag_detection_mode[1], + rfid::command::frame::set_tag_detection_mode[2], rfid::command::frame::set_tag_detection_mode[3], + rfid::command::frame::set_tag_detection_mode[4], rfid::command::frame::set_tag_detection_mode[5], + rfid::command::frame::set_tag_detection_mode[6], rfid::command::frame::set_tag_detection_mode[7], + rfid::command::frame::set_tag_detection_mode[8], rfid::command::frame::set_tag_detection_mode[9], + rfid::command::frame::set_tag_detection_mode[10], rfid::command::frame::set_tag_detection_mode[11], + rfid::command::frame::set_tag_detection_mode[12], rfid::command::frame::set_tag_detection_mode[13], + rfid::command::frame::set_tag_detection_mode[14], rfid::command::frame::set_tag_detection_mode[15]); + + EXPECT_CALL(mockBufferedSerial, write).With(Args<0, 1>(expected_values)); + } + + void sendSetProtocol() + { + const auto expected_values = ElementsAre(rfid::command::set_protocol::id, rfid::command::set_protocol::length, + rfid::protocol::iso14443A.id, rfid::settings::default_rx_tx_speed); + EXPECT_CALL(mockBufferedSerial, write).With(Args<0, 1>(expected_values)); + } + + void sendSetGainAndModulation() + { + const auto expected_values = ElementsAre( + rfid::command::set_gain_and_modulation::id, rfid::command::set_gain_and_modulation::length, + rfid::settings::arc_b, rfid::settings::flag_increment, rfid::settings::acr_b_index_for_gain_and_modulation, + rfid::protocol::iso14443A.gain_modulation_values()); + EXPECT_CALL(mockBufferedSerial, write).With(Args<0, 1>(expected_values)); + } + + template + void receiveRFIDReaderAnswer(const std::array &returned_values) + { + EXPECT_CALL(mockBufferedSerial, readable).WillOnce(Return(true)); + EXPECT_CALL(mockBufferedSerial, read) + .WillOnce(DoAll(SetArrayArgument<0>(begin(returned_values), begin(returned_values) + size), Return(size))); + } +}; + +TEST_F(CoreRFIDReaderTest, initialization) +{ + EXPECT_NE(&reader, nullptr); +} + +TEST_F(CoreRFIDReaderTest, enableTagDetection) +{ + EXPECT_CALL(mockBufferedSerial, sigio).Times(1); + + reader.init(); +} + +TEST_F(CoreRFIDReaderTest, registerCallbacks) +{ + reader.registerOnTagDetectedCallback(callbackFunction); + reader.registerOnTagReadableCallback(callbackFunction); +} + +TEST_F(CoreRFIDReaderTest, setTagDetectionMode) +{ + sendSetModeTagDetection(); + + reader.setTagDetectionMode(); +} + +TEST_F(CoreRFIDReaderTest, onDataAvailableSuccess) +{ + setFakeCallback(); + + std::array expected_tag_detection_callback = {0x00, 0x01, 0x02}; + + reader.registerOnTagDetectedCallback(callbackFunction); + + receiveRFIDReaderAnswer(expected_tag_detection_callback); + + reader.onDataAvailable(); + + auto is_tag_detected = reader.isTagDetected(); + + EXPECT_TRUE(is_tag_detected); +} + +TEST_F(CoreRFIDReaderTest, onDataAvailableWrongCallback) +{ + setFakeCallback(); + + std::array expected_tag_detection_callback = {0x00, 0x01, 0xff}; + + reader.registerOnTagDetectedCallback(callbackFunction); + + receiveRFIDReaderAnswer(expected_tag_detection_callback); + + reader.onDataAvailable(); + + auto is_tag_detected = reader.isTagDetected(); + + EXPECT_FALSE(is_tag_detected); +} + +TEST_F(CoreRFIDReaderTest, onDataAvailableTooLongCallback) +{ + setFakeCallback(); + + std::array expected_tag_detection_callback = {0x00, 0x01, 0x03, 0x04}; + + reader.registerOnTagDetectedCallback(callbackFunction); + + receiveRFIDReaderAnswer(expected_tag_detection_callback); + + reader.onDataAvailable(); + + auto is_tag_detected = reader.isTagDetected(); + + EXPECT_FALSE(is_tag_detected); +} + +TEST_F(CoreRFIDReaderTest, setCommunicationProtocolSuccess) +{ + setFakeCallback(); + + std::array set_protocol_success_answer = {0x00, 0x00}; + std::array set_gain_and_modulation_success_answer = {0x00, 0x00}; + + { + InSequence seq; + receiveRFIDReaderAnswer(set_protocol_success_answer); + sendSetProtocol(); + sendSetGainAndModulation(); + } + + reader.onDataAvailable(); + reader.setCommunicationProtocol(rfid::Protocol::ISO14443A); +} + +TEST_F(CoreRFIDReaderTest, setCommunicationProtocolFailedOnAnswerTooBig) +{ + setFakeCallback(); + std::array set_protocol_failed_answer = {0x00, 0x00, 0x00}; + { + InSequence seq; + receiveRFIDReaderAnswer(set_protocol_failed_answer); + sendSetProtocol(); + sendSetGainAndModulation(); + } + + reader.onDataAvailable(); + reader.setCommunicationProtocol(rfid::Protocol::ISO14443A); +} + +TEST_F(CoreRFIDReaderTest, setCommunicationProtocolFailedOnWrongFirstValue) +{ + setFakeCallback(); + std::array set_protocol_failed_answer = {0x82, 0x00}; + { + InSequence seq; + receiveRFIDReaderAnswer(set_protocol_failed_answer); + sendSetProtocol(); + sendSetGainAndModulation(); + } + + reader.onDataAvailable(); + reader.setCommunicationProtocol(rfid::Protocol::ISO14443A); +} + +TEST_F(CoreRFIDReaderTest, sendCommandSuccess) +{ + std::array command = {0x26, 0x07}; + const auto expected_values = ElementsAre(rfid::command::send_receive, command.size(), command[0], command[1]); + + EXPECT_CALL(mockBufferedSerial, write).With(Args<0, 1>(expected_values)); + + reader.sendToTag(command); +} + +TEST_F(CoreRFIDReaderTest, receiveDataSuccess) +{ + setFakeCallback(); + + std::array read_values = {0x80, 0x15, 0x4C, 0x45, 0x4B, 0x41, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x6C, 0x28, 0x00, 0x00}; + + std::array expected_values = {0x4C, 0x45, 0x4B, 0x41, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x6C}; + + std::array actual_values {0}; + + receiveRFIDReaderAnswer(read_values); + + reader.onDataAvailable(); + reader.getDataFromTag(actual_values); + + bool is_communication_succeed = reader.didTagCommunicationSucceed(actual_values.size()); + + EXPECT_EQ(is_communication_succeed, true); + EXPECT_EQ(actual_values, expected_values); +} + +TEST_F(CoreRFIDReaderTest, receiveDataFailedWrongAnswerFlag) +{ + setFakeCallback(); + + std::array read_values = {0xff, 0x05, 0x44, 0x00, 0x28, 0x00, 0x00}; + std::array actual_values {0}; + + receiveRFIDReaderAnswer(read_values); + + reader.onDataAvailable(); + + reader.getDataFromTag(actual_values); + bool is_communication_succeed = reader.didTagCommunicationSucceed(read_values.size()); + + EXPECT_EQ(is_communication_succeed, false); + EXPECT_NE(actual_values, read_values); +} + +TEST_F(CoreRFIDReaderTest, receiveDataFailedWrongLength) +{ + setFakeCallback(); + + std::array read_values = {0x80, 0x02, 0x44, 0x00, 0x28, 0x00, 0x00}; + std::array actual_values {0}; + + receiveRFIDReaderAnswer(read_values); + + reader.onDataAvailable(); + + reader.getDataFromTag(actual_values); + + bool is_communication_succeed = reader.didTagCommunicationSucceed(read_values.size()); + + EXPECT_EQ(is_communication_succeed, false); + EXPECT_NE(actual_values, read_values); +} + +TEST_F(CoreRFIDReaderTest, getTag) +{ + setFakeCallback(); + reader.registerOnTagReadableCallback(callbackFunction); + + std::array read_values = {0x80, 0x15, 0x4C, 0x45, 0x4B, 0x41, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x6C, 0x28, 0x00, 0x00}; + + std::array expected_values = {0x4C, 0x45, 0x4B, 0x41, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + std::array actual_values {0}; + + receiveRFIDReaderAnswer(read_values); + + reader.onDataAvailable(); + reader.getDataFromTag(actual_values); + + reader.onTagDataReceived(); + + auto tag = reader.getTag(); + + EXPECT_EQ(tag.data, expected_values); +} From 38ef807d60d2e8f7b7ced75090cfe8f04570da98 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Fri, 22 Jul 2022 14:17:18 +0200 Subject: [PATCH 026/130] :white_check_mark: (tests): Add RFIDKit and ISO tests --- libs/RFIDKit/CMakeLists.txt | 7 ++ libs/RFIDKit/tests/ISO14443A_test.cpp | 0 libs/RFIDKit/tests/RFIDKit_test.cpp | 110 ++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 libs/RFIDKit/tests/ISO14443A_test.cpp create mode 100644 libs/RFIDKit/tests/RFIDKit_test.cpp diff --git a/libs/RFIDKit/CMakeLists.txt b/libs/RFIDKit/CMakeLists.txt index 7a6c58aa9e..48e0b70cd5 100644 --- a/libs/RFIDKit/CMakeLists.txt +++ b/libs/RFIDKit/CMakeLists.txt @@ -18,3 +18,10 @@ target_link_libraries(RFIDKit mbed-os CoreRFIDReader ) + +if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") + leka_unit_tests_sources( + tests/ISO14443A_test.cpp + tests/RFIDKit_test.cpp + ) +endif() diff --git a/libs/RFIDKit/tests/ISO14443A_test.cpp b/libs/RFIDKit/tests/ISO14443A_test.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libs/RFIDKit/tests/RFIDKit_test.cpp b/libs/RFIDKit/tests/RFIDKit_test.cpp new file mode 100644 index 0000000000..b45c6d5b56 --- /dev/null +++ b/libs/RFIDKit/tests/RFIDKit_test.cpp @@ -0,0 +1,110 @@ +// Leka - LekaOS +// Copyright 2021 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "RFIDKit.h" + +#include "gtest/gtest.h" +#include "mocks/leka/CoreRFIDReader.h" + +using namespace leka; + +using ::testing::Return; +using ::testing::SaveArg; + +class RFIDKitTest : public ::testing::Test +{ + protected: + RFIDKitTest() : rfid_kit(mock_reader) {}; + + // void SetUp() override {} + // void TearDown() override {} + + RFIDKit rfid_kit; + mock::CoreRFIDReader mock_reader {}; + const std::function callback; + + std::function magic_card_callback {}; +}; + +TEST_F(RFIDKitTest, initialization) +{ + ASSERT_NE(&rfid_kit, nullptr); +} + +TEST_F(RFIDKitTest, init) +{ + EXPECT_CALL(mock_reader, registerOnTagDetectedCallback).Times(1); + EXPECT_CALL(mock_reader, registerOnTagReadableCallback).Times(1); + EXPECT_CALL(mock_reader, init).Times(1); + EXPECT_CALL(mock_reader, setTagDetectionMode).Times(1); + + rfid_kit.init(); +} + +TEST_F(RFIDKitTest, registerMagicCardCallbackTagValidCallbackSet) +{ + std::array id {}; + std::array sak {}; + std::array data {0x4C, 0x45, 0x4B, 0x41, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + rfid::Tag tag_valid {id, sak, data}; + + rfid_kit.onTagActivated(callback); + + EXPECT_CALL(mock_reader, registerOnTagReadableCallback).WillOnce(SaveArg<0>(&magic_card_callback)); + + rfid_kit.registerMagicCard(); + + EXPECT_CALL(mock_reader, getTag).WillOnce(Return(tag_valid)); + + magic_card_callback(); +} + +TEST_F(RFIDKitTest, registerMagicCardCallbackTagValidCallbackNotSet) +{ + std::array id {}; + std::array sak {}; + std::array data {0x4C, 0x45, 0x4B, 0x41, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + rfid::Tag tag_valid {id, sak, data}; + + EXPECT_CALL(mock_reader, registerOnTagReadableCallback).WillOnce(SaveArg<0>(&magic_card_callback)); + + rfid_kit.registerMagicCard(); + + EXPECT_CALL(mock_reader, getTag).WillOnce(Return(tag_valid)); + + magic_card_callback(); +} + +TEST_F(RFIDKitTest, TagSignatureIsValid) +{ + std::array id {}; + std::array sak {}; + std::array data {0x4C, 0x45, 0x4B, 0x41, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + rfid::Tag tag_valid {id, sak, data}; + + auto is_tag_valid = rfid_kit.isTagSignatureValid(tag_valid); + + EXPECT_TRUE(is_tag_valid); +} + +TEST_F(RFIDKitTest, TagSignatureIsNotValid) +{ + std::array id {}; + std::array sak {}; + std::array data {0x00, 0x45, 0x00, 0x41, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + rfid::Tag tag_valid {id, sak, data}; + + auto is_tag_valid = rfid_kit.isTagSignatureValid(tag_valid); + + EXPECT_FALSE(is_tag_valid); +} + +TEST_F(RFIDKitTest, onTagActivated) +{ + rfid_kit.onTagActivated(callback); +} From 00a7b4e96c639a69279a672070f349e6345ab381 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Tue, 26 Jul 2022 15:57:50 +0200 Subject: [PATCH 027/130] :art: (RFID): Change getDataFromTag implementation --- .../CoreRFIDReader/include/CoreRFIDReader.h | 2 +- .../CoreRFIDReader/source/CoreRFIDReader.cpp | 7 +++-- .../tests/CoreRFIDReader_test.cpp | 29 ++++++++----------- include/interface/drivers/RFIDReader.h | 2 +- libs/RFIDKit/include/ISO14443A.h | 16 +++++----- tests/unit/mocks/mocks/leka/CoreRFIDReader.h | 2 +- 6 files changed, 28 insertions(+), 30 deletions(-) diff --git a/drivers/CoreRFIDReader/include/CoreRFIDReader.h b/drivers/CoreRFIDReader/include/CoreRFIDReader.h index c8505744d4..c544e31889 100644 --- a/drivers/CoreRFIDReader/include/CoreRFIDReader.h +++ b/drivers/CoreRFIDReader/include/CoreRFIDReader.h @@ -183,7 +183,7 @@ class CoreRFIDReader : public interface::RFIDReader void setCommunicationProtocol(rfid::Protocol protocol) final; void sendToTag(std::span data) final; auto didTagCommunicationSucceed(size_t sizeTagData) -> bool final; - void getDataFromTag(std::span data) final; + auto getDataFromTag() -> std::array final; auto getTag() -> rfid::Tag final; void onTagDataReceived() final; void onDataAvailable(); diff --git a/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp b/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp index d6c6b175ae..3f5f803b3e 100644 --- a/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp +++ b/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp @@ -109,12 +109,15 @@ auto CoreRFIDReader::didTagCommunicationSucceed(size_t sizeTagData) -> bool return (did_communication_succeed && did_command_same_size); } -void CoreRFIDReader::getDataFromTag(std::span data) +auto CoreRFIDReader::getDataFromTag() -> std::array { - for (auto i = 0; i < data.size(); ++i) { + static constexpr auto register_size = 18; + std::array data {}; + for (auto i = 0; i < register_size; ++i) { data[i] = _rx_buf[i + rfid::tag_answer::heading_size]; _tag.data[i] = data[i]; } + return data; } auto CoreRFIDReader::getTag() -> rfid::Tag diff --git a/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp b/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp index e334c2f8e0..4ec5e3f3e2 100644 --- a/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp +++ b/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp @@ -218,7 +218,7 @@ TEST_F(CoreRFIDReaderTest, sendCommandSuccess) reader.sendToTag(command); } -TEST_F(CoreRFIDReaderTest, receiveDataSuccess) +TEST_F(CoreRFIDReaderTest, receiveDataSuccess2) { setFakeCallback(); @@ -228,12 +228,10 @@ TEST_F(CoreRFIDReaderTest, receiveDataSuccess) std::array expected_values = {0x4C, 0x45, 0x4B, 0x41, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x6C}; - std::array actual_values {0}; - receiveRFIDReaderAnswer(read_values); reader.onDataAvailable(); - reader.getDataFromTag(actual_values); + std::array actual_values = reader.getDataFromTag(); bool is_communication_succeed = reader.didTagCommunicationSucceed(actual_values.size()); @@ -241,36 +239,35 @@ TEST_F(CoreRFIDReaderTest, receiveDataSuccess) EXPECT_EQ(actual_values, expected_values); } -TEST_F(CoreRFIDReaderTest, receiveDataFailedWrongAnswerFlag) +TEST_F(CoreRFIDReaderTest, receiveDataFailedWrongAnswerFlag2) { setFakeCallback(); - std::array read_values = {0xff, 0x05, 0x44, 0x00, 0x28, 0x00, 0x00}; - std::array actual_values {0}; + std::array read_values = {0xff, 0x05, 0x4C, 0x45, 0x4B, 0x41, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; receiveRFIDReaderAnswer(read_values); reader.onDataAvailable(); - reader.getDataFromTag(actual_values); - bool is_communication_succeed = reader.didTagCommunicationSucceed(read_values.size()); + std::array actual_values = reader.getDataFromTag(); + bool is_communication_succeed = reader.didTagCommunicationSucceed(read_values.size()); EXPECT_EQ(is_communication_succeed, false); EXPECT_NE(actual_values, read_values); } -TEST_F(CoreRFIDReaderTest, receiveDataFailedWrongLength) +TEST_F(CoreRFIDReaderTest, receiveDataFailedWrongLength2) { setFakeCallback(); - std::array read_values = {0x80, 0x02, 0x44, 0x00, 0x28, 0x00, 0x00}; - std::array actual_values {0}; + std::array read_values = {0x80, 0x02, 0x44, 0x00, 0x28, 0x00, 0x00}; receiveRFIDReaderAnswer(read_values); reader.onDataAvailable(); - reader.getDataFromTag(actual_values); + std::array actual_values = reader.getDataFromTag(); bool is_communication_succeed = reader.didTagCommunicationSucceed(read_values.size()); @@ -278,7 +275,7 @@ TEST_F(CoreRFIDReaderTest, receiveDataFailedWrongLength) EXPECT_NE(actual_values, read_values); } -TEST_F(CoreRFIDReaderTest, getTag) +TEST_F(CoreRFIDReaderTest, getTag2) { setFakeCallback(); reader.registerOnTagReadableCallback(callbackFunction); @@ -289,12 +286,10 @@ TEST_F(CoreRFIDReaderTest, getTag) std::array expected_values = {0x4C, 0x45, 0x4B, 0x41, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - std::array actual_values {0}; - receiveRFIDReaderAnswer(read_values); reader.onDataAvailable(); - reader.getDataFromTag(actual_values); + reader.getDataFromTag(); reader.onTagDataReceived(); diff --git a/include/interface/drivers/RFIDReader.h b/include/interface/drivers/RFIDReader.h index 21d9e42f0a..8c3ba77ff1 100644 --- a/include/interface/drivers/RFIDReader.h +++ b/include/interface/drivers/RFIDReader.h @@ -53,7 +53,7 @@ namespace interface { virtual auto isTagDetected() -> bool = 0; virtual void onTagDataReceived() = 0; virtual auto getTag() -> rfid::Tag = 0; - virtual void getDataFromTag(std::span data) = 0; + virtual auto getDataFromTag() -> std::array = 0; virtual auto didTagCommunicationSucceed(size_t sizeTagData) -> bool = 0; virtual void sendToTag(std::span data) = 0; virtual void setCommunicationProtocol(rfid::Protocol protocol) = 0; diff --git a/libs/RFIDKit/include/ISO14443A.h b/libs/RFIDKit/include/ISO14443A.h index 56190595d6..2786609961 100644 --- a/libs/RFIDKit/include/ISO14443A.h +++ b/libs/RFIDKit/include/ISO14443A.h @@ -58,22 +58,22 @@ inline auto computeCRC(uint8_t const *data) -> std::array inline auto receiveAtqa(interface::RFIDReader &rfidreader) -> bool { - std::array atqa_answer {}; - - if (rfidreader.didTagCommunicationSucceed(ATQA_answer_size)) { - rfidreader.getDataFromTag(atqa_answer); + if (!rfidreader.didTagCommunicationSucceed(ATQA_answer_size)) { + return false; } + auto data = rfidreader.getDataFromTag(); + + std::array atqa_answer = {data[0], data[1]}; return atqa_answer == expected_ATQA_answer; } inline auto receiveRegister(interface::RFIDReader &rfidreader) -> bool { - std::array register_answer {}; - - if (rfidreader.didTagCommunicationSucceed(register_answer_size)) { - rfidreader.getDataFromTag(register_answer); + if (!rfidreader.didTagCommunicationSucceed(register_answer_size)) { + return false; } + auto register_answer = rfidreader.getDataFromTag(); std::array received_crc = {register_answer[16], register_answer[17]}; diff --git a/tests/unit/mocks/mocks/leka/CoreRFIDReader.h b/tests/unit/mocks/mocks/leka/CoreRFIDReader.h index 97637a3455..3bfe41af73 100644 --- a/tests/unit/mocks/mocks/leka/CoreRFIDReader.h +++ b/tests/unit/mocks/mocks/leka/CoreRFIDReader.h @@ -18,7 +18,7 @@ class CoreRFIDReader : public interface::RFIDReader MOCK_METHOD(void, setCommunicationProtocol, (rfid::Protocol), (override)); MOCK_METHOD(void, sendToTag, (std::span), (override)); MOCK_METHOD(bool, didTagCommunicationSucceed, (size_t), (override)); - MOCK_METHOD(void, getDataFromTag, (std::span data), (override)); + MOCK_METHOD((std::array), getDataFromTag, (), (override)); MOCK_METHOD(rfid::Tag, getTag, (), (override)); MOCK_METHOD(void, onTagDataReceived, (), (override)); MOCK_METHOD(void, registerOnTagDetectedCallback, (const std::function &), (override)); From 9123763a1767c55476dc6df4e55e7ad5b179008f Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Tue, 26 Jul 2022 15:58:35 +0200 Subject: [PATCH 028/130] :white_check_mark: (tests): Add ISO14443A tests --- libs/RFIDKit/tests/ISO14443A_test.cpp | 140 ++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/libs/RFIDKit/tests/ISO14443A_test.cpp b/libs/RFIDKit/tests/ISO14443A_test.cpp index e69de29bb2..2a14bc53f7 100644 --- a/libs/RFIDKit/tests/ISO14443A_test.cpp +++ b/libs/RFIDKit/tests/ISO14443A_test.cpp @@ -0,0 +1,140 @@ +// Leka - LekaOS +// Copyright 2021 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "ISO14443A.h" + +#include "gtest/gtest.h" +#include "mocks/leka/CoreRFIDReader.h" + +using namespace leka; +using namespace rfid::sm; + +using ::testing::Return; +using ::testing::ReturnPointee; +using ::testing::ReturnRef; + +class ISO14443ATest : public ::testing::Test +{ + protected: + // void SetUp() override {} + // void TearDown() override {} + + mock::CoreRFIDReader mock_reader {}; + boost::sml::sm sm {static_cast(mock_reader)}; + + static constexpr rfid::Command<1> command_requestA = {.data = {0x26}, .flags = leka::rfid::Flag::sb_7}; + static constexpr rfid::Command<2> command_read_register_4 = { + .data = {0x30, 0x04}, .flags = leka::rfid::Flag::crc | leka::rfid::Flag::sb_8}; + + std::function magic_card_callback {}; +}; + +TEST_F(ISO14443ATest, initialization) +{ + ASSERT_NE(&sm, nullptr); +} + +TEST_F(ISO14443ATest, initialState) +{ + EXPECT_TRUE(sm.is(state::idle)); + EXPECT_FALSE(sm.is(state::send_reqa)); + EXPECT_FALSE(sm.is(state::requesting_atqa)); + EXPECT_FALSE(sm.is(state::requesting_tag_data)); +} + +TEST_F(ISO14443ATest, stateIdleEventTagDetectedGuardIsTagDetectedTrue) +{ + sm.set_current_states(state::idle); + + EXPECT_CALL(mock_reader, setCommunicationProtocol).Times(1); + EXPECT_CALL(mock_reader, isTagDetected).Times(1).WillOnce(Return(true)); + + sm.process_event(event::tag_detected {}); + + EXPECT_TRUE(sm.is(state::send_reqa)); +} + +TEST_F(ISO14443ATest, stateIdleEventTagDetectedGuardIsTagDetectedFalse) +{ + sm.set_current_states(state::idle); + + EXPECT_CALL(mock_reader, setTagDetectionMode).Times(1); + EXPECT_CALL(mock_reader, isTagDetected).Times(2).WillRepeatedly(Return(false)); + + sm.process_event(event::tag_detected {}); + + EXPECT_TRUE(sm.is(state::idle)); +} + +TEST_F(ISO14443ATest, stateSendReqaEventTagDetected) +{ + sm.set_current_states(state::send_reqa); + + EXPECT_CALL(mock_reader, sendToTag); + + sm.process_event(event::tag_detected {}); + + EXPECT_TRUE(sm.is(state::requesting_atqa)); +} + +TEST_F(ISO14443ATest, stateRequestingAtqaEventTagDetectedGuardIsTrue) +{ + sm.set_current_states(state::requesting_atqa); + + constexpr std::array expected_ATQA_answer {0x44, 0x00}; + constexpr std::array data { + 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + EXPECT_CALL(mock_reader, didTagCommunicationSucceed).Times(1).WillOnce(Return(true)); + EXPECT_CALL(mock_reader, getDataFromTag).WillOnce(Return(data)); + EXPECT_CALL(mock_reader, sendToTag).Times(1); + + sm.process_event(event::tag_detected {}); + + EXPECT_TRUE(sm.is(state::requesting_tag_data)); +} + +TEST_F(ISO14443ATest, stateRequestingAtqaEventTagDetectedGuardIsFalse) +{ + sm.set_current_states(state::requesting_atqa); + + EXPECT_CALL(mock_reader, didTagCommunicationSucceed).Times(2).WillRepeatedly(Return(false)); + EXPECT_CALL(mock_reader, setTagDetectionMode).Times(1); + + sm.process_event(event::tag_detected {}); + + EXPECT_TRUE(sm.is(state::idle)); +} + +TEST_F(ISO14443ATest, stateRequestingTagDataEventTagDetectedGuardIsTrue) +{ + sm.set_current_states(state::requesting_tag_data); + + constexpr size_t register_answer_size = 18; + + std::array data = {0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, + 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x24, 0xF5}; + + EXPECT_CALL(mock_reader, didTagCommunicationSucceed).Times(1).WillOnce(Return(true)); + + EXPECT_CALL(mock_reader, getDataFromTag).WillOnce(Return(data)); + EXPECT_CALL(mock_reader, onTagDataReceived).Times(1); + EXPECT_CALL(mock_reader, setTagDetectionMode).Times(1); + + sm.process_event(event::tag_detected {}); + + EXPECT_TRUE(sm.is(state::idle)); +} + +TEST_F(ISO14443ATest, stateRequestingTagDataEventTagDetectedGuardIsFalse) +{ + sm.set_current_states(state::requesting_tag_data); + + EXPECT_CALL(mock_reader, didTagCommunicationSucceed).Times(2).WillRepeatedly(Return(false)); + EXPECT_CALL(mock_reader, setTagDetectionMode).Times(1); + + sm.process_event(event::tag_detected {}); + + EXPECT_TRUE(sm.is(state::idle)); +} From f32e5da5ba8848e558c0058da3f8bdef371a0806 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Tue, 26 Jul 2022 18:06:52 +0200 Subject: [PATCH 029/130] :art: (reader): Change [i] to at(i) --- .../CoreRFIDReader/source/CoreRFIDReader.cpp | 17 +++++++---------- include/interface/drivers/RFIDReader.h | 2 +- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp b/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp index 3f5f803b3e..a5d7153ecc 100644 --- a/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp +++ b/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp @@ -88,8 +88,8 @@ void CoreRFIDReader::setGainAndModulationISO14443A() void CoreRFIDReader::sendToTag(std::span data) { - _tx_buf[0] = rfid::command::send_receive; - _tx_buf[1] = static_cast(data.size()); + _tx_buf.at(0) = rfid::command::send_receive; + _tx_buf.at(1) = static_cast(data.size()); for (auto i = 0; i < data.size(); ++i) { _tx_buf.at(i + rfid::tag_answer::heading_size) = data[i]; @@ -100,8 +100,8 @@ void CoreRFIDReader::sendToTag(std::span data) auto CoreRFIDReader::didTagCommunicationSucceed(size_t sizeTagData) -> bool { - uint8_t status = _rx_buf[0]; - uint8_t length = _rx_buf[1]; + uint8_t status = _rx_buf.at(0); + uint8_t length = _rx_buf.at(1); auto did_communication_succeed = status == rfid::status::communication_succeed; auto did_command_same_size = sizeTagData == length - rfid::tag_answer::flag_size; @@ -111,13 +111,10 @@ auto CoreRFIDReader::didTagCommunicationSucceed(size_t sizeTagData) -> bool auto CoreRFIDReader::getDataFromTag() -> std::array { - static constexpr auto register_size = 18; - std::array data {}; - for (auto i = 0; i < register_size; ++i) { - data[i] = _rx_buf[i + rfid::tag_answer::heading_size]; - _tag.data[i] = data[i]; + for (auto i = 0; i < _tag.data.size(); ++i) { + _tag.data.at(i) = _rx_buf.at(i + rfid::tag_answer::heading_size); } - return data; + return _tag.data; } auto CoreRFIDReader::getTag() -> rfid::Tag diff --git a/include/interface/drivers/RFIDReader.h b/include/interface/drivers/RFIDReader.h index 8c3ba77ff1..46ce1613eb 100644 --- a/include/interface/drivers/RFIDReader.h +++ b/include/interface/drivers/RFIDReader.h @@ -15,7 +15,7 @@ namespace rfid { struct Tag { std::array id {}; std::array sak {}; - std::array data {}; + std::array data {}; }; enum class Flag : uint8_t From e239bd95f6cf704ac9866a7882806dc7c3998d97 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 27 Jul 2022 10:26:54 +0200 Subject: [PATCH 030/130] :zap: (span): getTagData returns span instead of array --- .../CoreRFIDReader/include/CoreRFIDReader.h | 2 +- .../CoreRFIDReader/source/CoreRFIDReader.cpp | 2 +- .../tests/CoreRFIDReader_test.cpp | 32 +++++++++---------- include/interface/drivers/RFIDReader.h | 2 +- libs/RFIDKit/tests/ISO14443A_test.cpp | 8 ++--- libs/RFIDKit/tests/RFIDKit_test.cpp | 8 ++--- tests/unit/mocks/mocks/leka/CoreRFIDReader.h | 2 +- 7 files changed, 28 insertions(+), 28 deletions(-) diff --git a/drivers/CoreRFIDReader/include/CoreRFIDReader.h b/drivers/CoreRFIDReader/include/CoreRFIDReader.h index c544e31889..037a72fe17 100644 --- a/drivers/CoreRFIDReader/include/CoreRFIDReader.h +++ b/drivers/CoreRFIDReader/include/CoreRFIDReader.h @@ -183,7 +183,7 @@ class CoreRFIDReader : public interface::RFIDReader void setCommunicationProtocol(rfid::Protocol protocol) final; void sendToTag(std::span data) final; auto didTagCommunicationSucceed(size_t sizeTagData) -> bool final; - auto getDataFromTag() -> std::array final; + auto getDataFromTag() -> std::span final; auto getTag() -> rfid::Tag final; void onTagDataReceived() final; void onDataAvailable(); diff --git a/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp b/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp index a5d7153ecc..b5b9c03716 100644 --- a/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp +++ b/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp @@ -109,7 +109,7 @@ auto CoreRFIDReader::didTagCommunicationSucceed(size_t sizeTagData) -> bool return (did_communication_succeed && did_command_same_size); } -auto CoreRFIDReader::getDataFromTag() -> std::array +auto CoreRFIDReader::getDataFromTag() -> std::span { for (auto i = 0; i < _tag.data.size(); ++i) { _tag.data.at(i) = _rx_buf.at(i + rfid::tag_answer::heading_size); diff --git a/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp b/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp index 4ec5e3f3e2..7ca64ab0f8 100644 --- a/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp +++ b/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp @@ -218,7 +218,7 @@ TEST_F(CoreRFIDReaderTest, sendCommandSuccess) reader.sendToTag(command); } -TEST_F(CoreRFIDReaderTest, receiveDataSuccess2) +TEST_F(CoreRFIDReaderTest, receiveDataSuccess) { setFakeCallback(); @@ -231,51 +231,51 @@ TEST_F(CoreRFIDReaderTest, receiveDataSuccess2) receiveRFIDReaderAnswer(read_values); reader.onDataAvailable(); - std::array actual_values = reader.getDataFromTag(); + std::span actual_values = reader.getDataFromTag(); bool is_communication_succeed = reader.didTagCommunicationSucceed(actual_values.size()); EXPECT_EQ(is_communication_succeed, true); - EXPECT_EQ(actual_values, expected_values); + EXPECT_TRUE(std::equal(actual_values.begin(), actual_values.end(), expected_values.begin())); } -TEST_F(CoreRFIDReaderTest, receiveDataFailedWrongAnswerFlag2) +TEST_F(CoreRFIDReaderTest, receiveDataFailedWrongAnswerFlag) { setFakeCallback(); - std::array read_values = {0xff, 0x05, 0x4C, 0x45, 0x4B, 0x41, 0x00, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + std::array read_values = {0xff, 0x05, 0x4C, 0x45, 0x4B, 0x41, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x6C, 0x28, 0x00, 0x00}; receiveRFIDReaderAnswer(read_values); reader.onDataAvailable(); - std::array actual_values = reader.getDataFromTag(); - bool is_communication_succeed = reader.didTagCommunicationSucceed(read_values.size()); + std::span actual_values = reader.getDataFromTag(); + bool is_communication_succeed = reader.didTagCommunicationSucceed(read_values.size()); EXPECT_EQ(is_communication_succeed, false); - EXPECT_NE(actual_values, read_values); + EXPECT_FALSE(std::equal(actual_values.begin(), actual_values.end(), read_values.begin())); } -TEST_F(CoreRFIDReaderTest, receiveDataFailedWrongLength2) +TEST_F(CoreRFIDReaderTest, receiveDataFailedWrongLength) { setFakeCallback(); - std::array read_values = {0x80, 0x02, 0x44, 0x00, 0x28, 0x00, 0x00}; + std::array read_values = {0x80, 0x02, 0x44, 0x00, 0x28, 0x00, 0x00}; receiveRFIDReaderAnswer(read_values); reader.onDataAvailable(); - std::array actual_values = reader.getDataFromTag(); + std::span actual_values = reader.getDataFromTag(); bool is_communication_succeed = reader.didTagCommunicationSucceed(read_values.size()); EXPECT_EQ(is_communication_succeed, false); - EXPECT_NE(actual_values, read_values); + EXPECT_FALSE(std::equal(read_values.begin(), read_values.end(), actual_values.begin())); } -TEST_F(CoreRFIDReaderTest, getTag2) +TEST_F(CoreRFIDReaderTest, getTag) { setFakeCallback(); reader.registerOnTagReadableCallback(callbackFunction); @@ -283,8 +283,8 @@ TEST_F(CoreRFIDReaderTest, getTag2) std::array read_values = {0x80, 0x15, 0x4C, 0x45, 0x4B, 0x41, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x6C, 0x28, 0x00, 0x00}; - std::array expected_values = {0x4C, 0x45, 0x4B, 0x41, 0x00, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + std::array expected_values = {0x4C, 0x45, 0x4B, 0x41, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x6C}; receiveRFIDReaderAnswer(read_values); diff --git a/include/interface/drivers/RFIDReader.h b/include/interface/drivers/RFIDReader.h index 46ce1613eb..3666ccc4db 100644 --- a/include/interface/drivers/RFIDReader.h +++ b/include/interface/drivers/RFIDReader.h @@ -53,7 +53,7 @@ namespace interface { virtual auto isTagDetected() -> bool = 0; virtual void onTagDataReceived() = 0; virtual auto getTag() -> rfid::Tag = 0; - virtual auto getDataFromTag() -> std::array = 0; + virtual auto getDataFromTag() -> std::span = 0; virtual auto didTagCommunicationSucceed(size_t sizeTagData) -> bool = 0; virtual void sendToTag(std::span data) = 0; virtual void setCommunicationProtocol(rfid::Protocol protocol) = 0; diff --git a/libs/RFIDKit/tests/ISO14443A_test.cpp b/libs/RFIDKit/tests/ISO14443A_test.cpp index 2a14bc53f7..6ff20004eb 100644 --- a/libs/RFIDKit/tests/ISO14443A_test.cpp +++ b/libs/RFIDKit/tests/ISO14443A_test.cpp @@ -83,11 +83,11 @@ TEST_F(ISO14443ATest, stateRequestingAtqaEventTagDetectedGuardIsTrue) sm.set_current_states(state::requesting_atqa); constexpr std::array expected_ATQA_answer {0x44, 0x00}; - constexpr std::array data { - 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + std::array data {0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; EXPECT_CALL(mock_reader, didTagCommunicationSucceed).Times(1).WillOnce(Return(true)); - EXPECT_CALL(mock_reader, getDataFromTag).WillOnce(Return(data)); + EXPECT_CALL(mock_reader, getDataFromTag).WillOnce(Return(std::span(data))); EXPECT_CALL(mock_reader, sendToTag).Times(1); sm.process_event(event::tag_detected {}); @@ -118,7 +118,7 @@ TEST_F(ISO14443ATest, stateRequestingTagDataEventTagDetectedGuardIsTrue) EXPECT_CALL(mock_reader, didTagCommunicationSucceed).Times(1).WillOnce(Return(true)); - EXPECT_CALL(mock_reader, getDataFromTag).WillOnce(Return(data)); + EXPECT_CALL(mock_reader, getDataFromTag).WillOnce(Return(std::span(data))); EXPECT_CALL(mock_reader, onTagDataReceived).Times(1); EXPECT_CALL(mock_reader, setTagDetectionMode).Times(1); diff --git a/libs/RFIDKit/tests/RFIDKit_test.cpp b/libs/RFIDKit/tests/RFIDKit_test.cpp index b45c6d5b56..8c6947044e 100644 --- a/libs/RFIDKit/tests/RFIDKit_test.cpp +++ b/libs/RFIDKit/tests/RFIDKit_test.cpp @@ -46,7 +46,7 @@ TEST_F(RFIDKitTest, registerMagicCardCallbackTagValidCallbackSet) { std::array id {}; std::array sak {}; - std::array data {0x4C, 0x45, 0x4B, 0x41, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + std::array data {0x4C, 0x45, 0x4B, 0x41, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; rfid::Tag tag_valid {id, sak, data}; @@ -65,7 +65,7 @@ TEST_F(RFIDKitTest, registerMagicCardCallbackTagValidCallbackNotSet) { std::array id {}; std::array sak {}; - std::array data {0x4C, 0x45, 0x4B, 0x41, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + std::array data {0x4C, 0x45, 0x4B, 0x41, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; rfid::Tag tag_valid {id, sak, data}; @@ -82,7 +82,7 @@ TEST_F(RFIDKitTest, TagSignatureIsValid) { std::array id {}; std::array sak {}; - std::array data {0x4C, 0x45, 0x4B, 0x41, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + std::array data {0x4C, 0x45, 0x4B, 0x41, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; rfid::Tag tag_valid {id, sak, data}; @@ -95,7 +95,7 @@ TEST_F(RFIDKitTest, TagSignatureIsNotValid) { std::array id {}; std::array sak {}; - std::array data {0x00, 0x45, 0x00, 0x41, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + std::array data {0x00, 0x45, 0x00, 0x41, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; rfid::Tag tag_valid {id, sak, data}; diff --git a/tests/unit/mocks/mocks/leka/CoreRFIDReader.h b/tests/unit/mocks/mocks/leka/CoreRFIDReader.h index 3bfe41af73..9b4b97c78b 100644 --- a/tests/unit/mocks/mocks/leka/CoreRFIDReader.h +++ b/tests/unit/mocks/mocks/leka/CoreRFIDReader.h @@ -18,7 +18,7 @@ class CoreRFIDReader : public interface::RFIDReader MOCK_METHOD(void, setCommunicationProtocol, (rfid::Protocol), (override)); MOCK_METHOD(void, sendToTag, (std::span), (override)); MOCK_METHOD(bool, didTagCommunicationSucceed, (size_t), (override)); - MOCK_METHOD((std::array), getDataFromTag, (), (override)); + MOCK_METHOD((std::span), getDataFromTag, (), (override)); MOCK_METHOD(rfid::Tag, getTag, (), (override)); MOCK_METHOD(void, onTagDataReceived, (), (override)); MOCK_METHOD(void, registerOnTagDetectedCallback, (const std::function &), (override)); From ea6121a5d7378eff8244adb77f496e4aa9392275 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Thu, 28 Jul 2022 17:58:30 +0200 Subject: [PATCH 031/130] :recycle: (rfid): Improve structure --- drivers/CoreRFIDReader/CMakeLists.txt | 1 + .../CoreRFIDReader/include/CoreRFIDReader.h | 36 ++-- .../CoreRFIDReader/source/CoreRFIDReader.cpp | 74 +++----- .../tests/CoreRFIDReader_test.cpp | 165 +++++++----------- include/interface/drivers/RFIDReader.h | 20 +-- libs/RFIDKit/include/ISO14443A.h | 53 +++--- libs/RFIDKit/source/RFIDKit.cpp | 9 +- libs/RFIDKit/tests/ISO14443A_test.cpp | 55 +++--- libs/RFIDKit/tests/RFIDKit_test.cpp | 37 ++-- tests/unit/mocks/mocks/leka/CoreRFIDReader.h | 12 +- 10 files changed, 204 insertions(+), 258 deletions(-) diff --git a/drivers/CoreRFIDReader/CMakeLists.txt b/drivers/CoreRFIDReader/CMakeLists.txt index 37ef3877bb..a78982ed98 100644 --- a/drivers/CoreRFIDReader/CMakeLists.txt +++ b/drivers/CoreRFIDReader/CMakeLists.txt @@ -17,6 +17,7 @@ target_sources(CoreRFIDReader target_link_libraries(CoreRFIDReader mbed-os CoreBufferedSerial + CoreEventQueue ) if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") diff --git a/drivers/CoreRFIDReader/include/CoreRFIDReader.h b/drivers/CoreRFIDReader/include/CoreRFIDReader.h index 037a72fe17..02e0f21ad5 100644 --- a/drivers/CoreRFIDReader/include/CoreRFIDReader.h +++ b/drivers/CoreRFIDReader/include/CoreRFIDReader.h @@ -2,11 +2,12 @@ // Copyright 2022 APF France handicap // SPDX-License-Identifier: Apache-2.0 -#pragma once +//? CR95HF RFID reader datasheet available at: +//? https://www.st.com/resource/en/datasheet/cr95hf.pdf -#include "events/EventQueue.h" -#include "rtos/Thread.h" +#pragma once +#include "CoreEventQueue.h" #include "interface/drivers/BufferedSerial.h" #include "interface/drivers/RFIDReader.h" @@ -176,33 +177,26 @@ class CoreRFIDReader : public interface::RFIDReader void init() final; void registerOnTagDetectedCallback(const std::function &callback) final; - void registerOnTagReadableCallback(const std::function &callback) final; + void registerOnTagReadableCallback(const std::function &callback) final; - auto isTagDetected() -> bool final; - void setTagDetectionMode() final; + void setModeTagDetection() final; void setCommunicationProtocol(rfid::Protocol protocol) final; - void sendToTag(std::span data) final; + void sendRequestToTag(std::span data) final; auto didTagCommunicationSucceed(size_t sizeTagData) -> bool final; - auto getDataFromTag() -> std::span final; - auto getTag() -> rfid::Tag final; - void onTagDataReceived() final; - void onDataAvailable(); + auto getTag() -> rfid::Tag & final; + void onTagReadable() final; private: - void registerOnDataAvailableCallback(); - - void read(); + void _receiveResponseFromTag(); - void setProtocolISO14443A(); - void setGainAndModulationISO14443A(); + void _setProtocolISO14443A(); + void _setGainAndModulationISO14443A(); rfid::Tag _tag {}; - size_t _answer_size {0}; - rtos::Thread _thread {}; - events::EventQueue _event_queue {}; + leka::CoreEventQueue _event_queue {}; interface::BufferedSerial &_serial; - std::function _on_tag_detected; - std::function _on_tag_valid; + std::function _on_tag_response_available; + std::function _on_tag_readable; std::array _tx_buf {}; std::array _rx_buf {}; diff --git a/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp b/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp index b5b9c03716..a29e776a37 100644 --- a/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp +++ b/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp @@ -5,61 +5,46 @@ #include "CoreRFIDReader.h" #include "rtos/ThisThread.h" + using namespace std::chrono; namespace leka { void CoreRFIDReader::init() { - _thread.start({&_event_queue, &events::EventQueue::dispatch_forever}); - registerOnDataAvailableCallback(); + _event_queue.dispatch_forever(); + + // ? BufferedSerial::sigio executes the callback in ISR context. We use an + // ? event queue to defer the execution of the callback outside the ISR context. + + auto callback = [this] { + _event_queue.call([this]() { + this->_receiveResponseFromTag(); + _on_tag_response_available(); + }); + }; + _serial.sigio(callback); } void CoreRFIDReader::registerOnTagDetectedCallback(const std::function &callback) { - _on_tag_detected = callback; + _on_tag_response_available = callback; }; -void CoreRFIDReader::registerOnTagReadableCallback(const std::function &callback) +void CoreRFIDReader::registerOnTagReadableCallback(const std::function &callback) { - _on_tag_valid = callback; + _on_tag_readable = callback; }; -void CoreRFIDReader::registerOnDataAvailableCallback() -{ - auto func = [this]() { this->onDataAvailable(); }; - auto callback = [this, func] { _event_queue.call(func); }; - _serial.sigio(callback); -} - -void CoreRFIDReader::onDataAvailable() -{ - read(); - _on_tag_detected(); -} - -void CoreRFIDReader::read() +void CoreRFIDReader::_receiveResponseFromTag() { rtos::ThisThread::sleep_for(10ms); if (_serial.readable()) { - _answer_size = _serial.read(_rx_buf.data(), _rx_buf.size()); + _serial.read(_rx_buf.data(), _rx_buf.size()); } } -auto CoreRFIDReader::isTagDetected() -> bool -{ - std::array buffer {}; - - if (_answer_size != rfid::expected_answer_size::tag_detection) { - return false; - } - - std::copy(_rx_buf.begin() + 1, _rx_buf.begin() + 1 + buffer.size(), buffer.begin()); - - return buffer == rfid::status::tag_detection_callback; -} - -void CoreRFIDReader::setTagDetectionMode() +void CoreRFIDReader::setModeTagDetection() { _serial.write(rfid::command::frame::set_tag_detection_mode.data(), rfid::command::frame::set_tag_detection_mode.size()); @@ -69,24 +54,24 @@ void CoreRFIDReader::setTagDetectionMode() void CoreRFIDReader::setCommunicationProtocol(rfid::Protocol protocol) { if (protocol == rfid::Protocol::ISO14443A) { - setProtocolISO14443A(); - setGainAndModulationISO14443A(); + _setProtocolISO14443A(); + _setGainAndModulationISO14443A(); } } -void CoreRFIDReader::setProtocolISO14443A() +void CoreRFIDReader::_setProtocolISO14443A() { _serial.write(rfid::command::frame::set_protocol_iso14443.data(), rfid::command::frame::set_protocol_iso14443.size()); } -void CoreRFIDReader::setGainAndModulationISO14443A() +void CoreRFIDReader::_setGainAndModulationISO14443A() { _serial.write(rfid::command::frame::set_gain_and_modulation.data(), rfid::command::frame::set_gain_and_modulation.size()); } -void CoreRFIDReader::sendToTag(std::span data) +void CoreRFIDReader::sendRequestToTag(std::span data) { _tx_buf.at(0) = rfid::command::send_receive; _tx_buf.at(1) = static_cast(data.size()); @@ -109,22 +94,17 @@ auto CoreRFIDReader::didTagCommunicationSucceed(size_t sizeTagData) -> bool return (did_communication_succeed && did_command_same_size); } -auto CoreRFIDReader::getDataFromTag() -> std::span +auto CoreRFIDReader::getTag() -> rfid::Tag & { for (auto i = 0; i < _tag.data.size(); ++i) { _tag.data.at(i) = _rx_buf.at(i + rfid::tag_answer::heading_size); } - return _tag.data; -} - -auto CoreRFIDReader::getTag() -> rfid::Tag -{ return _tag; } -void CoreRFIDReader::onTagDataReceived() +void CoreRFIDReader::onTagReadable() { - _on_tag_valid(); + _on_tag_readable(_tag); } } // namespace leka diff --git a/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp b/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp index 7ca64ab0f8..289a41864b 100644 --- a/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp +++ b/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp @@ -8,6 +8,7 @@ #include "CoreRFIDReader.h" #include "gtest/gtest.h" #include "mocks/leka/CoreBufferedSerial.h" +#include "mocks/leka/EventQueue.h" using namespace leka; using namespace interface; @@ -17,6 +18,7 @@ using ::testing::DoAll; using ::testing::ElementsAre; using ::testing::InSequence; using ::testing::Return; +using ::testing::SaveArg; using ::testing::SetArrayArgument; class CoreRFIDReaderTest : public ::testing::Test @@ -27,13 +29,17 @@ class CoreRFIDReaderTest : public ::testing::Test // void SetUp() override {} // void TearDown() override {} + mock::EventQueue event_queue {}; mock::CoreBufferedSerial mockBufferedSerial; CoreRFIDReader reader; bool _callbackCalled {false}; - static void func() {}; - std::function callbackFunction = func; + static void func1() {}; + static void func2(rfid::Tag tag) {}; + std::function callbackDetected = func1; + std::function callbackReadable = func2; + std::function callbackSigio {}; void isCallbackCalled() { _callbackCalled = true; }; @@ -44,21 +50,6 @@ class CoreRFIDReaderTest : public ::testing::Test reader.registerOnTagDetectedCallback(callback); } - void sendSetModeTagDetection() - { - const auto expected_values = ElementsAre( - rfid::command::frame::set_tag_detection_mode[0], rfid::command::frame::set_tag_detection_mode[1], - rfid::command::frame::set_tag_detection_mode[2], rfid::command::frame::set_tag_detection_mode[3], - rfid::command::frame::set_tag_detection_mode[4], rfid::command::frame::set_tag_detection_mode[5], - rfid::command::frame::set_tag_detection_mode[6], rfid::command::frame::set_tag_detection_mode[7], - rfid::command::frame::set_tag_detection_mode[8], rfid::command::frame::set_tag_detection_mode[9], - rfid::command::frame::set_tag_detection_mode[10], rfid::command::frame::set_tag_detection_mode[11], - rfid::command::frame::set_tag_detection_mode[12], rfid::command::frame::set_tag_detection_mode[13], - rfid::command::frame::set_tag_detection_mode[14], rfid::command::frame::set_tag_detection_mode[15]); - - EXPECT_CALL(mockBufferedSerial, write).With(Args<0, 1>(expected_values)); - } - void sendSetProtocol() { const auto expected_values = ElementsAre(rfid::command::set_protocol::id, rfid::command::set_protocol::length, @@ -89,7 +80,7 @@ TEST_F(CoreRFIDReaderTest, initialization) EXPECT_NE(&reader, nullptr); } -TEST_F(CoreRFIDReaderTest, enableTagDetection) +TEST_F(CoreRFIDReaderTest, init) { EXPECT_CALL(mockBufferedSerial, sigio).Times(1); @@ -98,71 +89,33 @@ TEST_F(CoreRFIDReaderTest, enableTagDetection) TEST_F(CoreRFIDReaderTest, registerCallbacks) { - reader.registerOnTagDetectedCallback(callbackFunction); - reader.registerOnTagReadableCallback(callbackFunction); + reader.registerOnTagDetectedCallback(callbackDetected); + reader.registerOnTagReadableCallback(callbackReadable); } -TEST_F(CoreRFIDReaderTest, setTagDetectionMode) +TEST_F(CoreRFIDReaderTest, setModeTagDetection) { - sendSetModeTagDetection(); - - reader.setTagDetectionMode(); -} - -TEST_F(CoreRFIDReaderTest, onDataAvailableSuccess) -{ - setFakeCallback(); - - std::array expected_tag_detection_callback = {0x00, 0x01, 0x02}; - - reader.registerOnTagDetectedCallback(callbackFunction); - - receiveRFIDReaderAnswer(expected_tag_detection_callback); - - reader.onDataAvailable(); - - auto is_tag_detected = reader.isTagDetected(); - - EXPECT_TRUE(is_tag_detected); -} - -TEST_F(CoreRFIDReaderTest, onDataAvailableWrongCallback) -{ - setFakeCallback(); - - std::array expected_tag_detection_callback = {0x00, 0x01, 0xff}; - - reader.registerOnTagDetectedCallback(callbackFunction); - - receiveRFIDReaderAnswer(expected_tag_detection_callback); - - reader.onDataAvailable(); + const auto expected_values = + ElementsAre(rfid::command::frame::set_tag_detection_mode[0], rfid::command::frame::set_tag_detection_mode[1], + rfid::command::frame::set_tag_detection_mode[2], rfid::command::frame::set_tag_detection_mode[3], + rfid::command::frame::set_tag_detection_mode[4], rfid::command::frame::set_tag_detection_mode[5], + rfid::command::frame::set_tag_detection_mode[6], rfid::command::frame::set_tag_detection_mode[7], + rfid::command::frame::set_tag_detection_mode[8], rfid::command::frame::set_tag_detection_mode[9], + rfid::command::frame::set_tag_detection_mode[10], rfid::command::frame::set_tag_detection_mode[11], + rfid::command::frame::set_tag_detection_mode[12], rfid::command::frame::set_tag_detection_mode[13], + rfid::command::frame::set_tag_detection_mode[14], rfid::command::frame::set_tag_detection_mode[15]); - auto is_tag_detected = reader.isTagDetected(); - - EXPECT_FALSE(is_tag_detected); -} - -TEST_F(CoreRFIDReaderTest, onDataAvailableTooLongCallback) -{ - setFakeCallback(); - - std::array expected_tag_detection_callback = {0x00, 0x01, 0x03, 0x04}; - - reader.registerOnTagDetectedCallback(callbackFunction); - - receiveRFIDReaderAnswer(expected_tag_detection_callback); - - reader.onDataAvailable(); - - auto is_tag_detected = reader.isTagDetected(); + EXPECT_CALL(mockBufferedSerial, write).With(Args<0, 1>(expected_values)); + ; - EXPECT_FALSE(is_tag_detected); + reader.setModeTagDetection(); } TEST_F(CoreRFIDReaderTest, setCommunicationProtocolSuccess) { - setFakeCallback(); + reader.registerOnTagDetectedCallback(callbackDetected); + EXPECT_CALL(mockBufferedSerial, sigio).Times(1).WillOnce(SaveArg<0>(&callbackSigio)); + reader.init(); std::array set_protocol_success_answer = {0x00, 0x00}; std::array set_gain_and_modulation_success_answer = {0x00, 0x00}; @@ -174,13 +127,16 @@ TEST_F(CoreRFIDReaderTest, setCommunicationProtocolSuccess) sendSetGainAndModulation(); } - reader.onDataAvailable(); + callbackSigio(); reader.setCommunicationProtocol(rfid::Protocol::ISO14443A); } TEST_F(CoreRFIDReaderTest, setCommunicationProtocolFailedOnAnswerTooBig) { - setFakeCallback(); + reader.registerOnTagDetectedCallback(callbackDetected); + EXPECT_CALL(mockBufferedSerial, sigio).Times(1).WillOnce(SaveArg<0>(&callbackSigio)); + reader.init(); + std::array set_protocol_failed_answer = {0x00, 0x00, 0x00}; { InSequence seq; @@ -189,13 +145,16 @@ TEST_F(CoreRFIDReaderTest, setCommunicationProtocolFailedOnAnswerTooBig) sendSetGainAndModulation(); } - reader.onDataAvailable(); + callbackSigio(); reader.setCommunicationProtocol(rfid::Protocol::ISO14443A); } TEST_F(CoreRFIDReaderTest, setCommunicationProtocolFailedOnWrongFirstValue) { - setFakeCallback(); + reader.registerOnTagDetectedCallback(callbackDetected); + EXPECT_CALL(mockBufferedSerial, sigio).Times(1).WillOnce(SaveArg<0>(&callbackSigio)); + reader.init(); + std::array set_protocol_failed_answer = {0x82, 0x00}; { InSequence seq; @@ -204,7 +163,7 @@ TEST_F(CoreRFIDReaderTest, setCommunicationProtocolFailedOnWrongFirstValue) sendSetGainAndModulation(); } - reader.onDataAvailable(); + callbackSigio(); reader.setCommunicationProtocol(rfid::Protocol::ISO14443A); } @@ -215,12 +174,14 @@ TEST_F(CoreRFIDReaderTest, sendCommandSuccess) EXPECT_CALL(mockBufferedSerial, write).With(Args<0, 1>(expected_values)); - reader.sendToTag(command); + reader.sendRequestToTag(command); } TEST_F(CoreRFIDReaderTest, receiveDataSuccess) { - setFakeCallback(); + reader.registerOnTagDetectedCallback(callbackDetected); + EXPECT_CALL(mockBufferedSerial, sigio).Times(1).WillOnce(SaveArg<0>(&callbackSigio)); + reader.init(); std::array read_values = {0x80, 0x15, 0x4C, 0x45, 0x4B, 0x41, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x6C, 0x28, 0x00, 0x00}; @@ -229,56 +190,61 @@ TEST_F(CoreRFIDReaderTest, receiveDataSuccess) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x6C}; receiveRFIDReaderAnswer(read_values); + callbackSigio(); + auto tag = reader.getTag(); - reader.onDataAvailable(); - std::span actual_values = reader.getDataFromTag(); - - bool is_communication_succeed = reader.didTagCommunicationSucceed(actual_values.size()); + bool is_communication_succeed = reader.didTagCommunicationSucceed(tag.data.size()); EXPECT_EQ(is_communication_succeed, true); - EXPECT_TRUE(std::equal(actual_values.begin(), actual_values.end(), expected_values.begin())); + EXPECT_TRUE(std::equal(tag.data.begin(), tag.data.end(), expected_values.begin())); } TEST_F(CoreRFIDReaderTest, receiveDataFailedWrongAnswerFlag) { - setFakeCallback(); + reader.registerOnTagDetectedCallback(callbackDetected); + EXPECT_CALL(mockBufferedSerial, sigio).Times(1).WillOnce(SaveArg<0>(&callbackSigio)); + reader.init(); std::array read_values = {0xff, 0x05, 0x4C, 0x45, 0x4B, 0x41, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x6C, 0x28, 0x00, 0x00}; receiveRFIDReaderAnswer(read_values); - reader.onDataAvailable(); + callbackSigio(); - std::span actual_values = reader.getDataFromTag(); - bool is_communication_succeed = reader.didTagCommunicationSucceed(read_values.size()); + auto tag = reader.getTag(); + bool is_communication_succeed = reader.didTagCommunicationSucceed(tag.data.size()); EXPECT_EQ(is_communication_succeed, false); - EXPECT_FALSE(std::equal(actual_values.begin(), actual_values.end(), read_values.begin())); + EXPECT_FALSE(std::equal(tag.data.begin(), tag.data.end(), read_values.begin())); } TEST_F(CoreRFIDReaderTest, receiveDataFailedWrongLength) { - setFakeCallback(); + reader.registerOnTagDetectedCallback(callbackDetected); + EXPECT_CALL(mockBufferedSerial, sigio).Times(1).WillOnce(SaveArg<0>(&callbackSigio)); + reader.init(); std::array read_values = {0x80, 0x02, 0x44, 0x00, 0x28, 0x00, 0x00}; receiveRFIDReaderAnswer(read_values); - reader.onDataAvailable(); + callbackSigio(); - std::span actual_values = reader.getDataFromTag(); + auto tag = reader.getTag(); bool is_communication_succeed = reader.didTagCommunicationSucceed(read_values.size()); EXPECT_EQ(is_communication_succeed, false); - EXPECT_FALSE(std::equal(read_values.begin(), read_values.end(), actual_values.begin())); + EXPECT_FALSE(std::equal(read_values.begin(), read_values.end(), tag.data.begin())); } TEST_F(CoreRFIDReaderTest, getTag) { - setFakeCallback(); - reader.registerOnTagReadableCallback(callbackFunction); + reader.registerOnTagReadableCallback(callbackReadable); + reader.registerOnTagDetectedCallback(callbackDetected); + EXPECT_CALL(mockBufferedSerial, sigio).Times(1).WillOnce(SaveArg<0>(&callbackSigio)); + reader.init(); std::array read_values = {0x80, 0x15, 0x4C, 0x45, 0x4B, 0x41, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x6C, 0x28, 0x00, 0x00}; @@ -288,10 +254,9 @@ TEST_F(CoreRFIDReaderTest, getTag) receiveRFIDReaderAnswer(read_values); - reader.onDataAvailable(); - reader.getDataFromTag(); + callbackSigio(); - reader.onTagDataReceived(); + reader.onTagReadable(); auto tag = reader.getTag(); diff --git a/include/interface/drivers/RFIDReader.h b/include/interface/drivers/RFIDReader.h index 3666ccc4db..d02fa214d5 100644 --- a/include/interface/drivers/RFIDReader.h +++ b/include/interface/drivers/RFIDReader.h @@ -48,17 +48,15 @@ namespace interface { public: virtual ~RFIDReader() = default; - virtual void init() = 0; - virtual void setTagDetectionMode() = 0; - virtual auto isTagDetected() -> bool = 0; - virtual void onTagDataReceived() = 0; - virtual auto getTag() -> rfid::Tag = 0; - virtual auto getDataFromTag() -> std::span = 0; - virtual auto didTagCommunicationSucceed(size_t sizeTagData) -> bool = 0; - virtual void sendToTag(std::span data) = 0; - virtual void setCommunicationProtocol(rfid::Protocol protocol) = 0; - virtual void registerOnTagDetectedCallback(const std::function &callback) = 0; - virtual void registerOnTagReadableCallback(const std::function &callback) = 0; + virtual void init() = 0; + virtual void setModeTagDetection() = 0; + virtual void onTagReadable() = 0; + virtual auto getTag() -> rfid::Tag & = 0; + virtual auto didTagCommunicationSucceed(size_t sizeTagData) -> bool = 0; + virtual void sendRequestToTag(std::span data) = 0; + virtual void setCommunicationProtocol(rfid::Protocol protocol) = 0; + virtual void registerOnTagDetectedCallback(const std::function &callback) = 0; + virtual void registerOnTagReadableCallback(const std::function &callback) = 0; }; } // namespace interface diff --git a/libs/RFIDKit/include/ISO14443A.h b/libs/RFIDKit/include/ISO14443A.h index 2786609961..811c087e27 100644 --- a/libs/RFIDKit/include/ISO14443A.h +++ b/libs/RFIDKit/include/ISO14443A.h @@ -21,10 +21,10 @@ struct Command { auto cmd = std::array {}; for (int i = 0; i < SIZE; ++i) { - cmd[i] = data[i]; + cmd.at(i) = data.at(i); } - cmd[SIZE] = static_cast(flags); + cmd.at(SIZE) = static_cast(flags); return cmd; } @@ -62,10 +62,10 @@ inline auto receiveAtqa(interface::RFIDReader &rfidreader) -> bool return false; } - auto data = rfidreader.getDataFromTag(); + auto tag = rfidreader.getTag(); - std::array atqa_answer = {data[0], data[1]}; - return atqa_answer == expected_ATQA_answer; + auto is_atqa_correct = std::equal(expected_ATQA_answer.begin(), expected_ATQA_answer.end(), tag.data.begin()); + return is_atqa_correct; } inline auto receiveRegister(interface::RFIDReader &rfidreader) -> bool @@ -73,16 +73,17 @@ inline auto receiveRegister(interface::RFIDReader &rfidreader) -> bool if (!rfidreader.didTagCommunicationSucceed(register_answer_size)) { return false; } - auto register_answer = rfidreader.getDataFromTag(); + auto tag = rfidreader.getTag(); - std::array received_crc = {register_answer[16], register_answer[17]}; + auto crc_computed = computeCRC(tag.data.data()); + auto is_crc_correct = std::equal(crc_computed.begin(), crc_computed.end(), tag.data.begin() + 16); - return received_crc == computeCRC(register_answer.data()); + return is_crc_correct; } namespace sm::event { - struct tag_detected { + struct tag_response_available { }; } // namespace sm::event @@ -100,10 +101,6 @@ namespace sm::guard { using irfidreader = interface::RFIDReader; - struct is_tag_detected { - auto operator()(irfidreader &rfidreader) const { return rfidreader.isTagDetected(); } - }; - struct atqa_received { auto operator()(irfidreader &rfidreader) const { return receiveAtqa(rfidreader); } }; @@ -118,8 +115,8 @@ namespace sm::action { using irfidreader = interface::RFIDReader; - struct set_tag_detection_mode { - auto operator()(irfidreader &rfidreader) const { rfidreader.setTagDetectionMode(); } + struct set_mode_tag_detection { + auto operator()(irfidreader &rfidreader) const { rfidreader.setModeTagDetection(); } }; struct set_communication_protocol { @@ -130,15 +127,18 @@ namespace sm::action { }; struct send_request_A { - auto operator()(irfidreader &rfidreader) const { rfidreader.sendToTag(command_requestA.getArray()); } + auto operator()(irfidreader &rfidreader) const { rfidreader.sendRequestToTag(command_requestA.getArray()); } }; struct send_register_4 { - auto operator()(irfidreader &rfidreader) const { rfidreader.sendToTag(command_read_register_4.getArray()); } + auto operator()(irfidreader &rfidreader) const + { + rfidreader.sendRequestToTag(command_read_register_4.getArray()); + } }; - struct on_tag_data_received { - auto operator()(irfidreader &rfidreader) const { rfidreader.onTagDataReceived(); } + struct on_tag_readable { + auto operator()(irfidreader &rfidreader) const { rfidreader.onTagReadable(); } }; } // namespace sm::action @@ -150,18 +150,17 @@ struct ISO14443A { return make_transition_table( // clang-format off - * sm::state::idle + event [sm::guard::is_tag_detected {} ] / (sm::action::set_communication_protocol {}) = sm::state::send_reqa - , sm::state::idle + event [!sm::guard::is_tag_detected {}] / (sm::action::set_tag_detection_mode {}) = sm::state::idle + * sm::state::idle + event / (sm::action::set_communication_protocol {}) = sm::state::send_reqa - , sm::state::send_reqa + event / (sm::action::send_request_A {}) = sm::state::requesting_atqa + , sm::state::send_reqa + event / (sm::action::send_request_A {}) = sm::state::requesting_atqa - , sm::state::requesting_atqa + event [sm::guard::atqa_received {}] / (sm::action::send_register_4 {}) = sm::state::requesting_tag_data - , sm::state::requesting_atqa + event [!sm::guard::atqa_received {}] / (sm::action::set_tag_detection_mode {}) = sm::state::idle + , sm::state::requesting_atqa + event [sm::guard::atqa_received {}] / (sm::action::send_register_4 {}) = sm::state::requesting_tag_data + , sm::state::requesting_atqa + event [!sm::guard::atqa_received {}] / (sm::action::set_mode_tag_detection {}) = sm::state::idle - , sm::state::requesting_tag_data + event [sm::guard::register_received {}] / (sm::action::on_tag_data_received {}) = sm::state::idle - , sm::state::requesting_tag_data + event [!sm::guard::register_received {}] = sm::state::idle + , sm::state::requesting_tag_data + event [sm::guard::register_received {}] / (sm::action::on_tag_readable {}) = sm::state::idle + , sm::state::requesting_tag_data + event [!sm::guard::register_received {}] = sm::state::idle - , sm::state::requesting_tag_data + boost::sml::on_exit<_> / ( sm::action::set_tag_detection_mode {}) + , sm::state::requesting_tag_data + boost::sml::on_exit<_> / ( sm::action::set_mode_tag_detection {}) // clang-format on ); diff --git a/libs/RFIDKit/source/RFIDKit.cpp b/libs/RFIDKit/source/RFIDKit.cpp index eddf70a6e5..66a44542a6 100644 --- a/libs/RFIDKit/source/RFIDKit.cpp +++ b/libs/RFIDKit/source/RFIDKit.cpp @@ -10,21 +10,20 @@ void RFIDKit::init() { using namespace rfid::sm; - static auto tag_detected_callback = [this]() { state_machine.process_event(event::tag_detected {}); }; + static auto tag_detected_callback = [this]() { state_machine.process_event(event::tag_response_available {}); }; _rfid_reader.registerOnTagDetectedCallback(tag_detected_callback); registerMagicCard(); _rfid_reader.init(); - _rfid_reader.setTagDetectionMode(); + _rfid_reader.setModeTagDetection(); } void RFIDKit::registerMagicCard() { - auto on_magic_card_readable_callback = [this]() { - rfid::Tag tag = _rfid_reader.getTag(); + auto on_magic_card_readable_callback = [this](rfid::Tag &tag) { if (isTagSignatureValid(tag)) { - _card = MagicCard {tag.data[5]}; + _card = MagicCard {static_cast((tag.data[4] << 8) + tag.data[5])}; if (_on_tag_available_callback != nullptr) { _on_tag_available_callback(_card); diff --git a/libs/RFIDKit/tests/ISO14443A_test.cpp b/libs/RFIDKit/tests/ISO14443A_test.cpp index 6ff20004eb..11b879f687 100644 --- a/libs/RFIDKit/tests/ISO14443A_test.cpp +++ b/libs/RFIDKit/tests/ISO14443A_test.cpp @@ -11,7 +11,6 @@ using namespace leka; using namespace rfid::sm; using ::testing::Return; -using ::testing::ReturnPointee; using ::testing::ReturnRef; class ISO14443ATest : public ::testing::Test @@ -48,32 +47,19 @@ TEST_F(ISO14443ATest, stateIdleEventTagDetectedGuardIsTagDetectedTrue) sm.set_current_states(state::idle); EXPECT_CALL(mock_reader, setCommunicationProtocol).Times(1); - EXPECT_CALL(mock_reader, isTagDetected).Times(1).WillOnce(Return(true)); - sm.process_event(event::tag_detected {}); + sm.process_event(event::tag_response_available {}); EXPECT_TRUE(sm.is(state::send_reqa)); } -TEST_F(ISO14443ATest, stateIdleEventTagDetectedGuardIsTagDetectedFalse) -{ - sm.set_current_states(state::idle); - - EXPECT_CALL(mock_reader, setTagDetectionMode).Times(1); - EXPECT_CALL(mock_reader, isTagDetected).Times(2).WillRepeatedly(Return(false)); - - sm.process_event(event::tag_detected {}); - - EXPECT_TRUE(sm.is(state::idle)); -} - TEST_F(ISO14443ATest, stateSendReqaEventTagDetected) { sm.set_current_states(state::send_reqa); - EXPECT_CALL(mock_reader, sendToTag); + EXPECT_CALL(mock_reader, sendRequestToTag); - sm.process_event(event::tag_detected {}); + sm.process_event(event::tag_response_available {}); EXPECT_TRUE(sm.is(state::requesting_atqa)); } @@ -83,14 +69,19 @@ TEST_F(ISO14443ATest, stateRequestingAtqaEventTagDetectedGuardIsTrue) sm.set_current_states(state::requesting_atqa); constexpr std::array expected_ATQA_answer {0x44, 0x00}; + + std::array id {}; + std::array sak {}; std::array data {0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + rfid::Tag tag {id, sak, data}; + EXPECT_CALL(mock_reader, didTagCommunicationSucceed).Times(1).WillOnce(Return(true)); - EXPECT_CALL(mock_reader, getDataFromTag).WillOnce(Return(std::span(data))); - EXPECT_CALL(mock_reader, sendToTag).Times(1); + EXPECT_CALL(mock_reader, getTag).WillOnce(ReturnRef(tag)); + EXPECT_CALL(mock_reader, sendRequestToTag).Times(1); - sm.process_event(event::tag_detected {}); + sm.process_event(event::tag_response_available {}); EXPECT_TRUE(sm.is(state::requesting_tag_data)); } @@ -100,9 +91,9 @@ TEST_F(ISO14443ATest, stateRequestingAtqaEventTagDetectedGuardIsFalse) sm.set_current_states(state::requesting_atqa); EXPECT_CALL(mock_reader, didTagCommunicationSucceed).Times(2).WillRepeatedly(Return(false)); - EXPECT_CALL(mock_reader, setTagDetectionMode).Times(1); + EXPECT_CALL(mock_reader, setModeTagDetection).Times(1); - sm.process_event(event::tag_detected {}); + sm.process_event(event::tag_response_available {}); EXPECT_TRUE(sm.is(state::idle)); } @@ -113,16 +104,20 @@ TEST_F(ISO14443ATest, stateRequestingTagDataEventTagDetectedGuardIsTrue) constexpr size_t register_answer_size = 18; - std::array data = {0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, - 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x24, 0xF5}; + std::array id {}; + std::array sak {}; + std::array data {0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, + 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x24, 0xF5}; + + rfid::Tag tag {id, sak, data}; EXPECT_CALL(mock_reader, didTagCommunicationSucceed).Times(1).WillOnce(Return(true)); - EXPECT_CALL(mock_reader, getDataFromTag).WillOnce(Return(std::span(data))); - EXPECT_CALL(mock_reader, onTagDataReceived).Times(1); - EXPECT_CALL(mock_reader, setTagDetectionMode).Times(1); + EXPECT_CALL(mock_reader, getTag).WillOnce(ReturnRef(tag)); + EXPECT_CALL(mock_reader, onTagReadable).Times(1); + EXPECT_CALL(mock_reader, setModeTagDetection).Times(1); - sm.process_event(event::tag_detected {}); + sm.process_event(event::tag_response_available {}); EXPECT_TRUE(sm.is(state::idle)); } @@ -132,9 +127,9 @@ TEST_F(ISO14443ATest, stateRequestingTagDataEventTagDetectedGuardIsFalse) sm.set_current_states(state::requesting_tag_data); EXPECT_CALL(mock_reader, didTagCommunicationSucceed).Times(2).WillRepeatedly(Return(false)); - EXPECT_CALL(mock_reader, setTagDetectionMode).Times(1); + EXPECT_CALL(mock_reader, setModeTagDetection).Times(1); - sm.process_event(event::tag_detected {}); + sm.process_event(event::tag_response_available {}); EXPECT_TRUE(sm.is(state::idle)); } diff --git a/libs/RFIDKit/tests/RFIDKit_test.cpp b/libs/RFIDKit/tests/RFIDKit_test.cpp index 8c6947044e..75c85b8497 100644 --- a/libs/RFIDKit/tests/RFIDKit_test.cpp +++ b/libs/RFIDKit/tests/RFIDKit_test.cpp @@ -9,7 +9,7 @@ using namespace leka; -using ::testing::Return; +using ::testing::ReturnRef; using ::testing::SaveArg; class RFIDKitTest : public ::testing::Test @@ -24,7 +24,7 @@ class RFIDKitTest : public ::testing::Test mock::CoreRFIDReader mock_reader {}; const std::function callback; - std::function magic_card_callback {}; + std::function magic_card_callback {}; }; TEST_F(RFIDKitTest, initialization) @@ -37,7 +37,7 @@ TEST_F(RFIDKitTest, init) EXPECT_CALL(mock_reader, registerOnTagDetectedCallback).Times(1); EXPECT_CALL(mock_reader, registerOnTagReadableCallback).Times(1); EXPECT_CALL(mock_reader, init).Times(1); - EXPECT_CALL(mock_reader, setTagDetectionMode).Times(1); + EXPECT_CALL(mock_reader, setModeTagDetection).Times(1); rfid_kit.init(); } @@ -50,15 +50,34 @@ TEST_F(RFIDKitTest, registerMagicCardCallbackTagValidCallbackSet) rfid::Tag tag_valid {id, sak, data}; + auto callback = [](MagicCard &) { ; }; + rfid_kit.onTagActivated(callback); EXPECT_CALL(mock_reader, registerOnTagReadableCallback).WillOnce(SaveArg<0>(&magic_card_callback)); rfid_kit.registerMagicCard(); - EXPECT_CALL(mock_reader, getTag).WillOnce(Return(tag_valid)); + magic_card_callback(tag_valid); +} - magic_card_callback(); +TEST_F(RFIDKitTest, registerMagicCardCallbackTagNotValidCallbackSet) +{ + std::array id {}; + std::array sak {}; + std::array data {0x4C, 0x45, 0x4B, 0x00, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + rfid::Tag tag_not_valid {id, sak, data}; + + auto callback = [](MagicCard &) { ; }; + + rfid_kit.onTagActivated(callback); + + EXPECT_CALL(mock_reader, registerOnTagReadableCallback).WillOnce(SaveArg<0>(&magic_card_callback)); + + rfid_kit.registerMagicCard(); + + magic_card_callback(tag_not_valid); } TEST_F(RFIDKitTest, registerMagicCardCallbackTagValidCallbackNotSet) @@ -73,9 +92,7 @@ TEST_F(RFIDKitTest, registerMagicCardCallbackTagValidCallbackNotSet) rfid_kit.registerMagicCard(); - EXPECT_CALL(mock_reader, getTag).WillOnce(Return(tag_valid)); - - magic_card_callback(); + magic_card_callback(tag_valid); } TEST_F(RFIDKitTest, TagSignatureIsValid) @@ -97,9 +114,9 @@ TEST_F(RFIDKitTest, TagSignatureIsNotValid) std::array sak {}; std::array data {0x00, 0x45, 0x00, 0x41, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - rfid::Tag tag_valid {id, sak, data}; + rfid::Tag tag_not_valid {id, sak, data}; - auto is_tag_valid = rfid_kit.isTagSignatureValid(tag_valid); + auto is_tag_valid = rfid_kit.isTagSignatureValid(tag_not_valid); EXPECT_FALSE(is_tag_valid); } diff --git a/tests/unit/mocks/mocks/leka/CoreRFIDReader.h b/tests/unit/mocks/mocks/leka/CoreRFIDReader.h index 9b4b97c78b..30f0589eae 100644 --- a/tests/unit/mocks/mocks/leka/CoreRFIDReader.h +++ b/tests/unit/mocks/mocks/leka/CoreRFIDReader.h @@ -13,16 +13,14 @@ class CoreRFIDReader : public interface::RFIDReader { public: MOCK_METHOD(void, init, (), (override)); - MOCK_METHOD(bool, isTagDetected, (), (override)); - MOCK_METHOD(void, setTagDetectionMode, (), (override)); + MOCK_METHOD(void, setModeTagDetection, (), (override)); MOCK_METHOD(void, setCommunicationProtocol, (rfid::Protocol), (override)); - MOCK_METHOD(void, sendToTag, (std::span), (override)); + MOCK_METHOD(void, sendRequestToTag, (std::span), (override)); MOCK_METHOD(bool, didTagCommunicationSucceed, (size_t), (override)); - MOCK_METHOD((std::span), getDataFromTag, (), (override)); - MOCK_METHOD(rfid::Tag, getTag, (), (override)); - MOCK_METHOD(void, onTagDataReceived, (), (override)); + MOCK_METHOD(rfid::Tag &, getTag, (), (override)); + MOCK_METHOD(void, onTagReadable, (), (override)); MOCK_METHOD(void, registerOnTagDetectedCallback, (const std::function &), (override)); - MOCK_METHOD(void, registerOnTagReadableCallback, (const std::function &), (override)); + MOCK_METHOD(void, registerOnTagReadableCallback, (const std::function &), (override)); }; } // namespace leka::mock From bd5c491263ebdc6293d6f6e34660c6e268ba3ca1 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Fri, 29 Jul 2022 18:01:20 +0200 Subject: [PATCH 032/130] :white_check_mark: (tests): Improve core & kit tests --- .../tests/CoreRFIDReader_test.cpp | 110 +++++------------- libs/RFIDKit/tests/RFIDKit_test.cpp | 18 +-- 2 files changed, 41 insertions(+), 87 deletions(-) diff --git a/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp b/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp index 289a41864b..d507b15ac1 100644 --- a/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp +++ b/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp @@ -26,29 +26,22 @@ class CoreRFIDReaderTest : public ::testing::Test protected: CoreRFIDReaderTest() : reader(mockBufferedSerial) {}; - // void SetUp() override {} + void SetUp() override + { + reader.registerOnTagReadableCallback(callback_readable.AsStdFunction()); + reader.registerOnTagDetectedCallback(callback_detected.AsStdFunction()); + EXPECT_CALL(mockBufferedSerial, sigio).WillOnce(SaveArg<0>(&callback_sigio)); + reader.init(); + } // void TearDown() override {} mock::EventQueue event_queue {}; mock::CoreBufferedSerial mockBufferedSerial; CoreRFIDReader reader; - bool _callbackCalled {false}; - - static void func1() {}; - static void func2(rfid::Tag tag) {}; - std::function callbackDetected = func1; - std::function callbackReadable = func2; - std::function callbackSigio {}; - - void isCallbackCalled() { _callbackCalled = true; }; - - void setFakeCallback() - { - _callbackCalled = false; - auto callback = [this]() { this->isCallbackCalled(); }; - reader.registerOnTagDetectedCallback(callback); - } + testing::MockFunction callback_detected; + testing::MockFunction callback_readable; + std::function callback_sigio {}; void sendSetProtocol() { @@ -89,36 +82,22 @@ TEST_F(CoreRFIDReaderTest, init) TEST_F(CoreRFIDReaderTest, registerCallbacks) { - reader.registerOnTagDetectedCallback(callbackDetected); - reader.registerOnTagReadableCallback(callbackReadable); + reader.registerOnTagDetectedCallback(callback_detected.AsStdFunction()); + reader.registerOnTagReadableCallback(callback_readable.AsStdFunction()); } TEST_F(CoreRFIDReaderTest, setModeTagDetection) { - const auto expected_values = - ElementsAre(rfid::command::frame::set_tag_detection_mode[0], rfid::command::frame::set_tag_detection_mode[1], - rfid::command::frame::set_tag_detection_mode[2], rfid::command::frame::set_tag_detection_mode[3], - rfid::command::frame::set_tag_detection_mode[4], rfid::command::frame::set_tag_detection_mode[5], - rfid::command::frame::set_tag_detection_mode[6], rfid::command::frame::set_tag_detection_mode[7], - rfid::command::frame::set_tag_detection_mode[8], rfid::command::frame::set_tag_detection_mode[9], - rfid::command::frame::set_tag_detection_mode[10], rfid::command::frame::set_tag_detection_mode[11], - rfid::command::frame::set_tag_detection_mode[12], rfid::command::frame::set_tag_detection_mode[13], - rfid::command::frame::set_tag_detection_mode[14], rfid::command::frame::set_tag_detection_mode[15]); + const auto expected_values = testing::ElementsAreArray(rfid::command::frame::set_tag_detection_mode); EXPECT_CALL(mockBufferedSerial, write).With(Args<0, 1>(expected_values)); - ; reader.setModeTagDetection(); } TEST_F(CoreRFIDReaderTest, setCommunicationProtocolSuccess) { - reader.registerOnTagDetectedCallback(callbackDetected); - EXPECT_CALL(mockBufferedSerial, sigio).Times(1).WillOnce(SaveArg<0>(&callbackSigio)); - reader.init(); - - std::array set_protocol_success_answer = {0x00, 0x00}; - std::array set_gain_and_modulation_success_answer = {0x00, 0x00}; + std::array set_protocol_success_answer = {0x00, 0x00}; { InSequence seq; @@ -127,34 +106,26 @@ TEST_F(CoreRFIDReaderTest, setCommunicationProtocolSuccess) sendSetGainAndModulation(); } - callbackSigio(); + callback_sigio(); reader.setCommunicationProtocol(rfid::Protocol::ISO14443A); } -TEST_F(CoreRFIDReaderTest, setCommunicationProtocolFailedOnAnswerTooBig) -{ - reader.registerOnTagDetectedCallback(callbackDetected); - EXPECT_CALL(mockBufferedSerial, sigio).Times(1).WillOnce(SaveArg<0>(&callbackSigio)); - reader.init(); - - std::array set_protocol_failed_answer = {0x00, 0x00, 0x00}; - { - InSequence seq; - receiveRFIDReaderAnswer(set_protocol_failed_answer); - sendSetProtocol(); - sendSetGainAndModulation(); - } +// TEST_F(CoreRFIDReaderTest, setCommunicationProtocolFailedOnAnswerTooBig) +// { +// std::array set_protocol_failed_answer = {0x00, 0x00, 0x00}; +// { +// InSequence seq; +// receiveRFIDReaderAnswer(set_protocol_failed_answer); +// sendSetProtocol(); +// sendSetGainAndModulation(); +// } - callbackSigio(); - reader.setCommunicationProtocol(rfid::Protocol::ISO14443A); -} +// callback_sigio(); +// reader.setCommunicationProtocol(rfid::Protocol::ISO14443A); +// } TEST_F(CoreRFIDReaderTest, setCommunicationProtocolFailedOnWrongFirstValue) { - reader.registerOnTagDetectedCallback(callbackDetected); - EXPECT_CALL(mockBufferedSerial, sigio).Times(1).WillOnce(SaveArg<0>(&callbackSigio)); - reader.init(); - std::array set_protocol_failed_answer = {0x82, 0x00}; { InSequence seq; @@ -163,7 +134,7 @@ TEST_F(CoreRFIDReaderTest, setCommunicationProtocolFailedOnWrongFirstValue) sendSetGainAndModulation(); } - callbackSigio(); + callback_sigio(); reader.setCommunicationProtocol(rfid::Protocol::ISO14443A); } @@ -179,10 +150,6 @@ TEST_F(CoreRFIDReaderTest, sendCommandSuccess) TEST_F(CoreRFIDReaderTest, receiveDataSuccess) { - reader.registerOnTagDetectedCallback(callbackDetected); - EXPECT_CALL(mockBufferedSerial, sigio).Times(1).WillOnce(SaveArg<0>(&callbackSigio)); - reader.init(); - std::array read_values = {0x80, 0x15, 0x4C, 0x45, 0x4B, 0x41, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x6C, 0x28, 0x00, 0x00}; @@ -190,7 +157,7 @@ TEST_F(CoreRFIDReaderTest, receiveDataSuccess) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x6C}; receiveRFIDReaderAnswer(read_values); - callbackSigio(); + callback_sigio(); auto tag = reader.getTag(); bool is_communication_succeed = reader.didTagCommunicationSucceed(tag.data.size()); @@ -201,16 +168,12 @@ TEST_F(CoreRFIDReaderTest, receiveDataSuccess) TEST_F(CoreRFIDReaderTest, receiveDataFailedWrongAnswerFlag) { - reader.registerOnTagDetectedCallback(callbackDetected); - EXPECT_CALL(mockBufferedSerial, sigio).Times(1).WillOnce(SaveArg<0>(&callbackSigio)); - reader.init(); - std::array read_values = {0xff, 0x05, 0x4C, 0x45, 0x4B, 0x41, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x6C, 0x28, 0x00, 0x00}; receiveRFIDReaderAnswer(read_values); - callbackSigio(); + callback_sigio(); auto tag = reader.getTag(); bool is_communication_succeed = reader.didTagCommunicationSucceed(tag.data.size()); @@ -221,15 +184,11 @@ TEST_F(CoreRFIDReaderTest, receiveDataFailedWrongAnswerFlag) TEST_F(CoreRFIDReaderTest, receiveDataFailedWrongLength) { - reader.registerOnTagDetectedCallback(callbackDetected); - EXPECT_CALL(mockBufferedSerial, sigio).Times(1).WillOnce(SaveArg<0>(&callbackSigio)); - reader.init(); - std::array read_values = {0x80, 0x02, 0x44, 0x00, 0x28, 0x00, 0x00}; receiveRFIDReaderAnswer(read_values); - callbackSigio(); + callback_sigio(); auto tag = reader.getTag(); @@ -241,11 +200,6 @@ TEST_F(CoreRFIDReaderTest, receiveDataFailedWrongLength) TEST_F(CoreRFIDReaderTest, getTag) { - reader.registerOnTagReadableCallback(callbackReadable); - reader.registerOnTagDetectedCallback(callbackDetected); - EXPECT_CALL(mockBufferedSerial, sigio).Times(1).WillOnce(SaveArg<0>(&callbackSigio)); - reader.init(); - std::array read_values = {0x80, 0x15, 0x4C, 0x45, 0x4B, 0x41, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCA, 0x6C, 0x28, 0x00, 0x00}; @@ -254,7 +208,7 @@ TEST_F(CoreRFIDReaderTest, getTag) receiveRFIDReaderAnswer(read_values); - callbackSigio(); + callback_sigio(); reader.onTagReadable(); diff --git a/libs/RFIDKit/tests/RFIDKit_test.cpp b/libs/RFIDKit/tests/RFIDKit_test.cpp index 75c85b8497..91e261c7c3 100644 --- a/libs/RFIDKit/tests/RFIDKit_test.cpp +++ b/libs/RFIDKit/tests/RFIDKit_test.cpp @@ -9,6 +9,7 @@ using namespace leka; +using ::testing::MockFunction; using ::testing::ReturnRef; using ::testing::SaveArg; @@ -22,7 +23,7 @@ class RFIDKitTest : public ::testing::Test RFIDKit rfid_kit; mock::CoreRFIDReader mock_reader {}; - const std::function callback; + MockFunction mock_callback; std::function magic_card_callback {}; }; @@ -50,14 +51,13 @@ TEST_F(RFIDKitTest, registerMagicCardCallbackTagValidCallbackSet) rfid::Tag tag_valid {id, sak, data}; - auto callback = [](MagicCard &) { ; }; - - rfid_kit.onTagActivated(callback); + rfid_kit.onTagActivated(mock_callback.AsStdFunction()); EXPECT_CALL(mock_reader, registerOnTagReadableCallback).WillOnce(SaveArg<0>(&magic_card_callback)); rfid_kit.registerMagicCard(); + EXPECT_CALL(mock_callback, Call).Times(1); magic_card_callback(tag_valid); } @@ -69,14 +69,12 @@ TEST_F(RFIDKitTest, registerMagicCardCallbackTagNotValidCallbackSet) rfid::Tag tag_not_valid {id, sak, data}; - auto callback = [](MagicCard &) { ; }; - - rfid_kit.onTagActivated(callback); - + rfid_kit.onTagActivated(mock_callback.AsStdFunction()); EXPECT_CALL(mock_reader, registerOnTagReadableCallback).WillOnce(SaveArg<0>(&magic_card_callback)); rfid_kit.registerMagicCard(); + EXPECT_CALL(mock_callback, Call).Times(0); magic_card_callback(tag_not_valid); } @@ -88,6 +86,8 @@ TEST_F(RFIDKitTest, registerMagicCardCallbackTagValidCallbackNotSet) rfid::Tag tag_valid {id, sak, data}; + rfid_kit.onTagActivated(nullptr); + EXPECT_CALL(mock_reader, registerOnTagReadableCallback).WillOnce(SaveArg<0>(&magic_card_callback)); rfid_kit.registerMagicCard(); @@ -123,5 +123,5 @@ TEST_F(RFIDKitTest, TagSignatureIsNotValid) TEST_F(RFIDKitTest, onTagActivated) { - rfid_kit.onTagActivated(callback); + rfid_kit.onTagActivated(nullptr); } From 3f9e3d05c86be9ca22a5de914f3fb70afbcca25a Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Tue, 16 Aug 2022 16:06:10 +0200 Subject: [PATCH 033/130] :art: (SM ISO14443A): Split receive funcs into receive and check funcs --- libs/RFIDKit/include/ISO14443A.h | 59 ++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/libs/RFIDKit/include/ISO14443A.h b/libs/RFIDKit/include/ISO14443A.h index 811c087e27..a7fde786be 100644 --- a/libs/RFIDKit/include/ISO14443A.h +++ b/libs/RFIDKit/include/ISO14443A.h @@ -34,51 +34,66 @@ constexpr Command<1> command_requestA = {.data = {0x26}, .flags = leka::rfid:: constexpr Command<2> command_read_register_4 = {.data = {0x30, 0x04}, .flags = leka::rfid::Flag::crc | leka::rfid::Flag::sb_8}; -constexpr size_t ATQA_answer_size = 2; -constexpr size_t register_answer_size = 18; -constexpr std::array expected_ATQA_answer {0x44, 0x00}; +constexpr auto ATQA_answer_size = 2; +constexpr auto initial_polynomial_value = uint16_t {0x6363}; +constexpr auto register_answer_size = size_t {18}; +constexpr auto expected_ATQA_answer = std::array {0x44, 0x00}; inline auto computeCRC(uint8_t const *data) -> std::array { - uint32_t wCrc = 0x6363; + // ? Implementation taken from libnfc: + // ? https://github.com/nfc-tools/libnfc/blob/master/libnfc/iso14443-subr.c#L47-L62 + + uint32_t wCrc = initial_polynomial_value; size_t size = 16; do { - std::byte bt; - bt = static_cast(*data++); - bt = (bt ^ static_cast(wCrc & 0x00FF)); - bt = (bt ^ (bt << 4)); - wCrc = (wCrc >> 8) ^ (static_cast(bt) << 8) ^ (static_cast(bt) << 3) ^ - (static_cast(bt) >> 4); + std::byte val; + val = static_cast(*data++); + val = (val ^ static_cast(wCrc & 0x00FF)); + val = (val ^ (val << 4)); + wCrc = (wCrc >> 8) ^ (static_cast(val) << 8) ^ (static_cast(val) << 3) ^ + (static_cast(val) >> 4); } while (--size); std::array pbtCrc = {static_cast(wCrc & 0xFF), static_cast((wCrc >> 8) & 0xFF)}; return pbtCrc; } -inline auto receiveAtqa(interface::RFIDReader &rfidreader) -> bool +inline auto checkATQA(rfid::Tag tag) -> bool +{ + auto is_atqa_correct = std::equal(expected_ATQA_answer.begin(), expected_ATQA_answer.end(), tag.data.begin()); + return is_atqa_correct; +} + +inline auto checkRegister(rfid::Tag tag) -> bool +{ + auto crc_computed = computeCRC(tag.data.data()); + auto is_crc_correct = std::equal(crc_computed.begin(), crc_computed.end(), tag.data.begin() + 16); + + return is_crc_correct; +} + +inline auto atqaReceived(interface::RFIDReader &rfidreader) -> bool { if (!rfidreader.didTagCommunicationSucceed(ATQA_answer_size)) { return false; } - auto tag = rfidreader.getTag(); + const auto &tag = rfidreader.getTag(); - auto is_atqa_correct = std::equal(expected_ATQA_answer.begin(), expected_ATQA_answer.end(), tag.data.begin()); - return is_atqa_correct; + return checkATQA(tag); } -inline auto receiveRegister(interface::RFIDReader &rfidreader) -> bool +inline auto registerReceived(interface::RFIDReader &rfidreader) -> bool { - if (!rfidreader.didTagCommunicationSucceed(register_answer_size)) { + if (!rfidreader.didTagCommunicationSucceed(ATQA_answer_size)) { return false; } - auto tag = rfidreader.getTag(); - auto crc_computed = computeCRC(tag.data.data()); - auto is_crc_correct = std::equal(crc_computed.begin(), crc_computed.end(), tag.data.begin() + 16); + const auto &tag = rfidreader.getTag(); - return is_crc_correct; + return checkRegister(tag); } namespace sm::event { @@ -102,11 +117,11 @@ namespace sm::guard { using irfidreader = interface::RFIDReader; struct atqa_received { - auto operator()(irfidreader &rfidreader) const { return receiveAtqa(rfidreader); } + auto operator()(irfidreader &rfidreader) const { return atqaReceived(rfidreader); } }; struct register_received { - auto operator()(irfidreader &rfidreader) const { return receiveRegister(rfidreader); } + auto operator()(irfidreader &rfidreader) const { return registerReceived(rfidreader); } }; } // namespace sm::guard From 634010b570b69367147807ebeba8fa2b5a442bcd Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Tue, 16 Aug 2022 17:59:54 +0200 Subject: [PATCH 034/130] :art: (RFIDReaderCR95HF): Rename Reader and namespace --- app/os/main.cpp | 4 +- drivers/CoreRFIDReader/CMakeLists.txt | 4 +- ...oreRFIDReader.h => CoreRFIDReaderCR95HF.h} | 72 ++++++------ .../CoreRFIDReader/source/CoreRFIDReader.cpp | 110 ------------------ .../source/CoreRFIDReaderCR95HF.cpp | 110 ++++++++++++++++++ ...test.cpp => CoreRFIDReaderCR95HF_test.cpp} | 37 +++--- libs/RFIDKit/source/RFIDKit.cpp | 5 +- libs/RFIDKit/tests/ISO14443A_test.cpp | 4 +- libs/RFIDKit/tests/RFIDKit_test.cpp | 4 +- spikes/lk_rfid/main.cpp | 4 +- spikes/lk_watchdog_isr/main.cpp | 4 +- ...oreRFIDReader.h => CoreRFIDReaderCR95HF.h} | 2 +- 12 files changed, 175 insertions(+), 185 deletions(-) rename drivers/CoreRFIDReader/include/{CoreRFIDReader.h => CoreRFIDReaderCR95HF.h} (75%) delete mode 100644 drivers/CoreRFIDReader/source/CoreRFIDReader.cpp create mode 100644 drivers/CoreRFIDReader/source/CoreRFIDReaderCR95HF.cpp rename drivers/CoreRFIDReader/tests/{CoreRFIDReader_test.cpp => CoreRFIDReaderCR95HF_test.cpp} (82%) rename tests/unit/mocks/mocks/leka/{CoreRFIDReader.h => CoreRFIDReaderCR95HF.h} (93%) diff --git a/app/os/main.cpp b/app/os/main.cpp index 664ab89195..960bb5133f 100644 --- a/app/os/main.cpp +++ b/app/os/main.cpp @@ -29,7 +29,7 @@ #include "CoreMotor.h" #include "CorePwm.h" #include "CoreQSPI.h" -#include "CoreRFIDReader.h" +#include "CoreRFIDReaderCR95HF.h" #include "CoreSDRAM.hpp" #include "CoreSPI.h" #include "CoreSTM32Hal.h" @@ -305,7 +305,7 @@ namespace firmware { namespace rfid { auto serial = CoreBufferedSerial(RFID_UART_TX, RFID_UART_RX, 57600); - auto reader = CoreRFIDReader(serial); + auto reader = CoreRFIDReaderCR95HF(serial); } // namespace rfid diff --git a/drivers/CoreRFIDReader/CMakeLists.txt b/drivers/CoreRFIDReader/CMakeLists.txt index a78982ed98..f76d1b9e6b 100644 --- a/drivers/CoreRFIDReader/CMakeLists.txt +++ b/drivers/CoreRFIDReader/CMakeLists.txt @@ -11,7 +11,7 @@ target_include_directories(CoreRFIDReader target_sources(CoreRFIDReader PRIVATE - source/CoreRFIDReader.cpp + source/CoreRFIDReaderCR95HF.cpp ) target_link_libraries(CoreRFIDReader @@ -22,6 +22,6 @@ target_link_libraries(CoreRFIDReader if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") leka_unit_tests_sources( - tests/CoreRFIDReader_test.cpp + tests/CoreRFIDReaderCR95HF_test.cpp ) endif() diff --git a/drivers/CoreRFIDReader/include/CoreRFIDReader.h b/drivers/CoreRFIDReader/include/CoreRFIDReaderCR95HF.h similarity index 75% rename from drivers/CoreRFIDReader/include/CoreRFIDReader.h rename to drivers/CoreRFIDReader/include/CoreRFIDReaderCR95HF.h index 02e0f21ad5..a4a9f6eb0b 100644 --- a/drivers/CoreRFIDReader/include/CoreRFIDReader.h +++ b/drivers/CoreRFIDReader/include/CoreRFIDReaderCR95HF.h @@ -13,7 +13,7 @@ namespace leka { -namespace rfid { +namespace rfid::cr95hf { struct RFIDProtocol { const uint8_t id; @@ -119,60 +119,60 @@ namespace rfid { namespace frame { inline constexpr auto set_tag_detection_mode = std::to_array({ - rfid::settings::idle_tag_detection::tag_detection_command, - rfid::settings::idle_tag_detection::length, - rfid::settings::idle_tag_detection::wu_source, - rfid::settings::idle_tag_detection::enter_control[0], - rfid::settings::idle_tag_detection::enter_control[1], - rfid::settings::idle_tag_detection::wu_control[0], - rfid::settings::idle_tag_detection::wu_control[1], - rfid::settings::idle_tag_detection::leave_control[0], - rfid::settings::idle_tag_detection::leave_control[1], - rfid::settings::idle_tag_detection::wu_periode, - rfid::settings::idle_tag_detection::oscillator_start, - rfid::settings::idle_tag_detection::digital_to_analog_start, - rfid::settings::idle_tag_detection::digital_to_analog_data[0], - rfid::settings::idle_tag_detection::digital_to_analog_data[1], - rfid::settings::idle_tag_detection::swing_count, - rfid::settings::idle_tag_detection::max_sleep, + settings::idle_tag_detection::tag_detection_command, + settings::idle_tag_detection::length, + settings::idle_tag_detection::wu_source, + settings::idle_tag_detection::enter_control[0], + settings::idle_tag_detection::enter_control[1], + settings::idle_tag_detection::wu_control[0], + settings::idle_tag_detection::wu_control[1], + settings::idle_tag_detection::leave_control[0], + settings::idle_tag_detection::leave_control[1], + settings::idle_tag_detection::wu_periode, + settings::idle_tag_detection::oscillator_start, + settings::idle_tag_detection::digital_to_analog_start, + settings::idle_tag_detection::digital_to_analog_data[0], + settings::idle_tag_detection::digital_to_analog_data[1], + settings::idle_tag_detection::swing_count, + settings::idle_tag_detection::max_sleep, }); inline constexpr auto idn = std::to_array({ - rfid::command::idn::id, - rfid::command::idn::length, + command::idn::id, + command::idn::length, }); inline constexpr auto set_baudrate = std::to_array({ - rfid::command::set_baudrate::id, - rfid::command::set_baudrate::length, + command::set_baudrate::id, + command::set_baudrate::length, }); inline constexpr auto set_protocol_iso14443 = std::to_array({ - rfid::command::set_protocol::id, - rfid::command::set_protocol::length, - rfid::protocol::iso14443A.id, - rfid::settings::default_rx_tx_speed, + command::set_protocol::id, + command::set_protocol::length, + protocol::iso14443A.id, + settings::default_rx_tx_speed, }); inline constexpr auto set_gain_and_modulation = std::to_array({ - rfid::command::set_gain_and_modulation::id, - rfid::command::set_gain_and_modulation::length, - rfid::settings::arc_b, - rfid::settings::flag_increment, - rfid::settings::acr_b_index_for_gain_and_modulation, - rfid::protocol::iso14443A.gain_modulation_values(), + command::set_gain_and_modulation::id, + command::set_gain_and_modulation::length, + settings::arc_b, + settings::flag_increment, + settings::acr_b_index_for_gain_and_modulation, + protocol::iso14443A.gain_modulation_values(), }); } // namespace frame } // namespace command -} // namespace rfid +} // namespace rfid::cr95hf -class CoreRFIDReader : public interface::RFIDReader +class CoreRFIDReaderCR95HF : public interface::RFIDReader { public: - explicit CoreRFIDReader(interface::BufferedSerial &serial) : _serial(serial) {}; + explicit CoreRFIDReaderCR95HF(interface::BufferedSerial &serial) : _serial(serial) {}; void init() final; @@ -198,8 +198,8 @@ class CoreRFIDReader : public interface::RFIDReader std::function _on_tag_response_available; std::function _on_tag_readable; - std::array _tx_buf {}; - std::array _rx_buf {}; + std::array _tx_buf {}; + std::array _rx_buf {}; }; } // namespace leka diff --git a/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp b/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp deleted file mode 100644 index a29e776a37..0000000000 --- a/drivers/CoreRFIDReader/source/CoreRFIDReader.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// Leka - LekaOS -// Copyright 2022 APF France handicap -// SPDX-License-Identifier: Apache-2.0 - -#include "CoreRFIDReader.h" - -#include "rtos/ThisThread.h" - -using namespace std::chrono; - -namespace leka { - -void CoreRFIDReader::init() -{ - _event_queue.dispatch_forever(); - - // ? BufferedSerial::sigio executes the callback in ISR context. We use an - // ? event queue to defer the execution of the callback outside the ISR context. - - auto callback = [this] { - _event_queue.call([this]() { - this->_receiveResponseFromTag(); - _on_tag_response_available(); - }); - }; - _serial.sigio(callback); -} - -void CoreRFIDReader::registerOnTagDetectedCallback(const std::function &callback) -{ - _on_tag_response_available = callback; -}; - -void CoreRFIDReader::registerOnTagReadableCallback(const std::function &callback) -{ - _on_tag_readable = callback; -}; - -void CoreRFIDReader::_receiveResponseFromTag() -{ - rtos::ThisThread::sleep_for(10ms); - if (_serial.readable()) { - _serial.read(_rx_buf.data(), _rx_buf.size()); - } -} - -void CoreRFIDReader::setModeTagDetection() -{ - _serial.write(rfid::command::frame::set_tag_detection_mode.data(), - rfid::command::frame::set_tag_detection_mode.size()); - rtos::ThisThread::sleep_for(10ms); -} - -void CoreRFIDReader::setCommunicationProtocol(rfid::Protocol protocol) -{ - if (protocol == rfid::Protocol::ISO14443A) { - _setProtocolISO14443A(); - _setGainAndModulationISO14443A(); - } -} - -void CoreRFIDReader::_setProtocolISO14443A() -{ - _serial.write(rfid::command::frame::set_protocol_iso14443.data(), - rfid::command::frame::set_protocol_iso14443.size()); -} - -void CoreRFIDReader::_setGainAndModulationISO14443A() -{ - _serial.write(rfid::command::frame::set_gain_and_modulation.data(), - rfid::command::frame::set_gain_and_modulation.size()); -} - -void CoreRFIDReader::sendRequestToTag(std::span data) -{ - _tx_buf.at(0) = rfid::command::send_receive; - _tx_buf.at(1) = static_cast(data.size()); - - for (auto i = 0; i < data.size(); ++i) { - _tx_buf.at(i + rfid::tag_answer::heading_size) = data[i]; - } - - _serial.write(_tx_buf.data(), data.size() + rfid::tag_answer::heading_size); -} - -auto CoreRFIDReader::didTagCommunicationSucceed(size_t sizeTagData) -> bool -{ - uint8_t status = _rx_buf.at(0); - uint8_t length = _rx_buf.at(1); - - auto did_communication_succeed = status == rfid::status::communication_succeed; - auto did_command_same_size = sizeTagData == length - rfid::tag_answer::flag_size; - - return (did_communication_succeed && did_command_same_size); -} - -auto CoreRFIDReader::getTag() -> rfid::Tag & -{ - for (auto i = 0; i < _tag.data.size(); ++i) { - _tag.data.at(i) = _rx_buf.at(i + rfid::tag_answer::heading_size); - } - return _tag; -} - -void CoreRFIDReader::onTagReadable() -{ - _on_tag_readable(_tag); -} - -} // namespace leka diff --git a/drivers/CoreRFIDReader/source/CoreRFIDReaderCR95HF.cpp b/drivers/CoreRFIDReader/source/CoreRFIDReaderCR95HF.cpp new file mode 100644 index 0000000000..ec93e10112 --- /dev/null +++ b/drivers/CoreRFIDReader/source/CoreRFIDReaderCR95HF.cpp @@ -0,0 +1,110 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "CoreRFIDReaderCR95HF.h" + +#include "rtos/ThisThread.h" + +using namespace std::chrono; + +namespace leka { + +void CoreRFIDReaderCR95HF::init() +{ + _event_queue.dispatch_forever(); + + // ? BufferedSerial::sigio executes the callback in ISR context. We use an + // ? event queue to defer the execution of the callback outside the ISR context. + + auto callback = [this] { + _event_queue.call([this]() { + this->_receiveResponseFromTag(); + _on_tag_response_available(); + }); + }; + _serial.sigio(callback); +} + +void CoreRFIDReaderCR95HF::registerOnTagDetectedCallback(const std::function &callback) +{ + _on_tag_response_available = callback; +}; + +void CoreRFIDReaderCR95HF::registerOnTagReadableCallback(const std::function &callback) +{ + _on_tag_readable = callback; +}; + +void CoreRFIDReaderCR95HF::_receiveResponseFromTag() +{ + rtos::ThisThread::sleep_for(10ms); + if (_serial.readable()) { + _serial.read(_rx_buf.data(), _rx_buf.size()); + } +} + +void CoreRFIDReaderCR95HF::setModeTagDetection() +{ + _serial.write(rfid::cr95hf::command::frame::set_tag_detection_mode.data(), + rfid::cr95hf::command::frame::set_tag_detection_mode.size()); + rtos::ThisThread::sleep_for(10ms); +} + +void CoreRFIDReaderCR95HF::setCommunicationProtocol(rfid::Protocol protocol) +{ + if (protocol == rfid::Protocol::ISO14443A) { + _setProtocolISO14443A(); + _setGainAndModulationISO14443A(); + } +} + +void CoreRFIDReaderCR95HF::_setProtocolISO14443A() +{ + _serial.write(rfid::cr95hf::command::frame::set_protocol_iso14443.data(), + rfid::cr95hf::command::frame::set_protocol_iso14443.size()); +} + +void CoreRFIDReaderCR95HF::_setGainAndModulationISO14443A() +{ + _serial.write(rfid::cr95hf::command::frame::set_gain_and_modulation.data(), + rfid::cr95hf::command::frame::set_gain_and_modulation.size()); +} + +void CoreRFIDReaderCR95HF::sendRequestToTag(std::span data) +{ + _tx_buf.at(0) = rfid::cr95hf::command::send_receive; + _tx_buf.at(1) = static_cast(data.size()); + + for (auto i = 0; i < data.size(); ++i) { + _tx_buf.at(i + rfid::cr95hf::tag_answer::heading_size) = data[i]; + } + + _serial.write(_tx_buf.data(), data.size() + rfid::cr95hf::tag_answer::heading_size); +} + +auto CoreRFIDReaderCR95HF::didTagCommunicationSucceed(size_t sizeTagData) -> bool +{ + uint8_t status = _rx_buf.at(0); + uint8_t length = _rx_buf.at(1); + + auto did_communication_succeed = status == rfid::cr95hf::status::communication_succeed; + auto did_command_same_size = sizeTagData == length - rfid::cr95hf::tag_answer::flag_size; + + return (did_communication_succeed && did_command_same_size); +} + +auto CoreRFIDReaderCR95HF::getTag() -> rfid::Tag & +{ + for (auto i = 0; i < _tag.data.size(); ++i) { + _tag.data.at(i) = _rx_buf.at(i + rfid::cr95hf::tag_answer::heading_size); + } + return _tag; +} + +void CoreRFIDReaderCR95HF::onTagReadable() +{ + _on_tag_readable(_tag); +} + +} // namespace leka diff --git a/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp b/drivers/CoreRFIDReader/tests/CoreRFIDReaderCR95HF_test.cpp similarity index 82% rename from drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp rename to drivers/CoreRFIDReader/tests/CoreRFIDReaderCR95HF_test.cpp index d507b15ac1..ab994007b5 100644 --- a/drivers/CoreRFIDReader/tests/CoreRFIDReader_test.cpp +++ b/drivers/CoreRFIDReader/tests/CoreRFIDReaderCR95HF_test.cpp @@ -5,7 +5,7 @@ #include #include -#include "CoreRFIDReader.h" +#include "CoreRFIDReaderCR95HF.h" #include "gtest/gtest.h" #include "mocks/leka/CoreBufferedSerial.h" #include "mocks/leka/EventQueue.h" @@ -37,7 +37,7 @@ class CoreRFIDReaderTest : public ::testing::Test mock::EventQueue event_queue {}; mock::CoreBufferedSerial mockBufferedSerial; - CoreRFIDReader reader; + CoreRFIDReaderCR95HF reader; testing::MockFunction callback_detected; testing::MockFunction callback_readable; @@ -45,17 +45,19 @@ class CoreRFIDReaderTest : public ::testing::Test void sendSetProtocol() { - const auto expected_values = ElementsAre(rfid::command::set_protocol::id, rfid::command::set_protocol::length, - rfid::protocol::iso14443A.id, rfid::settings::default_rx_tx_speed); + const auto expected_values = + ElementsAre(rfid::cr95hf::command::set_protocol::id, rfid::cr95hf::command::set_protocol::length, + rfid::cr95hf::protocol::iso14443A.id, rfid::cr95hf::settings::default_rx_tx_speed); EXPECT_CALL(mockBufferedSerial, write).With(Args<0, 1>(expected_values)); } void sendSetGainAndModulation() { - const auto expected_values = ElementsAre( - rfid::command::set_gain_and_modulation::id, rfid::command::set_gain_and_modulation::length, - rfid::settings::arc_b, rfid::settings::flag_increment, rfid::settings::acr_b_index_for_gain_and_modulation, - rfid::protocol::iso14443A.gain_modulation_values()); + const auto expected_values = ElementsAre(rfid::cr95hf::command::set_gain_and_modulation::id, + rfid::cr95hf::command::set_gain_and_modulation::length, + rfid::cr95hf::settings::arc_b, rfid::cr95hf::settings::flag_increment, + rfid::cr95hf::settings::acr_b_index_for_gain_and_modulation, + rfid::cr95hf::protocol::iso14443A.gain_modulation_values()); EXPECT_CALL(mockBufferedSerial, write).With(Args<0, 1>(expected_values)); } @@ -88,7 +90,7 @@ TEST_F(CoreRFIDReaderTest, registerCallbacks) TEST_F(CoreRFIDReaderTest, setModeTagDetection) { - const auto expected_values = testing::ElementsAreArray(rfid::command::frame::set_tag_detection_mode); + const auto expected_values = testing::ElementsAreArray(rfid::cr95hf::command::frame::set_tag_detection_mode); EXPECT_CALL(mockBufferedSerial, write).With(Args<0, 1>(expected_values)); @@ -110,20 +112,6 @@ TEST_F(CoreRFIDReaderTest, setCommunicationProtocolSuccess) reader.setCommunicationProtocol(rfid::Protocol::ISO14443A); } -// TEST_F(CoreRFIDReaderTest, setCommunicationProtocolFailedOnAnswerTooBig) -// { -// std::array set_protocol_failed_answer = {0x00, 0x00, 0x00}; -// { -// InSequence seq; -// receiveRFIDReaderAnswer(set_protocol_failed_answer); -// sendSetProtocol(); -// sendSetGainAndModulation(); -// } - -// callback_sigio(); -// reader.setCommunicationProtocol(rfid::Protocol::ISO14443A); -// } - TEST_F(CoreRFIDReaderTest, setCommunicationProtocolFailedOnWrongFirstValue) { std::array set_protocol_failed_answer = {0x82, 0x00}; @@ -141,7 +129,8 @@ TEST_F(CoreRFIDReaderTest, setCommunicationProtocolFailedOnWrongFirstValue) TEST_F(CoreRFIDReaderTest, sendCommandSuccess) { std::array command = {0x26, 0x07}; - const auto expected_values = ElementsAre(rfid::command::send_receive, command.size(), command[0], command[1]); + const auto expected_values = + ElementsAre(rfid::cr95hf::command::send_receive, command.size(), command[0], command[1]); EXPECT_CALL(mockBufferedSerial, write).With(Args<0, 1>(expected_values)); diff --git a/libs/RFIDKit/source/RFIDKit.cpp b/libs/RFIDKit/source/RFIDKit.cpp index 66a44542a6..fab28dac35 100644 --- a/libs/RFIDKit/source/RFIDKit.cpp +++ b/libs/RFIDKit/source/RFIDKit.cpp @@ -10,7 +10,7 @@ void RFIDKit::init() { using namespace rfid::sm; - static auto tag_detected_callback = [this]() { state_machine.process_event(event::tag_response_available {}); }; + auto tag_detected_callback = [this]() { state_machine.process_event(event::tag_response_available {}); }; _rfid_reader.registerOnTagDetectedCallback(tag_detected_callback); registerMagicCard(); @@ -23,7 +23,8 @@ void RFIDKit::registerMagicCard() { auto on_magic_card_readable_callback = [this](rfid::Tag &tag) { if (isTagSignatureValid(tag)) { - _card = MagicCard {static_cast((tag.data[4] << 8) + tag.data[5])}; + auto id = utils::memory::combineBytes({.high = tag.data[4], .low = tag.data[5]}); + _card = MagicCard {id}; if (_on_tag_available_callback != nullptr) { _on_tag_available_callback(_card); diff --git a/libs/RFIDKit/tests/ISO14443A_test.cpp b/libs/RFIDKit/tests/ISO14443A_test.cpp index 11b879f687..359f2861d1 100644 --- a/libs/RFIDKit/tests/ISO14443A_test.cpp +++ b/libs/RFIDKit/tests/ISO14443A_test.cpp @@ -5,7 +5,7 @@ #include "ISO14443A.h" #include "gtest/gtest.h" -#include "mocks/leka/CoreRFIDReader.h" +#include "mocks/leka/CoreRFIDReaderCR95HF.h" using namespace leka; using namespace rfid::sm; @@ -19,7 +19,7 @@ class ISO14443ATest : public ::testing::Test // void SetUp() override {} // void TearDown() override {} - mock::CoreRFIDReader mock_reader {}; + mock::CoreRFIDReaderCR95HF mock_reader {}; boost::sml::sm sm {static_cast(mock_reader)}; static constexpr rfid::Command<1> command_requestA = {.data = {0x26}, .flags = leka::rfid::Flag::sb_7}; diff --git a/libs/RFIDKit/tests/RFIDKit_test.cpp b/libs/RFIDKit/tests/RFIDKit_test.cpp index 91e261c7c3..4952fd78c7 100644 --- a/libs/RFIDKit/tests/RFIDKit_test.cpp +++ b/libs/RFIDKit/tests/RFIDKit_test.cpp @@ -5,7 +5,7 @@ #include "RFIDKit.h" #include "gtest/gtest.h" -#include "mocks/leka/CoreRFIDReader.h" +#include "mocks/leka/CoreRFIDReaderCR95HF.h" using namespace leka; @@ -22,7 +22,7 @@ class RFIDKitTest : public ::testing::Test // void TearDown() override {} RFIDKit rfid_kit; - mock::CoreRFIDReader mock_reader {}; + mock::CoreRFIDReaderCR95HF mock_reader {}; MockFunction mock_callback; std::function magic_card_callback {}; diff --git a/spikes/lk_rfid/main.cpp b/spikes/lk_rfid/main.cpp index 860e539ab5..70cea240d0 100644 --- a/spikes/lk_rfid/main.cpp +++ b/spikes/lk_rfid/main.cpp @@ -8,7 +8,7 @@ #include "rtos/ThisThread.h" #include "CoreBufferedSerial.h" -#include "CoreRFIDReader.h" +#include "CoreRFIDReaderCR95HF.h" #include "HelloWorld.h" #include "LogKit.h" #include "RFIDKit.h" @@ -23,7 +23,7 @@ auto main() -> int log_info("Hello, World!\n\n"); auto rfidserial = CoreBufferedSerial(RFID_UART_TX, RFID_UART_RX, 57600); - auto rfidreader = CoreRFIDReader(rfidserial); + auto rfidreader = CoreRFIDReaderCR95HF(rfidserial); auto rfidkit = RFIDKit(rfidreader); rtos::ThisThread::sleep_for(2s); diff --git a/spikes/lk_watchdog_isr/main.cpp b/spikes/lk_watchdog_isr/main.cpp index 82fef22394..ba45281e1e 100644 --- a/spikes/lk_watchdog_isr/main.cpp +++ b/spikes/lk_watchdog_isr/main.cpp @@ -12,7 +12,7 @@ #include "rtos/Thread.h" #include "CoreBufferedSerial.h" -#include "CoreRFIDReader.h" +#include "CoreRFIDReaderCR95HF.h" #include "CriticalSection.h" #include "HelloWorld.h" #include "LogKit.h" @@ -116,7 +116,7 @@ namespace watchdog { namespace rfid { auto serial = CoreBufferedSerial(RFID_UART_TX, RFID_UART_RX, 57600); - auto reader = CoreRFIDReader(serial); + auto reader = CoreRFIDReaderCR95HF(serial); } // namespace rfid diff --git a/tests/unit/mocks/mocks/leka/CoreRFIDReader.h b/tests/unit/mocks/mocks/leka/CoreRFIDReaderCR95HF.h similarity index 93% rename from tests/unit/mocks/mocks/leka/CoreRFIDReader.h rename to tests/unit/mocks/mocks/leka/CoreRFIDReaderCR95HF.h index 30f0589eae..1d186efa1f 100644 --- a/tests/unit/mocks/mocks/leka/CoreRFIDReader.h +++ b/tests/unit/mocks/mocks/leka/CoreRFIDReaderCR95HF.h @@ -9,7 +9,7 @@ namespace leka::mock { -class CoreRFIDReader : public interface::RFIDReader +class CoreRFIDReaderCR95HF : public interface::RFIDReader { public: MOCK_METHOD(void, init, (), (override)); From 1a89dc639671cdcbff62e8b59d6ed1131415c2b0 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 17 Aug 2022 11:57:05 +0200 Subject: [PATCH 035/130] :white_check_mark: (tests): Add MagicCard and ISOUtils tests --- libs/RFIDKit/CMakeLists.txt | 2 + libs/RFIDKit/tests/ISO14443AUtils_test.cpp | 85 ++++++++++++++++++++++ libs/RFIDKit/tests/ISO14443A_test.cpp | 12 +-- libs/RFIDKit/tests/MagicCard_test.cpp | 48 ++++++++++++ 4 files changed, 141 insertions(+), 6 deletions(-) create mode 100644 libs/RFIDKit/tests/ISO14443AUtils_test.cpp create mode 100644 libs/RFIDKit/tests/MagicCard_test.cpp diff --git a/libs/RFIDKit/CMakeLists.txt b/libs/RFIDKit/CMakeLists.txt index 48e0b70cd5..983bbf023e 100644 --- a/libs/RFIDKit/CMakeLists.txt +++ b/libs/RFIDKit/CMakeLists.txt @@ -22,6 +22,8 @@ target_link_libraries(RFIDKit if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") leka_unit_tests_sources( tests/ISO14443A_test.cpp + tests/ISO14443AUtils_test.cpp + tests/MagicCard_test.cpp tests/RFIDKit_test.cpp ) endif() diff --git a/libs/RFIDKit/tests/ISO14443AUtils_test.cpp b/libs/RFIDKit/tests/ISO14443AUtils_test.cpp new file mode 100644 index 0000000000..490a3e3cea --- /dev/null +++ b/libs/RFIDKit/tests/ISO14443AUtils_test.cpp @@ -0,0 +1,85 @@ +// Leka - LekaOS +// Copyright 2021 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "ISO14443A.h" +#include "gtest/gtest.h" +#include "mocks/leka/CoreRFIDReaderCR95HF.h" + +using namespace leka; +using namespace rfid::sm; + +using ::testing::Return; +using ::testing::ReturnRef; + +class ISO14443AUtilsTest : public ::testing::Test +{ + protected: + // void SetUp() override {} + // void TearDown() override {} +}; + +TEST_F(ISO14443AUtilsTest, computeCRCChecksumCorrect1) +{ + auto expected_checksum = std::array {0x67, 0x1A}; + + rfid::Tag tag {.data = std::array {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0xCA, 0x67}}; + + EXPECT_EQ(expected_checksum, rfid::computeCRC(tag.data.data())); +} + +TEST_F(ISO14443AUtilsTest, computeCRCChecksumCorrect2) +{ + auto expected_checksum = std::array {0x37, 0x49}; + + rfid::Tag tag {.data = std::array {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x1A}}; + + EXPECT_EQ(expected_checksum, rfid::computeCRC(tag.data.data())); +} + +TEST_F(ISO14443AUtilsTest, computeCRCChecksumIncorrect) +{ + auto expected_checksum = std::array {0x49, 0x1A}; + + rfid::Tag tag {.data = std::array {0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, + 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, + 0x03, 0x04, 0x49, 0x1A}}; + + EXPECT_NE(expected_checksum, rfid::computeCRC(tag.data.data())); +} + +TEST_F(ISO14443AUtilsTest, checkATQATagCorrect) +{ + rfid::Tag tag {.data = {0x44, 0x00}}; + + EXPECT_TRUE(rfid::checkATQA(tag)); +} + +TEST_F(ISO14443AUtilsTest, checkATQATagIncorrect) +{ + rfid::Tag tag {.data = {0x00, 0x00}}; + + EXPECT_FALSE(rfid::checkATQA(tag)); +} + +TEST_F(ISO14443AUtilsTest, checkRegisterTagCorrect) +{ + rfid::Tag tag {.data = std::array {0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, + 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, + 0x03, 0x04, 0x24, 0xF5}}; + + EXPECT_TRUE(rfid::checkRegister(tag)); +} + +TEST_F(ISO14443AUtilsTest, checkRegisterTagIncorrect) +{ + rfid::Tag tag {.data = std::array {0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, + 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, + 0x03, 0x04, 0x00, 0x00}}; + + EXPECT_FALSE(rfid::checkRegister(tag)); +} diff --git a/libs/RFIDKit/tests/ISO14443A_test.cpp b/libs/RFIDKit/tests/ISO14443A_test.cpp index 359f2861d1..ea7c9a9926 100644 --- a/libs/RFIDKit/tests/ISO14443A_test.cpp +++ b/libs/RFIDKit/tests/ISO14443A_test.cpp @@ -42,7 +42,7 @@ TEST_F(ISO14443ATest, initialState) EXPECT_FALSE(sm.is(state::requesting_tag_data)); } -TEST_F(ISO14443ATest, stateIdleEventTagDetectedGuardIsTagDetectedTrue) +TEST_F(ISO14443ATest, stateIdleEventTagResponseAvailableGuardIsTagDetectedTrue) { sm.set_current_states(state::idle); @@ -53,7 +53,7 @@ TEST_F(ISO14443ATest, stateIdleEventTagDetectedGuardIsTagDetectedTrue) EXPECT_TRUE(sm.is(state::send_reqa)); } -TEST_F(ISO14443ATest, stateSendReqaEventTagDetected) +TEST_F(ISO14443ATest, stateSendReqaEventTagResponseAvailable) { sm.set_current_states(state::send_reqa); @@ -64,7 +64,7 @@ TEST_F(ISO14443ATest, stateSendReqaEventTagDetected) EXPECT_TRUE(sm.is(state::requesting_atqa)); } -TEST_F(ISO14443ATest, stateRequestingAtqaEventTagDetectedGuardIsTrue) +TEST_F(ISO14443ATest, stateRequestingAtqaEventTagResponseAvailableGuardIsTrue) { sm.set_current_states(state::requesting_atqa); @@ -86,7 +86,7 @@ TEST_F(ISO14443ATest, stateRequestingAtqaEventTagDetectedGuardIsTrue) EXPECT_TRUE(sm.is(state::requesting_tag_data)); } -TEST_F(ISO14443ATest, stateRequestingAtqaEventTagDetectedGuardIsFalse) +TEST_F(ISO14443ATest, stateRequestingAtqaEventTagResponseAvailableGuardIsFalse) { sm.set_current_states(state::requesting_atqa); @@ -98,7 +98,7 @@ TEST_F(ISO14443ATest, stateRequestingAtqaEventTagDetectedGuardIsFalse) EXPECT_TRUE(sm.is(state::idle)); } -TEST_F(ISO14443ATest, stateRequestingTagDataEventTagDetectedGuardIsTrue) +TEST_F(ISO14443ATest, stateRequestingTagDataEventTagResponseAvailableGuardIsTrue) { sm.set_current_states(state::requesting_tag_data); @@ -122,7 +122,7 @@ TEST_F(ISO14443ATest, stateRequestingTagDataEventTagDetectedGuardIsTrue) EXPECT_TRUE(sm.is(state::idle)); } -TEST_F(ISO14443ATest, stateRequestingTagDataEventTagDetectedGuardIsFalse) +TEST_F(ISO14443ATest, stateRequestingTagDataEventTagResponseAvailableGuardIsFalse) { sm.set_current_states(state::requesting_tag_data); diff --git a/libs/RFIDKit/tests/MagicCard_test.cpp b/libs/RFIDKit/tests/MagicCard_test.cpp new file mode 100644 index 0000000000..1732893e2b --- /dev/null +++ b/libs/RFIDKit/tests/MagicCard_test.cpp @@ -0,0 +1,48 @@ +// Leka - LekaOS +// Copyright 2021 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "MagicCard.h" + +#include "gtest/gtest.h" + +using namespace leka; + +class MagicCardTest : public ::testing::Test +{ + protected: + // void SetUp() override {} + // void TearDown() override {} + + std::array id {}; + std::array sak {}; + std::array data {0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + rfid::Tag tag {id, sak, data}; + static constexpr uint16_t number_ten_id = 0x00'14; + + MagicCard pre_implemented_number_10 = MagicCard::number_10; + MagicCard number_10_by_tag = MagicCard(tag); + MagicCard number_10_by_id = MagicCard(number_ten_id); +}; + +TEST_F(MagicCardTest, initialization) +{ + ASSERT_NE(&pre_implemented_number_10, nullptr); +} + +TEST_F(MagicCardTest, PreImplementedEqualToById) +{ + EXPECT_EQ(pre_implemented_number_10, number_10_by_id); +} + +TEST_F(MagicCardTest, PreImplementedEqualToByTag) +{ + EXPECT_EQ(pre_implemented_number_10, number_10_by_tag); +} + +TEST_F(MagicCardTest, getId) +{ + EXPECT_EQ(pre_implemented_number_10.getId(), number_ten_id); +} From 636ab0aa855ab4003519016a19f602fe11868fb6 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 17 Aug 2022 15:50:42 +0200 Subject: [PATCH 036/130] :truck: (typos): Fix typos of comments --- drivers/CoreRFIDReader/include/CoreRFIDReaderCR95HF.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/CoreRFIDReader/include/CoreRFIDReaderCR95HF.h b/drivers/CoreRFIDReader/include/CoreRFIDReaderCR95HF.h index a4a9f6eb0b..cc35f173fe 100644 --- a/drivers/CoreRFIDReader/include/CoreRFIDReaderCR95HF.h +++ b/drivers/CoreRFIDReader/include/CoreRFIDReaderCR95HF.h @@ -2,8 +2,8 @@ // Copyright 2022 APF France handicap // SPDX-License-Identifier: Apache-2.0 -//? CR95HF RFID reader datasheet available at: -//? https://www.st.com/resource/en/datasheet/cr95hf.pdf +// ? CR95HF RFID reader datasheet available at: +// ? https://www.st.com/resource/en/datasheet/cr95hf.pdf #pragma once From db1220fb9a2650bef9d871e82fb8b93a883cafcb Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 17 Aug 2022 15:52:44 +0200 Subject: [PATCH 037/130] :clown_face: (mocks): Rename RFIDReader mock --- libs/RFIDKit/tests/ISO14443AUtils_test.cpp | 50 ++++++++----------- libs/RFIDKit/tests/ISO14443A_test.cpp | 4 +- libs/RFIDKit/tests/RFIDKit_test.cpp | 4 +- ...oreRFIDReaderCR95HF.h => CoreRFIDReader.h} | 2 +- 4 files changed, 25 insertions(+), 35 deletions(-) rename tests/unit/mocks/mocks/leka/{CoreRFIDReaderCR95HF.h => CoreRFIDReader.h} (93%) diff --git a/libs/RFIDKit/tests/ISO14443AUtils_test.cpp b/libs/RFIDKit/tests/ISO14443AUtils_test.cpp index 490a3e3cea..a3db5b3896 100644 --- a/libs/RFIDKit/tests/ISO14443AUtils_test.cpp +++ b/libs/RFIDKit/tests/ISO14443AUtils_test.cpp @@ -4,13 +4,8 @@ #include "ISO14443A.h" #include "gtest/gtest.h" -#include "mocks/leka/CoreRFIDReaderCR95HF.h" -using namespace leka; -using namespace rfid::sm; - -using ::testing::Return; -using ::testing::ReturnRef; +using namespace leka::rfid; class ISO14443AUtilsTest : public ::testing::Test { @@ -23,63 +18,58 @@ TEST_F(ISO14443AUtilsTest, computeCRCChecksumCorrect1) { auto expected_checksum = std::array {0x67, 0x1A}; - rfid::Tag tag {.data = std::array {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0xCA, 0x67}}; + Tag tag {.data = std::array {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xCA, 0x67}}; - EXPECT_EQ(expected_checksum, rfid::computeCRC(tag.data.data())); + EXPECT_EQ(expected_checksum, computeCRC(tag.data.data())); } TEST_F(ISO14443AUtilsTest, computeCRCChecksumCorrect2) { auto expected_checksum = std::array {0x37, 0x49}; - rfid::Tag tag {.data = std::array {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x49, 0x1A}}; + Tag tag {.data = std::array {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x1A}}; - EXPECT_EQ(expected_checksum, rfid::computeCRC(tag.data.data())); + EXPECT_EQ(expected_checksum, computeCRC(tag.data.data())); } TEST_F(ISO14443AUtilsTest, computeCRCChecksumIncorrect) { auto expected_checksum = std::array {0x49, 0x1A}; - rfid::Tag tag {.data = std::array {0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, - 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, - 0x03, 0x04, 0x49, 0x1A}}; + Tag tag {.data = std::array {0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, + 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x49, 0x1A}}; - EXPECT_NE(expected_checksum, rfid::computeCRC(tag.data.data())); + EXPECT_NE(expected_checksum, computeCRC(tag.data.data())); } TEST_F(ISO14443AUtilsTest, checkATQATagCorrect) { - rfid::Tag tag {.data = {0x44, 0x00}}; + Tag tag {.data = {0x44, 0x00}}; - EXPECT_TRUE(rfid::checkATQA(tag)); + EXPECT_TRUE(checkATQA(tag)); } TEST_F(ISO14443AUtilsTest, checkATQATagIncorrect) { - rfid::Tag tag {.data = {0x00, 0x00}}; + Tag tag {.data = {0x00, 0x00}}; - EXPECT_FALSE(rfid::checkATQA(tag)); + EXPECT_FALSE(checkATQA(tag)); } TEST_F(ISO14443AUtilsTest, checkRegisterTagCorrect) { - rfid::Tag tag {.data = std::array {0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, - 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, - 0x03, 0x04, 0x24, 0xF5}}; + Tag tag {.data = std::array {0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, + 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x24, 0xF5}}; - EXPECT_TRUE(rfid::checkRegister(tag)); + EXPECT_TRUE(checkRegister(tag)); } TEST_F(ISO14443AUtilsTest, checkRegisterTagIncorrect) { - rfid::Tag tag {.data = std::array {0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, - 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, - 0x03, 0x04, 0x00, 0x00}}; + Tag tag {.data = std::array {0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, + 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x00, 0x00}}; - EXPECT_FALSE(rfid::checkRegister(tag)); + EXPECT_FALSE(checkRegister(tag)); } diff --git a/libs/RFIDKit/tests/ISO14443A_test.cpp b/libs/RFIDKit/tests/ISO14443A_test.cpp index ea7c9a9926..7b67baafee 100644 --- a/libs/RFIDKit/tests/ISO14443A_test.cpp +++ b/libs/RFIDKit/tests/ISO14443A_test.cpp @@ -5,7 +5,7 @@ #include "ISO14443A.h" #include "gtest/gtest.h" -#include "mocks/leka/CoreRFIDReaderCR95HF.h" +#include "mocks/leka/CoreRFIDReader.h" using namespace leka; using namespace rfid::sm; @@ -19,7 +19,7 @@ class ISO14443ATest : public ::testing::Test // void SetUp() override {} // void TearDown() override {} - mock::CoreRFIDReaderCR95HF mock_reader {}; + mock::CoreRFIDReader mock_reader {}; boost::sml::sm sm {static_cast(mock_reader)}; static constexpr rfid::Command<1> command_requestA = {.data = {0x26}, .flags = leka::rfid::Flag::sb_7}; diff --git a/libs/RFIDKit/tests/RFIDKit_test.cpp b/libs/RFIDKit/tests/RFIDKit_test.cpp index 4952fd78c7..91e261c7c3 100644 --- a/libs/RFIDKit/tests/RFIDKit_test.cpp +++ b/libs/RFIDKit/tests/RFIDKit_test.cpp @@ -5,7 +5,7 @@ #include "RFIDKit.h" #include "gtest/gtest.h" -#include "mocks/leka/CoreRFIDReaderCR95HF.h" +#include "mocks/leka/CoreRFIDReader.h" using namespace leka; @@ -22,7 +22,7 @@ class RFIDKitTest : public ::testing::Test // void TearDown() override {} RFIDKit rfid_kit; - mock::CoreRFIDReaderCR95HF mock_reader {}; + mock::CoreRFIDReader mock_reader {}; MockFunction mock_callback; std::function magic_card_callback {}; diff --git a/tests/unit/mocks/mocks/leka/CoreRFIDReaderCR95HF.h b/tests/unit/mocks/mocks/leka/CoreRFIDReader.h similarity index 93% rename from tests/unit/mocks/mocks/leka/CoreRFIDReaderCR95HF.h rename to tests/unit/mocks/mocks/leka/CoreRFIDReader.h index 1d186efa1f..30f0589eae 100644 --- a/tests/unit/mocks/mocks/leka/CoreRFIDReaderCR95HF.h +++ b/tests/unit/mocks/mocks/leka/CoreRFIDReader.h @@ -9,7 +9,7 @@ namespace leka::mock { -class CoreRFIDReaderCR95HF : public interface::RFIDReader +class CoreRFIDReader : public interface::RFIDReader { public: MOCK_METHOD(void, init, (), (override)); From 431344847c6fc6dbfb649b6f97ac79ae23d6afa7 Mon Sep 17 00:00:00 2001 From: Mourad Latoundji Date: Wed, 20 Jul 2022 11:34:12 +0200 Subject: [PATCH 038/130] :clown_face: (mocks): CoreI2C - Fix #include Include interface instead of implementation --- tests/unit/mocks/mocks/leka/CoreI2C.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/mocks/mocks/leka/CoreI2C.h b/tests/unit/mocks/mocks/leka/CoreI2C.h index 0ec4bff3c7..96c42e5ebf 100644 --- a/tests/unit/mocks/mocks/leka/CoreI2C.h +++ b/tests/unit/mocks/mocks/leka/CoreI2C.h @@ -4,8 +4,8 @@ #pragma once -#include "CoreI2C.h" #include "gmock/gmock.h" +#include "interface/drivers/I2C.h" namespace leka::mock { From 35239e528243f87bc0d6f05a8102d126dfb0825e Mon Sep 17 00:00:00 2001 From: Mourad Latoundji Date: Thu, 18 Aug 2022 18:06:00 +0200 Subject: [PATCH 039/130] :sparkles: (interface): Add QDAC --- include/interface/drivers/QDAC.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 include/interface/drivers/QDAC.h diff --git a/include/interface/drivers/QDAC.h b/include/interface/drivers/QDAC.h new file mode 100644 index 0000000000..dcbfb88107 --- /dev/null +++ b/include/interface/drivers/QDAC.h @@ -0,0 +1,22 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include + +namespace leka::interface { +class QDAC +{ + public: + virtual ~QDAC() = default; + + virtual void init() = 0; + virtual void write(uint8_t channel, uint16_t data) = 0; + virtual auto read(uint8_t channel) -> uint16_t = 0; +}; + +} // namespace leka::interface From 525aa76031efb6d568f940b4893cc3b1f6e72bba Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Thu, 18 Aug 2022 18:12:24 +0200 Subject: [PATCH 040/130] :sparkles: (drivers): Add CoreQDAC driver w/ tests --- drivers/CMakeLists.txt | 3 + drivers/CoreQDAC/CMakeLists.txt | 26 +++ drivers/CoreQDAC/include/CoreQDAC.h | 51 ++++++ drivers/CoreQDAC/include/external/MCP4728.h | 138 ++++++++++++++++ drivers/CoreQDAC/source/CoreQDAC.cpp | 168 ++++++++++++++++++++ drivers/CoreQDAC/tests/CoreQDAC_test.cpp | 124 +++++++++++++++ tests/unit/CMakeLists.txt | 1 + 7 files changed, 511 insertions(+) create mode 100644 drivers/CoreQDAC/CMakeLists.txt create mode 100644 drivers/CoreQDAC/include/CoreQDAC.h create mode 100644 drivers/CoreQDAC/include/external/MCP4728.h create mode 100644 drivers/CoreQDAC/source/CoreQDAC.cpp create mode 100644 drivers/CoreQDAC/tests/CoreQDAC_test.cpp diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 26e1b4db72..f3154b563c 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -39,3 +39,6 @@ add_subdirectory(${DRIVERS_DIR}/CoreMotor) # Touch drivers add_subdirectory(${DRIVERS_DIR}/CoreIOExpander) +add_subdirectory(${DRIVERS_DIR}/CoreQDAC) + + diff --git a/drivers/CoreQDAC/CMakeLists.txt b/drivers/CoreQDAC/CMakeLists.txt new file mode 100644 index 0000000000..a9bb657d92 --- /dev/null +++ b/drivers/CoreQDAC/CMakeLists.txt @@ -0,0 +1,26 @@ +# Leka - LekaOS +# Copyright 2022 APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +add_library(CoreQDAC STATIC) + +target_include_directories(CoreQDAC + PUBLIC + include +) + +target_sources(CoreQDAC + PRIVATE + source/CoreQDAC.cpp +) + +target_link_libraries(CoreQDAC + mbed-os + CoreI2C +) + +if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") + leka_unit_tests_sources( + tests/CoreQDAC_test.cpp + ) +endif() diff --git a/drivers/CoreQDAC/include/CoreQDAC.h b/drivers/CoreQDAC/include/CoreQDAC.h new file mode 100644 index 0000000000..00f2ebd349 --- /dev/null +++ b/drivers/CoreQDAC/include/CoreQDAC.h @@ -0,0 +1,51 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +#include "external/MCP4728.h" +#include "interface/drivers/I2C.h" +#include "interface/drivers/QDAC.h" + +namespace leka { + +class CoreQDACMCP4728 : public interface::QDAC +{ + public: + CoreQDACMCP4728(interface::I2C &i2c, uint8_t address) : _i2c(i2c), _address(address) {}; + + void init() final; + + void write(uint8_t channel, uint16_t data) final; + auto read(uint8_t channel) -> uint16_t final; + + private: + void writeInputRegisters(); + void readInputRegisters(); + + void setVoltageReference(uint8_t data); + void setPowerDown(uint8_t data); + void setGain(uint8_t data); + + interface::I2C &_i2c; + uint8_t _address; + + struct QDACInputData { + uint8_t vref = 0x00; + uint8_t pd = 0x00; + uint8_t gain = 0x00; + uint16_t data = 0x0000; + }; + + std::array _tx_registers {}; + std::array _rx_registers {}; + + static constexpr std::array _channels {mcp4728::channel::A, mcp4728::channel::B, mcp4728::channel::C, + mcp4728::channel::D}; +}; + +} // namespace leka diff --git a/drivers/CoreQDAC/include/external/MCP4728.h b/drivers/CoreQDAC/include/external/MCP4728.h new file mode 100644 index 0000000000..a6ce453b71 --- /dev/null +++ b/drivers/CoreQDAC/include/external/MCP4728.h @@ -0,0 +1,138 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +// Source: https://www.dropbox.com/home/Development/hardware/electronics/datasheets?preview=Aceltis-Flex-DAC-MCP4728.pdf + +#pragma once + +#include + +#include "cstdint" + +namespace leka::mcp4728 { + +namespace command { + + inline constexpr auto fast_write = uint8_t {0x00}; + inline constexpr auto multi_write = uint8_t {0x40}; + inline constexpr auto sequential_write = uint8_t {0x50}; + inline constexpr auto single_write = uint8_t {0x58}; + + inline constexpr auto set_vref = uint8_t {0x80}; + inline constexpr auto set_power_down = uint8_t {0xA0}; + inline constexpr auto set_gain = uint8_t {0xC0}; + + namespace read { + + inline constexpr auto buffer_size = std::size_t {24}; + + } // namespace read + +} // namespace command + +namespace channel { + + inline constexpr auto A = uint8_t {0x00}; + inline constexpr auto B = uint8_t {0x01}; + inline constexpr auto C = uint8_t {0x02}; + inline constexpr auto D = uint8_t {0x03}; + +} // namespace channel + +namespace data { + + namespace voltage_reference { + + inline constexpr auto Vdd = uint8_t {0x00}; + + namespace internal { + + namespace channel { + + inline constexpr auto A = uint8_t {0x08}; + inline constexpr auto B = uint8_t {0x04}; + inline constexpr auto C = uint8_t {0x02}; + inline constexpr auto D = uint8_t {0x01}; + + } // namespace channel + + inline constexpr auto all = uint8_t {0x0f}; + + } // namespace internal + + } // namespace voltage_reference + + namespace power_down { + + inline constexpr auto normal = uint8_t {0x00}; + + namespace channel { + + namespace A { + + inline constexpr auto normal = uint8_t {0x00}; + inline constexpr auto powerDown1K = uint8_t {0x40}; + inline constexpr auto powerDown100K = uint8_t {0x80}; + inline constexpr auto powerDown500K = uint8_t {0xC0}; + + } // namespace A + + namespace B { + + inline constexpr auto normal = uint8_t {0x00}; + inline constexpr auto powerDown1K = uint8_t {0x10}; + inline constexpr auto powerDown100K = uint8_t {0x20}; + inline constexpr auto powerDown500K = uint8_t {0x30}; + + } // namespace B + + namespace C { + + inline constexpr auto normal = uint8_t {0x00}; + inline constexpr auto powerDown1K = uint8_t {0x04}; + inline constexpr auto powerDown100K = uint8_t {0x08}; + inline constexpr auto powerDown500K = uint8_t {0x0C}; + + } // namespace C + + namespace D { + + inline constexpr auto normal = uint8_t {0x00}; + inline constexpr auto powerDown1K = uint8_t {0x01}; + inline constexpr auto powerDown100K = uint8_t {0x02}; + inline constexpr auto powerDown500K = uint8_t {0x03}; + + } // namespace D + + } // namespace channel + } // namespace power_down + + namespace gain { + + namespace x1 { + + inline constexpr auto all = uint8_t {0x00}; + + } + + namespace x2 { + + namespace channel { + + inline constexpr auto A = uint8_t {0x08}; + inline constexpr auto B = uint8_t {0x04}; + inline constexpr auto C = uint8_t {0x02}; + inline constexpr auto D = uint8_t {0x01}; + + } // namespace channel + + inline constexpr auto all = uint8_t {0x0f}; + + } // namespace x2 + + } // namespace gain + +} // namespace data + +} // namespace leka::mcp4728 diff --git a/drivers/CoreQDAC/source/CoreQDAC.cpp b/drivers/CoreQDAC/source/CoreQDAC.cpp new file mode 100644 index 0000000000..84841f241b --- /dev/null +++ b/drivers/CoreQDAC/source/CoreQDAC.cpp @@ -0,0 +1,168 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "CoreQDAC.h" +#include +#include + +#include "MemoryUtils.h" + +using namespace leka; + +void CoreQDACMCP4728::init() +{ + setVoltageReference(mcp4728::data::voltage_reference::Vdd); + setPowerDown(mcp4728::data::power_down::normal); + setGain(mcp4728::data::gain::x1::all); +} + +void CoreQDACMCP4728::write(uint8_t channel, uint16_t data) +{ + const auto *it = std::find(std::begin(_channels), std::end(_channels), channel); + + if (it != std::end(_channels)) { + _tx_registers.at(channel).data = data; + writeInputRegisters(); + } +} + +auto CoreQDACMCP4728::read(uint8_t channel) -> uint16_t +{ + if (const auto *it = std::find(std::begin(_channels), std::end(_channels), channel); it != std::end(_channels)) { + readInputRegisters(); + return _rx_registers.at(channel).data; + } + + return 0; +} + +void CoreQDACMCP4728::writeInputRegisters() +{ + const auto number_of_bytes_per_channel = uint8_t {3}; + const auto command_size = number_of_bytes_per_channel * _channels.size(); + auto command = std::array {}; + + auto compute_first_byte_for_channel = [&command, this](auto channel) { + command.at(channel * 3) = static_cast(mcp4728::command::multi_write | ((0x03 & channel) << 1)); + }; + + auto compute_second_byte_for_channel = [&command, this](auto channel) { + command.at(channel * 3 + 1) = static_cast( + _tx_registers.at(channel).vref << 7 | _tx_registers.at(channel).pd << 5 | + _tx_registers.at(channel).gain << 4 | (0x0F & utils::memory::getHighByte(_tx_registers.at(channel).data))); + }; + + auto compute_third_byte_for_channel = [&command, this](auto channel) { + command.at(channel * 3 + 2) = utils::memory::getLowByte(_tx_registers.at(channel).data); + }; + + for (auto channel: _channels) { + compute_first_byte_for_channel(channel); + compute_second_byte_for_channel(channel); + compute_third_byte_for_channel(channel); + } + + _i2c.write(_address, command.data(), command.size(), false); +} + +void CoreQDACMCP4728::setVoltageReference(uint8_t data) +{ + auto compute_vref_for_channel = [data](auto channel) { + auto vref = 1 & (data >> (3 - channel)); + return vref; + }; + + auto set_vref_for_all_channels = [&] { + for (auto channel: _channels) { + auto vref = compute_vref_for_channel(channel); + _tx_registers.at(channel).vref = static_cast(vref); + } + }; + + set_vref_for_all_channels(); + + auto command = static_cast(mcp4728::command::set_vref | (0x0F & data)); + + _i2c.write(_address, &command, 1, false); +} + +void CoreQDACMCP4728::setPowerDown(uint8_t data) +{ + auto compute_power_down_for_channel = [data](auto channel) { + auto power_down = 3 & (data >> (6 - 2 * channel)); + return power_down; + }; + + auto set_power_down_for_all_channels = [&] { + for (auto channel: _channels) { + auto power_down = compute_power_down_for_channel(channel); + _tx_registers.at(channel).pd = static_cast(power_down); + } + }; + + set_power_down_for_all_channels(); + + auto command = std::array {}; + + command.at(0) = static_cast(mcp4728::command::set_power_down | (0xF0 & data) >> 4); + command.at(1) = static_cast((0x0F & data) << 4); + + _i2c.write(_address, command.data(), command.size(), false); +} + +void CoreQDACMCP4728::setGain(uint8_t data) +{ + auto compute_gain_for_channel = [data](auto channel) { + auto gain = 1 & (data >> (3 - channel)); + return gain; + }; + + auto set_gain_for_all_channels = [&] { + for (auto channel: _channels) { + auto gain = compute_gain_for_channel(channel); + _tx_registers.at(channel).gain = static_cast(gain); + } + }; + set_gain_for_all_channels(); + auto command = static_cast(mcp4728::command::set_gain | (0x0F & data)); + _i2c.write(_address, &command, 1, false); +} + +void CoreQDACMCP4728::readInputRegisters() +{ + auto buffer = std::array {}; + + _i2c.read(_address, buffer.data(), buffer.size(), false); + + auto compute_vref_for_channel = [&buffer](auto channel) { + auto vref = (buffer.at(channel * 6 + 1) & 0x80) >> 7; + return vref; + }; + + auto compute_power_down_for_channel = [&buffer](auto channel) { + auto power_down = (buffer.at(channel * 6 + 1) & 0x60) >> 5; + return power_down; + }; + + auto compute_gain_for_channel = [&buffer](auto channel) { + auto gain = (buffer.at(channel * 6 + 1) & 0x10) >> 4; + return gain; + }; + + auto compute_data_for_channel = [&buffer](auto channel) { + auto data = ((buffer.at(channel * 6 + 1) & 0x0F) << 8) | buffer.at(channel * 6 + 2); + return data; + }; + + auto set_input_data_for_all_channels = [&] { + for (auto channel: _channels) { + _rx_registers.at(channel).vref = static_cast(compute_vref_for_channel(channel)); + _rx_registers.at(channel).pd = static_cast(compute_power_down_for_channel(channel)); + _rx_registers.at(channel).gain = static_cast(compute_gain_for_channel(channel)); + _rx_registers.at(channel).data = static_cast(compute_data_for_channel(channel)); + } + }; + + set_input_data_for_all_channels(); +} diff --git a/drivers/CoreQDAC/tests/CoreQDAC_test.cpp b/drivers/CoreQDAC/tests/CoreQDAC_test.cpp new file mode 100644 index 0000000000..004b5fd5c4 --- /dev/null +++ b/drivers/CoreQDAC/tests/CoreQDAC_test.cpp @@ -0,0 +1,124 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "CoreQDAC.h" + +#include "MemoryUtils.h" +#include "external/MCP4728.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "mocks/leka/CoreI2C.h" + +using namespace leka; + +using ::testing::Args; +using ::testing::DoAll; +using ::testing::ElementsAre; +using ::testing::ElementsAreArray; +using ::testing::InSequence; +using ::testing::Return; +using ::testing::SetArrayArgument; + +class CoreQDACTest : public ::testing::Test + +{ + protected: + // void SetUp() override {} + // void TearDown() override {} + + const uint8_t i2c_address {0xC0}; + mock::CoreI2C mocki2c; + CoreQDACMCP4728 dac {mocki2c, i2c_address}; +}; + +TEST_F(CoreQDACTest, initializationDefault) +{ + auto new_dac = CoreQDACMCP4728 {mocki2c, i2c_address}; + ASSERT_NE(&new_dac, nullptr); +} + +TEST_F(CoreQDACTest, init) +{ + const auto expected_vref = static_cast(mcp4728::command::set_vref | 0x00); + const auto expected_buffer_vref = ElementsAre(expected_vref); + + EXPECT_CALL(mocki2c, write).With(Args<1, 2>(expected_buffer_vref)); + + const auto expected_pd_first_byte = static_cast(mcp4728::command::set_power_down | 0x00); + const auto expected_pd_second_byte = uint8_t {0x00}; + const auto expected_buffer_power_down = ElementsAre(expected_pd_first_byte, expected_pd_second_byte); + + EXPECT_CALL(mocki2c, write).With(Args<1, 2>(expected_buffer_power_down)); + + const auto expected_gain = static_cast(mcp4728::command::set_gain | 0x00); + const auto expected_buffer_gain = ElementsAre(expected_gain); + + EXPECT_CALL(mocki2c, write).With(Args<1, 2>(expected_buffer_gain)); + + dac.init(); +} + +TEST_F(CoreQDACTest, write) +{ + auto value_to_write = uint16_t {0x0ABC}; + + auto command = std::array {}; + + command.at(3 * mcp4728::channel::A) = + static_cast(mcp4728::command::multi_write | mcp4728::channel::A << 1); + command.at(3 * mcp4728::channel::A + 1) = 0x00 | (0x0F & utils::memory::getHighByte(0x00)); + command.at(3 * mcp4728::channel::A + 2) = utils::memory::getLowByte(0x00); + + command.at(3 * mcp4728::channel::B) = + static_cast(mcp4728::command::multi_write | mcp4728::channel::B << 1); + command.at(3 * mcp4728::channel::B + 1) = 0x00 | (0x0F & utils::memory::getHighByte(value_to_write)); + command.at(3 * mcp4728::channel::B + 2) = utils::memory::getLowByte(value_to_write); + + command.at(3 * mcp4728::channel::C) = + static_cast(mcp4728::command::multi_write | mcp4728::channel::C << 1); + command.at(3 * mcp4728::channel::C + 1) = 0x00 | (0x0F & utils::memory::getHighByte(0x00)); + command.at(3 * mcp4728::channel::C + 2) = utils::memory::getLowByte(0x00); + + command.at(3 * mcp4728::channel::D) = + static_cast(mcp4728::command::multi_write | mcp4728::channel::D << 1); + command.at(3 * mcp4728::channel::D + 1) = 0x00 | (0x0F & utils::memory::getHighByte(0x00)); + command.at(3 * mcp4728::channel::D + 2) = utils::memory::getLowByte(0x00); + + const auto expected_buffer = ElementsAreArray(command); + + EXPECT_CALL(mocki2c, write).With(Args<1, 2>(expected_buffer)).Times(1); + + dac.write(mcp4728::channel::B, value_to_write); +} + +TEST_F(CoreQDACTest, writeOutOfBounds) +{ + auto value_to_write = uint16_t {0x0ABC}; + + auto out_channel = uint8_t {5}; + dac.write(out_channel, value_to_write); +} + +TEST_F(CoreQDACTest, read) +{ + auto expected_buffer = std::array {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; + auto expected_data = static_cast(((expected_buffer.at(1) & 0x0F) << 8) | expected_buffer.at(2)); + + EXPECT_CALL(mocki2c, read) + .WillOnce(DoAll(SetArrayArgument<1>(begin(expected_buffer), end(expected_buffer)), Return(0))); + + auto data = dac.read(mcp4728::channel::A); + + ASSERT_EQ(expected_data, data); +} + +TEST_F(CoreQDACTest, readOutOfBounds) +{ + auto expected_data = uint16_t {0}; + + auto out_channel = uint8_t {5}; + auto data = dac.read(out_channel); + + ASSERT_EQ(expected_data, data); +} diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index e8447fbf46..413fafea2e 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -244,6 +244,7 @@ add_subdirectory(template) # Register drivers leka_register_unit_tests_for_driver(CoreBattery) leka_register_unit_tests_for_driver(CoreBufferedSerial) +leka_register_unit_tests_for_driver(CoreQDAC) leka_register_unit_tests_for_driver(CoreEventFlags) leka_register_unit_tests_for_driver(CoreEventQueue) leka_register_unit_tests_for_driver(CoreFlashMemory) From 0933a53a98ab80b771738297bacca4bca70659c9 Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Thu, 18 Aug 2022 18:11:03 +0200 Subject: [PATCH 041/130] :clown_face: (mocks): Add mock::CoreQDAC --- tests/unit/mocks/mocks/leka/CoreQDAC.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/unit/mocks/mocks/leka/CoreQDAC.h diff --git a/tests/unit/mocks/mocks/leka/CoreQDAC.h b/tests/unit/mocks/mocks/leka/CoreQDAC.h new file mode 100644 index 0000000000..ba1c3af07c --- /dev/null +++ b/tests/unit/mocks/mocks/leka/CoreQDAC.h @@ -0,0 +1,20 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "gmock/gmock.h" +#include "interface/drivers/QDAC.h" + +namespace leka::mock { + +class CoreQDAC : public interface::QDAC +{ + public: + MOCK_METHOD(void, init, (), (override)); + MOCK_METHOD(void, write, (uint8_t, uint16_t), (override)); + MOCK_METHOD(uint16_t, read, (uint8_t), (override)); +}; + +} // namespace leka::mock From 7e3b556c6f45a776734f7a4e24b83e31dbc84d0f Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Fri, 19 Aug 2022 13:19:12 +0200 Subject: [PATCH 042/130] :dizzy: (BehaviorKit): Add ChooseActivity behavior --- libs/BehaviorKit/include/BehaviorKit.h | 1 + libs/BehaviorKit/source/BehaviorKit.cpp | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/libs/BehaviorKit/include/BehaviorKit.h b/libs/BehaviorKit/include/BehaviorKit.h index 6f7872797a..e46f970918 100644 --- a/libs/BehaviorKit/include/BehaviorKit.h +++ b/libs/BehaviorKit/include/BehaviorKit.h @@ -43,6 +43,7 @@ class BehaviorKit void bleConnection(bool with_video); void working(); + void chooseActivity(); void stop(); diff --git a/libs/BehaviorKit/source/BehaviorKit.cpp b/libs/BehaviorKit/source/BehaviorKit.cpp index c8fe7f2b7e..1643f79e94 100644 --- a/libs/BehaviorKit/source/BehaviorKit.cpp +++ b/libs/BehaviorKit/source/BehaviorKit.cpp @@ -133,6 +133,11 @@ void BehaviorKit::working() _videokit.displayImage("/fs/home/img/system/robot-face-smiling-slightly.jpg"); } +void BehaviorKit::chooseActivity() +{ + _videokit.displayImage("/fs/home/img/system/robot-misc-choose_activity-fr_FR.jpg"); +} + void BehaviorKit::stop() { _ledkit.stop(); From 49097d97d43df3bd8a175f0deb4fe091804018c4 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Fri, 19 Aug 2022 16:14:51 +0200 Subject: [PATCH 043/130] :white_check_mark: (BehaviorKit): Add chooseActivity test --- libs/BehaviorKit/tests/BehaviorKit_test.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/BehaviorKit/tests/BehaviorKit_test.cpp b/libs/BehaviorKit/tests/BehaviorKit_test.cpp index d9663682d2..6a7304a23a 100644 --- a/libs/BehaviorKit/tests/BehaviorKit_test.cpp +++ b/libs/BehaviorKit/tests/BehaviorKit_test.cpp @@ -177,6 +177,12 @@ TEST_F(BehaviorKitTest, working) behaviorkit.working(); } +TEST_F(BehaviorKitTest, chooseActivity) +{ + EXPECT_CALL(mock_videokit, displayImage); + behaviorkit.chooseActivity(); +} + TEST_F(BehaviorKitTest, stop) { auto speed = 0.5; From 5af23f63cae7af1f2dbe28bacffaad6827505190 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Fri, 19 Aug 2022 13:16:00 +0200 Subject: [PATCH 044/130] :bug: (MagicCard): Fix remote cards --- libs/RFIDKit/include/MagicCard.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/RFIDKit/include/MagicCard.h b/libs/RFIDKit/include/MagicCard.h index f8da96417b..26602f9795 100644 --- a/libs/RFIDKit/include/MagicCard.h +++ b/libs/RFIDKit/include/MagicCard.h @@ -156,8 +156,8 @@ constexpr MagicCard MagicCard::activity_colors_and_sounds = MagicCard {0x00'1E}; constexpr MagicCard MagicCard::activity_magic_objects = MagicCard {0x00'1F}; constexpr MagicCard MagicCard::activity_dance_freeze = MagicCard {0x00'20}; -constexpr MagicCard MagicCard::activity_magic_objects = MagicCard {0x00'21}; -constexpr MagicCard MagicCard::activity_dance_freeze = MagicCard {0x00'22}; +constexpr MagicCard MagicCard::remote_standard = MagicCard {0x00'21}; +constexpr MagicCard MagicCard::remote_colored_arrows = MagicCard {0x00'22}; constexpr MagicCard MagicCard::reinforcer_1_blink_green = MagicCard {0x00'23}; constexpr MagicCard MagicCard::reinforcer_2_spin_blink = MagicCard {0x00'24}; From b696a2ea915e4e2ede89e9d66e813e803a3cbb42 Mon Sep 17 00:00:00 2001 From: Sibylle Malonga Date: Fri, 29 Jul 2022 20:11:27 +0200 Subject: [PATCH 045/130] :sparkles: (libs): Add GameKit lib + tests --- libs/CMakeLists.txt | 1 + libs/GameKit/CMakeLists.txt | 26 +++++++ libs/GameKit/include/Game.h | 21 ++++++ libs/GameKit/include/GameKit.h | 29 ++++++++ libs/GameKit/source/GameKit.cpp | 45 ++++++++++++ libs/GameKit/tests/GameKit_test.cpp | 108 ++++++++++++++++++++++++++++ libs/GameKit/tests/mocks/Game.h | 51 +++++++++++++ tests/unit/CMakeLists.txt | 1 + 8 files changed, 282 insertions(+) create mode 100644 libs/GameKit/CMakeLists.txt create mode 100644 libs/GameKit/include/Game.h create mode 100644 libs/GameKit/include/GameKit.h create mode 100644 libs/GameKit/source/GameKit.cpp create mode 100644 libs/GameKit/tests/GameKit_test.cpp create mode 100644 libs/GameKit/tests/mocks/Game.h diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index 58b3e88ff9..6e2e3abf87 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -12,6 +12,7 @@ add_subdirectory(${LIBS_DIR}/ContainerKit) add_subdirectory(${LIBS_DIR}/EventLoopKit) add_subdirectory(${LIBS_DIR}/FileManagerKit) add_subdirectory(${LIBS_DIR}/FirmwareKit) +add_subdirectory(${LIBS_DIR}/GameKit) add_subdirectory(${LIBS_DIR}/IOKit) add_subdirectory(${LIBS_DIR}/LedKit) add_subdirectory(${LIBS_DIR}/RobotKit) diff --git a/libs/GameKit/CMakeLists.txt b/libs/GameKit/CMakeLists.txt new file mode 100644 index 0000000000..dbb1f46384 --- /dev/null +++ b/libs/GameKit/CMakeLists.txt @@ -0,0 +1,26 @@ +# Leka - LekaOS +# Copyright 2022 APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +add_library(GameKit STATIC) + +target_include_directories(GameKit + PUBLIC + include + include/games +) + +target_sources(GameKit + PRIVATE + source/GameKit.cpp +) + +target_link_libraries(GameKit + EventLoopKit +) + +if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") + leka_unit_tests_sources( + tests/GameKit_test.cpp + ) +endif() diff --git a/libs/GameKit/include/Game.h b/libs/GameKit/include/Game.h new file mode 100644 index 0000000000..3491681d5b --- /dev/null +++ b/libs/GameKit/include/Game.h @@ -0,0 +1,21 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +namespace leka::interface { + +class Game +{ + public: + virtual ~Game() = default; + + virtual void start() = 0; + virtual void run() = 0; + virtual void stop() = 0; + + [[nodiscard]] virtual auto isRunning() const -> bool = 0; +}; + +} // namespace leka::interface diff --git a/libs/GameKit/include/GameKit.h b/libs/GameKit/include/GameKit.h new file mode 100644 index 0000000000..6dd999d75f --- /dev/null +++ b/libs/GameKit/include/GameKit.h @@ -0,0 +1,29 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +#include "Game.h" + +namespace leka { + +class GameKit +{ + public: + explicit GameKit(interface::EventLoop &event_loop) : _event_loop(event_loop) {} + + void init(); + void start(interface::Game *game); + void stop(); + + private: + void run(); + + interface::EventLoop &_event_loop; + interface::Game *_game = nullptr; +}; + +} // namespace leka diff --git a/libs/GameKit/source/GameKit.cpp b/libs/GameKit/source/GameKit.cpp new file mode 100644 index 0000000000..df4cd1ddbb --- /dev/null +++ b/libs/GameKit/source/GameKit.cpp @@ -0,0 +1,45 @@ +// Leka - LekaOS +// Copyright 2021 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "GameKit.h" + +#include "rtos/ThisThread.h" + +using namespace leka; +using namespace std::chrono; + +void GameKit::init() +{ + _event_loop.registerCallback([this] { run(); }); +} + +void GameKit::start(interface::Game *game) +{ + stop(); + + _game = game; + + if (_game == nullptr) { + return; + } + _game->start(); + _event_loop.start(); +} + +void GameKit::run() +{ + while (_game->isRunning()) { + _game->run(); + rtos::ThisThread::sleep_for(50ms); + } +} + +void GameKit::stop() +{ + _event_loop.stop(); + + if (_game != nullptr) { + _game->stop(); + } +} diff --git a/libs/GameKit/tests/GameKit_test.cpp b/libs/GameKit/tests/GameKit_test.cpp new file mode 100644 index 0000000000..03a9ac3ad6 --- /dev/null +++ b/libs/GameKit/tests/GameKit_test.cpp @@ -0,0 +1,108 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "GameKit.h" + +#include "gtest/gtest.h" +#include "mocks/Game.h" +#include "stubs/leka/EventLoopKit.h" + +using namespace leka; +using namespace std::chrono; + +using ::testing::InSequence; + +class GameKitTest : public ::testing::Test +{ + protected: + GameKitTest() = default; + + // void SetUp() override {} + // void TearDown() override {} + + stub::EventLoopKit stub_event_loop {}; + + GameKit gamekit {stub_event_loop}; + + mock::Game mock_game {}; +}; + +TEST_F(GameKitTest, initialization) +{ + EXPECT_NE(&mock_game, nullptr); +} + +TEST_F(GameKitTest, startGame) +{ + EXPECT_CALL(mock_game, startCalled).Times(1); + EXPECT_FALSE(mock_game.isRunning()); + + gamekit.start(&mock_game); + EXPECT_TRUE(mock_game.isRunning()); +} + +TEST_F(GameKitTest, startNullPtr) +{ + EXPECT_CALL(mock_game, startCalled).Times(0); + EXPECT_FALSE(mock_game.isRunning()); + + gamekit.start(nullptr); + EXPECT_FALSE(mock_game.isRunning()); +} + +TEST_F(GameKitTest, runGame) +{ + auto kMaxStageNumber = 10; + EXPECT_CALL(mock_game, startCalled).Times(1); + EXPECT_CALL(mock_game, stageCalled).Times(kMaxStageNumber); + + gamekit.init(); + gamekit.start(&mock_game); +} + +TEST_F(GameKitTest, stopWithoutGame) +{ + EXPECT_CALL(mock_game, stopCalled).Times(0); + + gamekit.stop(); +} + +TEST_F(GameKitTest, stopStartedGame) +{ + EXPECT_CALL(mock_game, startCalled).Times(1); + EXPECT_CALL(mock_game, stopCalled).Times(1); + EXPECT_FALSE(mock_game.isRunning()); + + gamekit.start(&mock_game); + EXPECT_TRUE(mock_game.isRunning()); + + gamekit.stop(); + EXPECT_FALSE(mock_game.isRunning()); +} + +TEST_F(GameKitTest, startNewGameSequence) +{ + mock::Game mock_new_game; + + { + InSequence seq; + + EXPECT_CALL(mock_game, startCalled).Times(1); + EXPECT_CALL(mock_game, stopCalled).Times(1); + EXPECT_CALL(mock_new_game, startCalled).Times(1); + } + + EXPECT_FALSE(mock_game.isRunning()); + EXPECT_FALSE(mock_new_game.isRunning()); + + gamekit.start(&mock_game); + + EXPECT_TRUE(mock_game.isRunning()); + EXPECT_FALSE(mock_new_game.isRunning()); + + gamekit.start(&mock_new_game); + + EXPECT_FALSE(mock_game.isRunning()); + EXPECT_TRUE(mock_new_game.isRunning()); +} diff --git a/libs/GameKit/tests/mocks/Game.h b/libs/GameKit/tests/mocks/Game.h new file mode 100644 index 0000000000..664e22f3ce --- /dev/null +++ b/libs/GameKit/tests/mocks/Game.h @@ -0,0 +1,51 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +#include "Game.h" +#include "gmock/gmock.h" + +namespace leka::mock { + +class Game : public interface::Game +{ + public: + explicit Game() = default; + + void start() final + { + startCalled(); + _running = true; + _stage = 0; + } + void run() final + { + auto kMaxStageNumber = 10; + if (_stage < kMaxStageNumber) { + ++_stage; + stageCalled(); + } else { + _running = false; + } + } + void stop() final + { + stopCalled(); + _running = false; + } + auto isRunning() const -> bool final { return _running; } + + MOCK_METHOD(void, startCalled, (), ()); + MOCK_METHOD(void, stopCalled, (), ()); + MOCK_METHOD(void, stageCalled, (), ()); + + private: + bool _running = false; + uint8_t _stage = 0; +}; + +} // namespace leka::mock diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 413fafea2e..ea4792faed 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -277,6 +277,7 @@ leka_register_unit_tests_for_library(ContainerKit) leka_register_unit_tests_for_library(EventLoopKit) leka_register_unit_tests_for_library(FileManagerKit) leka_register_unit_tests_for_library(FirmwareKit) +leka_register_unit_tests_for_library(GameKit) leka_register_unit_tests_for_library(WebKit) leka_register_unit_tests_for_library(IOKit) leka_register_unit_tests_for_library(LedKit) From a2cbd0b56b4665009b1e9a803e95334aa68e1368 Mon Sep 17 00:00:00 2001 From: Mourad Latoundji Date: Thu, 18 Aug 2022 18:16:16 +0200 Subject: [PATCH 046/130] :sparkles: (spikes): Add lk_qdac spike --- spikes/CMakeLists.txt | 2 + spikes/lk_qdac/CMakeLists.txt | 22 ++++++++++ spikes/lk_qdac/main.cpp | 76 +++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 spikes/lk_qdac/CMakeLists.txt create mode 100644 spikes/lk_qdac/main.cpp diff --git a/spikes/CMakeLists.txt b/spikes/CMakeLists.txt index b00e927dfb..d626c11ee0 100644 --- a/spikes/CMakeLists.txt +++ b/spikes/CMakeLists.txt @@ -20,6 +20,7 @@ add_subdirectory(${SPIKES_DIR}/lk_lcd) add_subdirectory(${SPIKES_DIR}/lk_led_kit) add_subdirectory(${SPIKES_DIR}/lk_log_kit) add_subdirectory(${SPIKES_DIR}/lk_motors) +add_subdirectory(${SPIKES_DIR}/lk_qdac) add_subdirectory(${SPIKES_DIR}/lk_reinforcer) add_subdirectory(${SPIKES_DIR}/lk_rfid) add_subdirectory(${SPIKES_DIR}/lk_sensors_battery) @@ -56,6 +57,7 @@ add_dependencies(spikes_leka spike_lk_led_kit spike_lk_log_kit spike_lk_motors + spike_lk_qdac spike_lk_reinforcer spike_lk_rfid spike_lk_sensors_battery diff --git a/spikes/lk_qdac/CMakeLists.txt b/spikes/lk_qdac/CMakeLists.txt new file mode 100644 index 0000000000..8e3d75f370 --- /dev/null +++ b/spikes/lk_qdac/CMakeLists.txt @@ -0,0 +1,22 @@ +# Leka - LekaOS +# Copyright 2022 APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +add_mbed_executable(spike_lk_qdac) + +target_include_directories(spike_lk_qdac + PRIVATE + . +) + +target_sources(spike_lk_qdac + PRIVATE + main.cpp +) + +target_link_libraries(spike_lk_qdac + CoreI2C + CoreQDAC +) + +target_link_custom_leka_targets(spike_lk_qdac) diff --git a/spikes/lk_qdac/main.cpp b/spikes/lk_qdac/main.cpp new file mode 100644 index 0000000000..f552690f5a --- /dev/null +++ b/spikes/lk_qdac/main.cpp @@ -0,0 +1,76 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "PinNames.h" + +#include "drivers/HighResClock.h" +#include "rtos/ThisThread.h" + +#include "CoreI2C.h" +#include "CoreQDAC.h" +#include "HelloWorld.h" +#include "LogKit.h" + +using namespace leka; +using namespace std::chrono; + +// +// MARK: - Global definitions +// + +auto corei2c = CoreI2C {PinName::SENSOR_PROXIMITY_MUX_I2C_SDA, PinName::SENSOR_PROXIMITY_MUX_I2C_SCL}; +auto dac = CoreQDACMCP4728 {corei2c, 0xC2}; + +auto data = uint16_t {}; + +void printInputRegisters() +{ + for (auto channels = std::array {mcp4728::channel::A, mcp4728::channel::B, mcp4728::channel::C, + mcp4728::channel::D}; + uint8_t channel: channels) { + log_info("================= Channel %d ================= ", channel); + data = dac.read(channel); + log_info("Input Registers === Data: %x", data); + log_info("\n\n"); + } +} + +auto main() -> int +{ + logger::init(); + + log_info("Hello, World!\n\n"); + + HelloWorld hello; + hello.start(); + + rtos::ThisThread::sleep_for(1s); + + auto channel = uint8_t {}; + log_info("Read DAC (channel 0=A, 1=B, 2=C, 3=D):\n"); + + log_info("Init !\n"); + dac.init(); + rtos::ThisThread::sleep_for(100ms); + printInputRegisters(); + rtos::ThisThread::sleep_for(2s); + + channel = 0x00; + + data = 0x0ABC; + log_info("Writing ! Channel : %d, Data : %x\n", channel, data); + dac.write(channel, data); + rtos::ThisThread::sleep_for(100ms); + printInputRegisters(); + rtos::ThisThread::sleep_for(1s); + + channel = 0x01; + + data = 0x0DEF; + log_info("Writing ! Channel : %d, Data : %x\n", channel, data); + dac.write(channel, data); + rtos::ThisThread::sleep_for(100ms); + printInputRegisters(); + rtos::ThisThread::sleep_for(1s); +} From 29cb7ae7662f53ab9549445b92196a5cffa3e0b8 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Fri, 26 Aug 2022 17:06:36 +0200 Subject: [PATCH 047/130] :bug: (RFIDKit): Make tag_detected_callback lambda static --- libs/RFIDKit/source/RFIDKit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/RFIDKit/source/RFIDKit.cpp b/libs/RFIDKit/source/RFIDKit.cpp index fab28dac35..060cb318fe 100644 --- a/libs/RFIDKit/source/RFIDKit.cpp +++ b/libs/RFIDKit/source/RFIDKit.cpp @@ -10,7 +10,7 @@ void RFIDKit::init() { using namespace rfid::sm; - auto tag_detected_callback = [this]() { state_machine.process_event(event::tag_response_available {}); }; + static auto tag_detected_callback = [this]() { state_machine.process_event(event::tag_response_available {}); }; _rfid_reader.registerOnTagDetectedCallback(tag_detected_callback); registerMagicCard(); From 1ceceee17a051699a31d3f89f2b7ff336e5ed2f4 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Fri, 26 Aug 2022 18:00:38 +0200 Subject: [PATCH 048/130] :bug: (RFIDKit): ISO/SM - Fix wrong array_size --- libs/RFIDKit/include/ISO14443A.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/RFIDKit/include/ISO14443A.h b/libs/RFIDKit/include/ISO14443A.h index a7fde786be..812d5167aa 100644 --- a/libs/RFIDKit/include/ISO14443A.h +++ b/libs/RFIDKit/include/ISO14443A.h @@ -35,7 +35,7 @@ constexpr Command<2> command_read_register_4 = {.data = {0x30, 0x04}, .flags = leka::rfid::Flag::crc | leka::rfid::Flag::sb_8}; constexpr auto ATQA_answer_size = 2; -constexpr auto initial_polynomial_value = uint16_t {0x6363}; +constexpr auto initial_polynomial_value = uint32_t {0x6363}; constexpr auto register_answer_size = size_t {18}; constexpr auto expected_ATQA_answer = std::array {0x44, 0x00}; @@ -87,7 +87,7 @@ inline auto atqaReceived(interface::RFIDReader &rfidreader) -> bool inline auto registerReceived(interface::RFIDReader &rfidreader) -> bool { - if (!rfidreader.didTagCommunicationSucceed(ATQA_answer_size)) { + if (!rfidreader.didTagCommunicationSucceed(register_answer_size)) { return false; } From 2be926e554c35d24f80b51037016390830195daa Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Thu, 25 Aug 2022 18:40:25 +0200 Subject: [PATCH 049/130] :art: (RFIDKit): Add getCallback() method --- libs/RFIDKit/include/RFIDKit.h | 2 ++ libs/RFIDKit/source/RFIDKit.cpp | 5 +++++ libs/RFIDKit/tests/RFIDKit_test.cpp | 23 +++++++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/libs/RFIDKit/include/RFIDKit.h b/libs/RFIDKit/include/RFIDKit.h index 49b37bf3a5..b655fafd39 100644 --- a/libs/RFIDKit/include/RFIDKit.h +++ b/libs/RFIDKit/include/RFIDKit.h @@ -20,6 +20,8 @@ class RFIDKit [[nodiscard]] auto isTagSignatureValid(rfid::Tag tag) const -> bool; void onTagActivated(std::function const &callback); + [[nodiscard]] auto getCallback() const -> const std::function &; + private: interface::RFIDReader &_rfid_reader; MagicCard _card = MagicCard::none; diff --git a/libs/RFIDKit/source/RFIDKit.cpp b/libs/RFIDKit/source/RFIDKit.cpp index 060cb318fe..fffe0e7dbe 100644 --- a/libs/RFIDKit/source/RFIDKit.cpp +++ b/libs/RFIDKit/source/RFIDKit.cpp @@ -44,4 +44,9 @@ void RFIDKit::onTagActivated(std::function const &callba _on_tag_available_callback = callback; } +[[nodiscard]] auto RFIDKit::getCallback() const -> const std::function & +{ + return _on_tag_available_callback; +} + } // namespace leka diff --git a/libs/RFIDKit/tests/RFIDKit_test.cpp b/libs/RFIDKit/tests/RFIDKit_test.cpp index 91e261c7c3..acdc63e70d 100644 --- a/libs/RFIDKit/tests/RFIDKit_test.cpp +++ b/libs/RFIDKit/tests/RFIDKit_test.cpp @@ -125,3 +125,26 @@ TEST_F(RFIDKitTest, onTagActivated) { rfid_kit.onTagActivated(nullptr); } + +TEST_F(RFIDKitTest, getNullPtrCallback) +{ + rfid_kit.onTagActivated(nullptr); + auto callback = rfid_kit.getCallback(); + EXPECT_EQ(callback, nullptr); +} + +TEST_F(RFIDKitTest, getCallback) +{ + auto card = MagicCard::none; + + const std::function expected_passed_callback = [](MagicCard &card) { + card = MagicCard::activity_super_simon; + }; + + rfid_kit.onTagActivated(expected_passed_callback); + auto callback = rfid_kit.getCallback(); + + callback(card); + + EXPECT_EQ(card, MagicCard::activity_super_simon); +} From cd0bbc4e12066964aa11a595ce38a709e8609d7b Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Thu, 25 Aug 2022 18:36:26 +0200 Subject: [PATCH 050/130] :truck: (libs): Rename GameKit --> ActivityKit --- libs/ActivityKit/CMakeLists.txt | 28 +++++ .../Game.h => ActivityKit/include/Activity.h} | 7 +- libs/ActivityKit/include/ActivityKit.h | 23 ++++ libs/ActivityKit/source/ActivityKit.cpp | 26 +++++ libs/ActivityKit/tests/ActivityKit_test.cpp | 77 +++++++++++++ libs/ActivityKit/tests/mocks/Activity.h | 19 +++ libs/CMakeLists.txt | 2 +- libs/GameKit/CMakeLists.txt | 26 ----- libs/GameKit/include/GameKit.h | 29 ----- libs/GameKit/source/GameKit.cpp | 45 -------- libs/GameKit/tests/GameKit_test.cpp | 108 ------------------ libs/GameKit/tests/mocks/Game.h | 51 --------- tests/unit/CMakeLists.txt | 2 +- 13 files changed, 177 insertions(+), 266 deletions(-) create mode 100644 libs/ActivityKit/CMakeLists.txt rename libs/{GameKit/include/Game.h => ActivityKit/include/Activity.h} (65%) create mode 100644 libs/ActivityKit/include/ActivityKit.h create mode 100644 libs/ActivityKit/source/ActivityKit.cpp create mode 100644 libs/ActivityKit/tests/ActivityKit_test.cpp create mode 100644 libs/ActivityKit/tests/mocks/Activity.h delete mode 100644 libs/GameKit/CMakeLists.txt delete mode 100644 libs/GameKit/include/GameKit.h delete mode 100644 libs/GameKit/source/GameKit.cpp delete mode 100644 libs/GameKit/tests/GameKit_test.cpp delete mode 100644 libs/GameKit/tests/mocks/Game.h diff --git a/libs/ActivityKit/CMakeLists.txt b/libs/ActivityKit/CMakeLists.txt new file mode 100644 index 0000000000..6356c8ed10 --- /dev/null +++ b/libs/ActivityKit/CMakeLists.txt @@ -0,0 +1,28 @@ +# Leka - LekaOS +# Copyright 2022 APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +add_library(ActivityKit STATIC) + +target_include_directories(ActivityKit + PUBLIC + include + include/activities +) + +target_sources(ActivityKit + PRIVATE + source/ActivityKit.cpp +) + +target_link_libraries(ActivityKit + RFIDKit + CoreLED + VideoKit +) + +if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") + leka_unit_tests_sources( + tests/ActivityKit_test.cpp + ) +endif() diff --git a/libs/GameKit/include/Game.h b/libs/ActivityKit/include/Activity.h similarity index 65% rename from libs/GameKit/include/Game.h rename to libs/ActivityKit/include/Activity.h index 3491681d5b..1b4e42810d 100644 --- a/libs/GameKit/include/Game.h +++ b/libs/ActivityKit/include/Activity.h @@ -6,16 +6,13 @@ namespace leka::interface { -class Game +class Activity { public: - virtual ~Game() = default; + virtual ~Activity() = default; virtual void start() = 0; - virtual void run() = 0; virtual void stop() = 0; - - [[nodiscard]] virtual auto isRunning() const -> bool = 0; }; } // namespace leka::interface diff --git a/libs/ActivityKit/include/ActivityKit.h b/libs/ActivityKit/include/ActivityKit.h new file mode 100644 index 0000000000..004a0f7b52 --- /dev/null +++ b/libs/ActivityKit/include/ActivityKit.h @@ -0,0 +1,23 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "Activity.h" + +namespace leka { + +class ActivityKit +{ + public: + explicit ActivityKit() = default; + + void start(interface::Activity *activity); + void stop(); + + private: + interface::Activity *_activity = nullptr; +}; + +} // namespace leka diff --git a/libs/ActivityKit/source/ActivityKit.cpp b/libs/ActivityKit/source/ActivityKit.cpp new file mode 100644 index 0000000000..9352a69ac0 --- /dev/null +++ b/libs/ActivityKit/source/ActivityKit.cpp @@ -0,0 +1,26 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "ActivityKit.h" + +using namespace leka; + +void ActivityKit::start(interface::Activity *activity) +{ + stop(); + + _activity = activity; + + if (_activity == nullptr) { + return; + } + _activity->start(); +} + +void ActivityKit::stop() +{ + if (_activity != nullptr) { + _activity->stop(); + } +} diff --git a/libs/ActivityKit/tests/ActivityKit_test.cpp b/libs/ActivityKit/tests/ActivityKit_test.cpp new file mode 100644 index 0000000000..7f4085abe3 --- /dev/null +++ b/libs/ActivityKit/tests/ActivityKit_test.cpp @@ -0,0 +1,77 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "ActivityKit.h" + +#include "gtest/gtest.h" +#include "mocks/Activity.h" + +using namespace leka; + +using ::testing::InSequence; + +class ActivityKitTest : public ::testing::Test +{ + protected: + ActivityKitTest() = default; + + // void SetUp() override {} + // void TearDown() override {} + + ActivityKit activitykit; + + mock::Activity mock_activity {}; +}; + +TEST_F(ActivityKitTest, initialization) +{ + EXPECT_NE(&mock_activity, nullptr); +} + +TEST_F(ActivityKitTest, startactivity) +{ + EXPECT_CALL(mock_activity, start).Times(1); + + activitykit.start(&mock_activity); +} + +TEST_F(ActivityKitTest, startNullPtr) +{ + EXPECT_CALL(mock_activity, start).Times(0); + + activitykit.start(nullptr); +} + +TEST_F(ActivityKitTest, stopWithoutactivity) +{ + EXPECT_CALL(mock_activity, stop).Times(0); + + activitykit.stop(); +} + +TEST_F(ActivityKitTest, stopStartedactivity) +{ + EXPECT_CALL(mock_activity, start).Times(1); + EXPECT_CALL(mock_activity, stop).Times(1); + + activitykit.start(&mock_activity); + activitykit.stop(); +} + +TEST_F(ActivityKitTest, startNewactivitySequence) +{ + mock::Activity mock_new_activity; + + { + InSequence seq; + + EXPECT_CALL(mock_activity, start).Times(1); + EXPECT_CALL(mock_activity, stop).Times(1); + EXPECT_CALL(mock_new_activity, start).Times(1); + } + + activitykit.start(&mock_activity); + + activitykit.start(&mock_new_activity); +} diff --git a/libs/ActivityKit/tests/mocks/Activity.h b/libs/ActivityKit/tests/mocks/Activity.h new file mode 100644 index 0000000000..f7da5eea80 --- /dev/null +++ b/libs/ActivityKit/tests/mocks/Activity.h @@ -0,0 +1,19 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "Activity.h" +#include "gmock/gmock.h" + +namespace leka::mock { + +class Activity : public interface::Activity +{ + public: + MOCK_METHOD(void, start, (), ()); + MOCK_METHOD(void, stop, (), ()); +}; + +} // namespace leka::mock diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index 6e2e3abf87..cb6aaad167 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -2,6 +2,7 @@ # Copyright 2020 APF France handicap # SPDX-License-Identifier: Apache-2.0 +add_subdirectory(${LIBS_DIR}/ActivityKit) add_subdirectory(${LIBS_DIR}/BatteryKit) add_subdirectory(${LIBS_DIR}/BehaviorKit) add_subdirectory(${LIBS_DIR}/BLEKit) @@ -12,7 +13,6 @@ add_subdirectory(${LIBS_DIR}/ContainerKit) add_subdirectory(${LIBS_DIR}/EventLoopKit) add_subdirectory(${LIBS_DIR}/FileManagerKit) add_subdirectory(${LIBS_DIR}/FirmwareKit) -add_subdirectory(${LIBS_DIR}/GameKit) add_subdirectory(${LIBS_DIR}/IOKit) add_subdirectory(${LIBS_DIR}/LedKit) add_subdirectory(${LIBS_DIR}/RobotKit) diff --git a/libs/GameKit/CMakeLists.txt b/libs/GameKit/CMakeLists.txt deleted file mode 100644 index dbb1f46384..0000000000 --- a/libs/GameKit/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -# Leka - LekaOS -# Copyright 2022 APF France handicap -# SPDX-License-Identifier: Apache-2.0 - -add_library(GameKit STATIC) - -target_include_directories(GameKit - PUBLIC - include - include/games -) - -target_sources(GameKit - PRIVATE - source/GameKit.cpp -) - -target_link_libraries(GameKit - EventLoopKit -) - -if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") - leka_unit_tests_sources( - tests/GameKit_test.cpp - ) -endif() diff --git a/libs/GameKit/include/GameKit.h b/libs/GameKit/include/GameKit.h deleted file mode 100644 index 6dd999d75f..0000000000 --- a/libs/GameKit/include/GameKit.h +++ /dev/null @@ -1,29 +0,0 @@ -// Leka - LekaOS -// Copyright 2022 APF France handicap -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include - -#include "Game.h" - -namespace leka { - -class GameKit -{ - public: - explicit GameKit(interface::EventLoop &event_loop) : _event_loop(event_loop) {} - - void init(); - void start(interface::Game *game); - void stop(); - - private: - void run(); - - interface::EventLoop &_event_loop; - interface::Game *_game = nullptr; -}; - -} // namespace leka diff --git a/libs/GameKit/source/GameKit.cpp b/libs/GameKit/source/GameKit.cpp deleted file mode 100644 index df4cd1ddbb..0000000000 --- a/libs/GameKit/source/GameKit.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Leka - LekaOS -// Copyright 2021 APF France handicap -// SPDX-License-Identifier: Apache-2.0 - -#include "GameKit.h" - -#include "rtos/ThisThread.h" - -using namespace leka; -using namespace std::chrono; - -void GameKit::init() -{ - _event_loop.registerCallback([this] { run(); }); -} - -void GameKit::start(interface::Game *game) -{ - stop(); - - _game = game; - - if (_game == nullptr) { - return; - } - _game->start(); - _event_loop.start(); -} - -void GameKit::run() -{ - while (_game->isRunning()) { - _game->run(); - rtos::ThisThread::sleep_for(50ms); - } -} - -void GameKit::stop() -{ - _event_loop.stop(); - - if (_game != nullptr) { - _game->stop(); - } -} diff --git a/libs/GameKit/tests/GameKit_test.cpp b/libs/GameKit/tests/GameKit_test.cpp deleted file mode 100644 index 03a9ac3ad6..0000000000 --- a/libs/GameKit/tests/GameKit_test.cpp +++ /dev/null @@ -1,108 +0,0 @@ -// Leka - LekaOS -// Copyright 2022 APF France handicap -// SPDX-License-Identifier: Apache-2.0 - -#include "GameKit.h" - -#include "gtest/gtest.h" -#include "mocks/Game.h" -#include "stubs/leka/EventLoopKit.h" - -using namespace leka; -using namespace std::chrono; - -using ::testing::InSequence; - -class GameKitTest : public ::testing::Test -{ - protected: - GameKitTest() = default; - - // void SetUp() override {} - // void TearDown() override {} - - stub::EventLoopKit stub_event_loop {}; - - GameKit gamekit {stub_event_loop}; - - mock::Game mock_game {}; -}; - -TEST_F(GameKitTest, initialization) -{ - EXPECT_NE(&mock_game, nullptr); -} - -TEST_F(GameKitTest, startGame) -{ - EXPECT_CALL(mock_game, startCalled).Times(1); - EXPECT_FALSE(mock_game.isRunning()); - - gamekit.start(&mock_game); - EXPECT_TRUE(mock_game.isRunning()); -} - -TEST_F(GameKitTest, startNullPtr) -{ - EXPECT_CALL(mock_game, startCalled).Times(0); - EXPECT_FALSE(mock_game.isRunning()); - - gamekit.start(nullptr); - EXPECT_FALSE(mock_game.isRunning()); -} - -TEST_F(GameKitTest, runGame) -{ - auto kMaxStageNumber = 10; - EXPECT_CALL(mock_game, startCalled).Times(1); - EXPECT_CALL(mock_game, stageCalled).Times(kMaxStageNumber); - - gamekit.init(); - gamekit.start(&mock_game); -} - -TEST_F(GameKitTest, stopWithoutGame) -{ - EXPECT_CALL(mock_game, stopCalled).Times(0); - - gamekit.stop(); -} - -TEST_F(GameKitTest, stopStartedGame) -{ - EXPECT_CALL(mock_game, startCalled).Times(1); - EXPECT_CALL(mock_game, stopCalled).Times(1); - EXPECT_FALSE(mock_game.isRunning()); - - gamekit.start(&mock_game); - EXPECT_TRUE(mock_game.isRunning()); - - gamekit.stop(); - EXPECT_FALSE(mock_game.isRunning()); -} - -TEST_F(GameKitTest, startNewGameSequence) -{ - mock::Game mock_new_game; - - { - InSequence seq; - - EXPECT_CALL(mock_game, startCalled).Times(1); - EXPECT_CALL(mock_game, stopCalled).Times(1); - EXPECT_CALL(mock_new_game, startCalled).Times(1); - } - - EXPECT_FALSE(mock_game.isRunning()); - EXPECT_FALSE(mock_new_game.isRunning()); - - gamekit.start(&mock_game); - - EXPECT_TRUE(mock_game.isRunning()); - EXPECT_FALSE(mock_new_game.isRunning()); - - gamekit.start(&mock_new_game); - - EXPECT_FALSE(mock_game.isRunning()); - EXPECT_TRUE(mock_new_game.isRunning()); -} diff --git a/libs/GameKit/tests/mocks/Game.h b/libs/GameKit/tests/mocks/Game.h deleted file mode 100644 index 664e22f3ce..0000000000 --- a/libs/GameKit/tests/mocks/Game.h +++ /dev/null @@ -1,51 +0,0 @@ -// Leka - LekaOS -// Copyright 2022 APF France handicap -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include - -#include "Game.h" -#include "gmock/gmock.h" - -namespace leka::mock { - -class Game : public interface::Game -{ - public: - explicit Game() = default; - - void start() final - { - startCalled(); - _running = true; - _stage = 0; - } - void run() final - { - auto kMaxStageNumber = 10; - if (_stage < kMaxStageNumber) { - ++_stage; - stageCalled(); - } else { - _running = false; - } - } - void stop() final - { - stopCalled(); - _running = false; - } - auto isRunning() const -> bool final { return _running; } - - MOCK_METHOD(void, startCalled, (), ()); - MOCK_METHOD(void, stopCalled, (), ()); - MOCK_METHOD(void, stageCalled, (), ()); - - private: - bool _running = false; - uint8_t _stage = 0; -}; - -} // namespace leka::mock diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index ea4792faed..f3e8052a2b 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -267,6 +267,7 @@ leka_register_unit_tests_for_driver(CoreTicker) leka_register_unit_tests_for_driver(CoreVideo) # Register libraries +leka_register_unit_tests_for_library(ActivityKit) leka_register_unit_tests_for_library(BatteryKit) leka_register_unit_tests_for_library(BehaviorKit) leka_register_unit_tests_for_library(BLEKit) @@ -277,7 +278,6 @@ leka_register_unit_tests_for_library(ContainerKit) leka_register_unit_tests_for_library(EventLoopKit) leka_register_unit_tests_for_library(FileManagerKit) leka_register_unit_tests_for_library(FirmwareKit) -leka_register_unit_tests_for_library(GameKit) leka_register_unit_tests_for_library(WebKit) leka_register_unit_tests_for_library(IOKit) leka_register_unit_tests_for_library(LedKit) From 9ae11a3897ffd1c48fa1483f5a83075fffdcb207 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Thu, 1 Sep 2022 15:30:29 +0200 Subject: [PATCH 051/130] :truck: (ActivityKit): Move Activity.h to local interface folder --- libs/ActivityKit/include/ActivityKit.h | 3 ++- libs/ActivityKit/include/{ => interface}/Activity.h | 0 libs/ActivityKit/tests/mocks/Activity.h | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) rename libs/ActivityKit/include/{ => interface}/Activity.h (100%) diff --git a/libs/ActivityKit/include/ActivityKit.h b/libs/ActivityKit/include/ActivityKit.h index 004a0f7b52..1b18d71a5d 100644 --- a/libs/ActivityKit/include/ActivityKit.h +++ b/libs/ActivityKit/include/ActivityKit.h @@ -4,7 +4,8 @@ #pragma once -#include "Activity.h" +#include "MagicCard.h" +#include "interface/Activity.h" namespace leka { diff --git a/libs/ActivityKit/include/Activity.h b/libs/ActivityKit/include/interface/Activity.h similarity index 100% rename from libs/ActivityKit/include/Activity.h rename to libs/ActivityKit/include/interface/Activity.h diff --git a/libs/ActivityKit/tests/mocks/Activity.h b/libs/ActivityKit/tests/mocks/Activity.h index f7da5eea80..0b32c2e29e 100644 --- a/libs/ActivityKit/tests/mocks/Activity.h +++ b/libs/ActivityKit/tests/mocks/Activity.h @@ -4,8 +4,8 @@ #pragma once -#include "Activity.h" #include "gmock/gmock.h" +#include "interface/Activity.h" namespace leka::mock { From a1404b7b4a8f72060095c2f5b69ad44467f574e8 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Fri, 2 Sep 2022 00:20:04 +0200 Subject: [PATCH 052/130] :sparkles: (RFIDKit): Make MagicCard std::hash compliant --- libs/RFIDKit/include/MagicCard.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libs/RFIDKit/include/MagicCard.h b/libs/RFIDKit/include/MagicCard.h index 26602f9795..42a0ad112b 100644 --- a/libs/RFIDKit/include/MagicCard.h +++ b/libs/RFIDKit/include/MagicCard.h @@ -190,3 +190,11 @@ constexpr MagicCard MagicCard::math_arithmetic_substraction_sign_minus = MagicCa constexpr MagicCard MagicCard::math_arithmetic_addition_sign_plus = MagicCard {0x00'3C}; } // namespace leka + +// ? Make MagicCard std::hash compatible for use in associative containers (i.e. std::unordered_map) +namespace std { +template <> +struct hash { + auto operator()(const leka::MagicCard &card) const -> size_t { return hash()(card.getId()); } +}; +} // namespace std From d238c32b4f83ec7307427a89e836bcb0bcc165e2 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Fri, 2 Sep 2022 00:22:37 +0200 Subject: [PATCH 053/130] :sparkles: (ActivityKit): Register activities through associative container --- libs/ActivityKit/include/ActivityKit.h | 10 +++-- libs/ActivityKit/source/ActivityKit.cpp | 22 +++++++--- libs/ActivityKit/tests/ActivityKit_test.cpp | 48 +++++++++++---------- 3 files changed, 48 insertions(+), 32 deletions(-) diff --git a/libs/ActivityKit/include/ActivityKit.h b/libs/ActivityKit/include/ActivityKit.h index 1b18d71a5d..2aadd6ebf8 100644 --- a/libs/ActivityKit/include/ActivityKit.h +++ b/libs/ActivityKit/include/ActivityKit.h @@ -4,21 +4,25 @@ #pragma once +#include + #include "MagicCard.h" #include "interface/Activity.h" namespace leka { - class ActivityKit { public: explicit ActivityKit() = default; - void start(interface::Activity *activity); + void registerActivities(std::unordered_map const &activities); + + void start(const MagicCard &card); void stop(); private: - interface::Activity *_activity = nullptr; + interface::Activity *_current_activity = nullptr; + std::unordered_map _activities {}; }; } // namespace leka diff --git a/libs/ActivityKit/source/ActivityKit.cpp b/libs/ActivityKit/source/ActivityKit.cpp index 9352a69ac0..87307acab2 100644 --- a/libs/ActivityKit/source/ActivityKit.cpp +++ b/libs/ActivityKit/source/ActivityKit.cpp @@ -6,21 +6,29 @@ using namespace leka; -void ActivityKit::start(interface::Activity *activity) +void ActivityKit::registerActivities(std::unordered_map const &activities) { - stop(); + _activities = activities; +} - _activity = activity; +void ActivityKit::start(const MagicCard &card) +{ + stop(); - if (_activity == nullptr) { + if (!_activities.contains(card)) { + _current_activity = nullptr; return; } - _activity->start(); + + _current_activity = _activities.at(card); + _current_activity->start(); } void ActivityKit::stop() { - if (_activity != nullptr) { - _activity->stop(); + if (_current_activity == nullptr) { + return; } + + _current_activity->stop(); } diff --git a/libs/ActivityKit/tests/ActivityKit_test.cpp b/libs/ActivityKit/tests/ActivityKit_test.cpp index 7f4085abe3..895318b28d 100644 --- a/libs/ActivityKit/tests/ActivityKit_test.cpp +++ b/libs/ActivityKit/tests/ActivityKit_test.cpp @@ -16,62 +16,66 @@ class ActivityKitTest : public ::testing::Test protected: ActivityKitTest() = default; - // void SetUp() override {} + void SetUp() override { activitykit.registerActivities(activity_list); } // void TearDown() override {} ActivityKit activitykit; - mock::Activity mock_activity {}; + mock::Activity mock_activity_0 {}; + mock::Activity mock_activity_1 {}; + + std::unordered_map activity_list = { + {MagicCard::number_0, &mock_activity_0}, + {MagicCard::number_1, &mock_activity_1}, + }; }; TEST_F(ActivityKitTest, initialization) { - EXPECT_NE(&mock_activity, nullptr); + EXPECT_NE(&mock_activity_0, nullptr); } -TEST_F(ActivityKitTest, startactivity) +TEST_F(ActivityKitTest, startActivity) { - EXPECT_CALL(mock_activity, start).Times(1); + EXPECT_CALL(mock_activity_0, start).Times(1); - activitykit.start(&mock_activity); + activitykit.start(MagicCard::number_0); } TEST_F(ActivityKitTest, startNullPtr) { - EXPECT_CALL(mock_activity, start).Times(0); + EXPECT_CALL(mock_activity_0, start).Times(0); - activitykit.start(nullptr); + activitykit.start(MagicCard::none); } -TEST_F(ActivityKitTest, stopWithoutactivity) +TEST_F(ActivityKitTest, stopWithoutActivity) { - EXPECT_CALL(mock_activity, stop).Times(0); + EXPECT_CALL(mock_activity_0, stop).Times(0); activitykit.stop(); } -TEST_F(ActivityKitTest, stopStartedactivity) +TEST_F(ActivityKitTest, stopStartedActivity) { - EXPECT_CALL(mock_activity, start).Times(1); - EXPECT_CALL(mock_activity, stop).Times(1); + EXPECT_CALL(mock_activity_0, start).Times(1); + EXPECT_CALL(mock_activity_0, stop).Times(1); - activitykit.start(&mock_activity); + activitykit.start(MagicCard::number_0); activitykit.stop(); } -TEST_F(ActivityKitTest, startNewactivitySequence) +TEST_F(ActivityKitTest, startNewActivitySequence) { - mock::Activity mock_new_activity; - { InSequence seq; - EXPECT_CALL(mock_activity, start).Times(1); - EXPECT_CALL(mock_activity, stop).Times(1); - EXPECT_CALL(mock_new_activity, start).Times(1); + EXPECT_CALL(mock_activity_0, start).Times(1); + EXPECT_CALL(mock_activity_0, stop).Times(1); + EXPECT_CALL(mock_activity_1, start).Times(1); } - activitykit.start(&mock_activity); + activitykit.start(MagicCard::number_0); - activitykit.start(&mock_new_activity); + activitykit.start(MagicCard::number_1); } From 54c59ff0db8fbed474f2c41312aec000196a3376 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Fri, 2 Sep 2022 00:24:09 +0200 Subject: [PATCH 054/130] :sparkles: (ActivityKit): Add 1st activity - DisplayTag --- libs/ActivityKit/CMakeLists.txt | 1 + .../include/activities/DisplayTags.h | 32 +++++++++++++++++ .../source/activities/DisplayTags.cpp | 36 +++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 libs/ActivityKit/include/activities/DisplayTags.h create mode 100644 libs/ActivityKit/source/activities/DisplayTags.cpp diff --git a/libs/ActivityKit/CMakeLists.txt b/libs/ActivityKit/CMakeLists.txt index 6356c8ed10..b639d06fc3 100644 --- a/libs/ActivityKit/CMakeLists.txt +++ b/libs/ActivityKit/CMakeLists.txt @@ -13,6 +13,7 @@ target_include_directories(ActivityKit target_sources(ActivityKit PRIVATE source/ActivityKit.cpp + source/activities/DisplayTags.cpp ) target_link_libraries(ActivityKit diff --git a/libs/ActivityKit/include/activities/DisplayTags.h b/libs/ActivityKit/include/activities/DisplayTags.h new file mode 100644 index 0000000000..a695fb9675 --- /dev/null +++ b/libs/ActivityKit/include/activities/DisplayTags.h @@ -0,0 +1,32 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +// LCOV_EXCL_START + +#include "RFIDKit.h" +#include "interface/Activity.h" +#include "interface/libs/VideoKit.h" + +namespace leka::activity { + +class DisplayTags : public interface::Activity +{ + public: + explicit DisplayTags(RFIDKit &rfidkit, interface::VideoKit &videokit) : _rfidkit(rfidkit), _videokit(videokit) {}; + + void start() final; + void stop() final; + + private: + RFIDKit &_rfidkit; + interface::VideoKit &_videokit; + std::array _path_buffer = {}; + std::function _backup_callback {}; +}; + +} // namespace leka::activity + +// LCOV_EXCL_STOP diff --git a/libs/ActivityKit/source/activities/DisplayTags.cpp b/libs/ActivityKit/source/activities/DisplayTags.cpp new file mode 100644 index 0000000000..ae1023f3ab --- /dev/null +++ b/libs/ActivityKit/source/activities/DisplayTags.cpp @@ -0,0 +1,36 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +// LCOV_EXCL_START + +#include "DisplayTags.h" + +namespace leka::activity { + +void DisplayTags::start() +{ + _videokit.displayImage("fs/home/img/system/robot-misc-robot-misc-screen_empty_white.jpg"); + + _backup_callback = _rfidkit.getCallback(); + + auto on_tag_detected_callback = [this](const MagicCard &card) { + if (card == MagicCard::remote_standard) { + stop(); + } else { + snprintf(_path_buffer.data(), _path_buffer.size(), "fs/home/img/id/%.4x.jpg", card.getId()); + _videokit.displayImage(_path_buffer.data()); + } + }; + + _rfidkit.onTagActivated(on_tag_detected_callback); +} + +void DisplayTags::stop() +{ + _rfidkit.onTagActivated(_backup_callback); +} + +} // namespace leka::activity + +// LCOV_EXCL_STOP From 2335e4db3068f3edf74572fd9e1ba67806525a88 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Mon, 5 Sep 2022 13:25:11 +0200 Subject: [PATCH 055/130] :recycle: (os): Rename emergencyStop to onMagicCardAvailable --- app/os/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/os/main.cpp b/app/os/main.cpp index 960bb5133f..ffb53c99fb 100644 --- a/app/os/main.cpp +++ b/app/os/main.cpp @@ -349,7 +349,7 @@ namespace robot { commandkit, }; - void emergencyStop(const MagicCard &card) + void onMagicCardAvailable(const MagicCard &card) { if (card == MagicCard::emergency_stop) { controller.raiseEmergencyStop(); @@ -488,7 +488,7 @@ auto main() -> int robot::controller.registerOnFactoryResetNotificationCallback(factory_reset::set); robot::controller.registerEvents(); - rfidkit.onTagActivated(robot::emergencyStop); + rfidkit.onTagActivated(robot::onMagicCardAvailable); // TODO(@team): Add functional test prior confirming the firmware firmware::confirmFirmware(); From fdfa601c085f56ec08fcfec25746120e58102183 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Mon, 5 Sep 2022 13:27:30 +0200 Subject: [PATCH 056/130] :recycle: (RobotKit): Move RFIDKit + onMagicCardAvailable to RC --- libs/RobotKit/CMakeLists.txt | 1 + libs/RobotKit/include/RobotController.h | 19 +++++++++++++++++-- libs/RobotKit/tests/RobotController_test.h | 11 +++++++++-- .../RobotController_test_stateCharging.cpp | 2 +- .../tests/RobotController_test_stateIdle.cpp | 2 +- .../RobotController_test_stateSleeping.cpp | 2 +- .../RobotController_test_stateWorking.cpp | 2 +- 7 files changed, 31 insertions(+), 8 deletions(-) diff --git a/libs/RobotKit/CMakeLists.txt b/libs/RobotKit/CMakeLists.txt index 94bf65758c..8365b56c91 100644 --- a/libs/RobotKit/CMakeLists.txt +++ b/libs/RobotKit/CMakeLists.txt @@ -28,6 +28,7 @@ target_link_libraries(RobotKit VideoKit BehaviorKit CommandKit + RFIDKit ) if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") diff --git a/libs/RobotKit/include/RobotController.h b/libs/RobotKit/include/RobotController.h index dd2f202bd5..e58c137fdf 100644 --- a/libs/RobotKit/include/RobotController.h +++ b/libs/RobotKit/include/RobotController.h @@ -21,7 +21,9 @@ #include "CoreMutex.h" #include "FileReception.h" #include "LedKit.h" +#include "MagicCard.h" #include "RCLogger.h" +#include "RFIDKit.h" #include "SerialNumberKit.h" #include "StateMachine.h" #include "interface/RobotController.h" @@ -45,7 +47,7 @@ class RobotController : public interface::RobotController interface::FirmwareUpdate &firmware_update, interface::Motor &motor_left, interface::Motor &motor_right, interface::LED &ears, interface::LED &belt, LedKit &ledkit, interface::LCD &lcd, interface::VideoKit &videokit, BehaviorKit &behaviorkit, - CommandKit &cmdkit) + CommandKit &cmdkit, RFIDKit &rfidkit) : _timeout(timeout), _battery(battery), _serialnumberkit(serialnumberkit), @@ -58,7 +60,8 @@ class RobotController : public interface::RobotController _lcd(lcd), _videokit(videokit), _behaviorkit(behaviorkit), - _cmdkit(cmdkit) + _cmdkit(cmdkit), + _rfidkit(rfidkit) { // nothing to do } @@ -220,6 +223,8 @@ class RobotController : public interface::RobotController { _thread.start({&_event_queue, &events::EventQueue::dispatch_forever}); + _rfidkit.init(); + _ble.setServices(services); _ble.init(); @@ -262,12 +267,21 @@ class RobotController : public interface::RobotController } } + void onMagicCardAvailable(const MagicCard &card) + { + if (card == MagicCard::emergency_stop) { + raiseEmergencyStop(); + } + } + void registerEvents() { using namespace system::robot::sm; // Setup callbacks for monitoring + _rfidkit.onTagActivated([this](const MagicCard &card) { onMagicCardAvailable(card); }); + _battery_kit.onDataUpdated([this](uint8_t level) { auto is_charging = _battery.isCharging(); @@ -370,6 +384,7 @@ class RobotController : public interface::RobotController LedKit &_ledkit; interface::LCD &_lcd; interface::VideoKit &_videokit; + RFIDKit &_rfidkit; BehaviorKit &_behaviorkit; CommandKit &_cmdkit; diff --git a/libs/RobotKit/tests/RobotController_test.h b/libs/RobotKit/tests/RobotController_test.h index aca8ef5f9b..d1185d841d 100644 --- a/libs/RobotKit/tests/RobotController_test.h +++ b/libs/RobotKit/tests/RobotController_test.h @@ -11,7 +11,10 @@ #include "BehaviorKit.h" #include "CommandKit.h" +#include "CoreBufferedSerial.h" #include "CorePwm.h" +#include "CoreRFIDReaderCR95HF.h" +#include "RFIDKit.h" #include "SerialNumberKit.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -86,12 +89,16 @@ class RobotControllerTest : public testing::Test BehaviorKit bhvkit {mock_videokit, ledkit, mock_motor_left, mock_motor_right}; + CoreBufferedSerial serial {RFID_UART_TX, RFID_UART_RX, 57600}; + CoreRFIDReaderCR95HF reader {serial}; + RFIDKit rfidkit {reader}; + stub::EventLoopKit event_loop {}; CommandKit cmdkit {event_loop}; RobotController> rc { - timeout, battery, serialnumberkit, firmware_update, mock_motor_left, mock_motor_right, mock_ears, mock_belt, - ledkit, mock_lcd, mock_videokit, bhvkit, cmdkit}; + timeout, battery, serialnumberkit, firmware_update, mock_motor_left, mock_motor_right, mock_ears, + mock_belt, ledkit, mock_lcd, mock_videokit, bhvkit, cmdkit, rfidkit}; ble::GapMock &mbed_mock_gap = ble::gap_mock(); ble::GattServerMock &mbed_mock_gatt = ble::gatt_server_mock(); diff --git a/libs/RobotKit/tests/RobotController_test_stateCharging.cpp b/libs/RobotKit/tests/RobotController_test_stateCharging.cpp index 8dba38f78d..e990e0f58d 100644 --- a/libs/RobotKit/tests/RobotController_test_stateCharging.cpp +++ b/libs/RobotKit/tests/RobotController_test_stateCharging.cpp @@ -179,7 +179,7 @@ TEST_F(RobotControllerTest, stateChargingEventEmergencyStop) EXPECT_CALL(mock_lcd, turnOff).Times(1); EXPECT_CALL(mock_videokit, stopVideo).Times(2); - rc.raiseEmergencyStop(); + rc.onMagicCardAvailable(MagicCard::emergency_stop); EXPECT_TRUE(rc.state_machine.is(lksm::state::emergency_stopped)); } diff --git a/libs/RobotKit/tests/RobotController_test_stateIdle.cpp b/libs/RobotKit/tests/RobotController_test_stateIdle.cpp index f11b3cf4ba..ea44695a0d 100644 --- a/libs/RobotKit/tests/RobotController_test_stateIdle.cpp +++ b/libs/RobotKit/tests/RobotController_test_stateIdle.cpp @@ -131,7 +131,7 @@ TEST_F(RobotControllerTest, stateIdleEventEmergencyStop) EXPECT_CALL(mock_lcd, turnOff).Times(1); EXPECT_CALL(mock_videokit, stopVideo).Times(AtLeast(1)); - rc.raiseEmergencyStop(); + rc.onMagicCardAvailable(MagicCard::emergency_stop); EXPECT_TRUE(rc.state_machine.is(lksm::state::emergency_stopped)); } diff --git a/libs/RobotKit/tests/RobotController_test_stateSleeping.cpp b/libs/RobotKit/tests/RobotController_test_stateSleeping.cpp index 8dc1704481..b16db56662 100644 --- a/libs/RobotKit/tests/RobotController_test_stateSleeping.cpp +++ b/libs/RobotKit/tests/RobotController_test_stateSleeping.cpp @@ -106,7 +106,7 @@ TEST_F(RobotControllerTest, stateSleepingEventEmergencyStop) EXPECT_CALL(mock_lcd, turnOff).Times(1); EXPECT_CALL(mock_videokit, stopVideo).Times(AtLeast(1)); - rc.raiseEmergencyStop(); + rc.onMagicCardAvailable(MagicCard::emergency_stop); EXPECT_TRUE(rc.state_machine.is(lksm::state::emergency_stopped)); } diff --git a/libs/RobotKit/tests/RobotController_test_stateWorking.cpp b/libs/RobotKit/tests/RobotController_test_stateWorking.cpp index 88a2dfd176..6487598caa 100644 --- a/libs/RobotKit/tests/RobotController_test_stateWorking.cpp +++ b/libs/RobotKit/tests/RobotController_test_stateWorking.cpp @@ -74,7 +74,7 @@ TEST_F(RobotControllerTest, stateWorkingEventEmergencyStop) EXPECT_CALL(mock_lcd, turnOff).Times(1); EXPECT_CALL(mock_videokit, stopVideo).Times(2); - rc.raiseEmergencyStop(); + rc.onMagicCardAvailable(MagicCard::emergency_stop); EXPECT_TRUE(rc.state_machine.is(lksm::state::emergency_stopped)); } From 62af1fa898b093e457d6e865a611b8538f37f0d5 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Mon, 5 Sep 2022 13:28:14 +0200 Subject: [PATCH 057/130] :fire: (os): Remove onMagicCardAvailable from os --- app/os/main.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/app/os/main.cpp b/app/os/main.cpp index ffb53c99fb..c98436b575 100644 --- a/app/os/main.cpp +++ b/app/os/main.cpp @@ -347,15 +347,9 @@ namespace robot { display::videokit, behaviorkit, commandkit, + rfidkit, }; - void onMagicCardAvailable(const MagicCard &card) - { - if (card == MagicCard::emergency_stop) { - controller.raiseEmergencyStop(); - } - } - } // namespace robot namespace watchdog { @@ -478,7 +472,6 @@ auto main() -> int hello.start(); sd::init(); - rfidkit.init(); firmware::initializeFlash(); commandkit.registerCommand(command::list); @@ -488,8 +481,6 @@ auto main() -> int robot::controller.registerOnFactoryResetNotificationCallback(factory_reset::set); robot::controller.registerEvents(); - rfidkit.onTagActivated(robot::onMagicCardAvailable); - // TODO(@team): Add functional test prior confirming the firmware firmware::confirmFirmware(); From 54d6c700e89948597768f260d90331db0c71f1c2 Mon Sep 17 00:00:00 2001 From: Mourad Latoundji Date: Mon, 5 Sep 2022 16:10:44 +0200 Subject: [PATCH 058/130] :sparkles: (interface): Add TouchSensor --- include/interface/drivers/TouchSensor.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 include/interface/drivers/TouchSensor.h diff --git a/include/interface/drivers/TouchSensor.h b/include/interface/drivers/TouchSensor.h new file mode 100644 index 0000000000..7e0a539c4d --- /dev/null +++ b/include/interface/drivers/TouchSensor.h @@ -0,0 +1,22 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +namespace leka::interface { + +class TouchSensor +{ + public: + virtual ~TouchSensor() = default; + + virtual void init() = 0; + virtual auto read() -> bool = 0; + virtual void reset() = 0; + virtual void setSensitivity(uint16_t value) = 0; +}; + +} // namespace leka::interface From 961b0e8526cbd97c41c65bbbabfb829db254ab21 Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Mon, 5 Sep 2022 16:26:19 +0200 Subject: [PATCH 059/130] :sparkles: (drivers): Add CoreTouchSensor --- drivers/CMakeLists.txt | 1 + drivers/CoreTouchSensor/CMakeLists.txt | 30 +++++++ .../CoreTouchSensor/include/CoreTouchSensor.h | 48 +++++++++++ .../source/CoreTouchSensor.cpp | 47 +++++++++++ .../tests/CoreTouchSensor_test.cpp | 84 +++++++++++++++++++ tests/unit/CMakeLists.txt | 1 + 6 files changed, 211 insertions(+) create mode 100644 drivers/CoreTouchSensor/CMakeLists.txt create mode 100644 drivers/CoreTouchSensor/include/CoreTouchSensor.h create mode 100644 drivers/CoreTouchSensor/source/CoreTouchSensor.cpp create mode 100644 drivers/CoreTouchSensor/tests/CoreTouchSensor_test.cpp diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index f3154b563c..c0436c0302 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -40,5 +40,6 @@ add_subdirectory(${DRIVERS_DIR}/CoreMotor) # Touch drivers add_subdirectory(${DRIVERS_DIR}/CoreIOExpander) add_subdirectory(${DRIVERS_DIR}/CoreQDAC) +add_subdirectory(${DRIVERS_DIR}/CoreTouchSensor) diff --git a/drivers/CoreTouchSensor/CMakeLists.txt b/drivers/CoreTouchSensor/CMakeLists.txt new file mode 100644 index 0000000000..665bb40312 --- /dev/null +++ b/drivers/CoreTouchSensor/CMakeLists.txt @@ -0,0 +1,30 @@ +# Leka - LekaOS +# Copyright 2022 APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +add_library(CoreTouchSensor STATIC) + +target_include_directories(CoreTouchSensor + PUBLIC + include +) + +target_sources(CoreTouchSensor + PRIVATE + source/CoreTouchSensor.cpp +) + +target_link_libraries(CoreTouchSensor + PRIVATE + mbed-os + CoreI2C + CoreIOExpander + CoreQDAC + IOKit +) + +if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") + leka_unit_tests_sources( + tests/CoreTouchSensor_test.cpp + ) +endif() diff --git a/drivers/CoreTouchSensor/include/CoreTouchSensor.h b/drivers/CoreTouchSensor/include/CoreTouchSensor.h new file mode 100644 index 0000000000..9d379562c8 --- /dev/null +++ b/drivers/CoreTouchSensor/include/CoreTouchSensor.h @@ -0,0 +1,48 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "drivers/interfaces/InterfaceDigitalIn.h" +#include "drivers/interfaces/InterfaceDigitalOut.h" + +#include "interface/drivers/QDAC.h" +#include "interface/drivers/TouchSensor.h" + +namespace leka { + +class CoreTouchSensor : public interface::TouchSensor +{ + public: + static constexpr auto default_max_sensitivity_value = uint16_t {0x0FFF}; + static constexpr auto default_min_sensitivity_value = uint16_t {0x0000}; + + explicit CoreTouchSensor(mbed::interface::DigitalIn &detect_pin, mbed::interface::DigitalOut &power_mode_pin, + interface::QDAC &dac, uint8_t channel) + : _detect_pin(detect_pin), _power_mode_pin(power_mode_pin), _sensitivity_pin({dac, channel}) {}; + void init() final; + auto read() -> bool final; + void reset() final; + void setSensitivity(uint16_t value) final; + + private: + enum class PowerMode : uint8_t + { + low = 0, + normal = 1 + }; + void setPowerMode(PowerMode power_mode); + + mbed::interface::DigitalIn &_detect_pin; + mbed::interface::DigitalOut &_power_mode_pin; + struct AnalogOut { + interface::QDAC &dac; + uint8_t channel; + }; + AnalogOut _sensitivity_pin; + + bool _state {}; +}; + +} // namespace leka diff --git a/drivers/CoreTouchSensor/source/CoreTouchSensor.cpp b/drivers/CoreTouchSensor/source/CoreTouchSensor.cpp new file mode 100644 index 0000000000..577faf7fb4 --- /dev/null +++ b/drivers/CoreTouchSensor/source/CoreTouchSensor.cpp @@ -0,0 +1,47 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "CoreTouchSensor.h" + +#include "rtos/ThisThread.h" + +#include "MathUtils.h" + +using namespace leka; +using namespace std::chrono_literals; + +void CoreTouchSensor::init() +{ + _detect_pin.mode(PinMode::PullUp); + setPowerMode(PowerMode::normal); + _sensitivity_pin.dac.init(); +} + +auto CoreTouchSensor::read() -> bool +{ + _state = (1 == _detect_pin.read()); + return _state; +} + +void CoreTouchSensor::reset() +{ + setPowerMode(PowerMode::low); + rtos::ThisThread::sleep_for(110ms); + setPowerMode(PowerMode::normal); + rtos::ThisThread::sleep_for(1ms); +} + +void CoreTouchSensor::setSensitivity(uint16_t value) +{ + auto inverted_value = + utils::math::map(value, default_min_sensitivity_value, default_max_sensitivity_value, + default_max_sensitivity_value, default_min_sensitivity_value); + _sensitivity_pin.dac.write(_sensitivity_pin.channel, inverted_value); +} + +void CoreTouchSensor::setPowerMode(PowerMode power_mode) +{ + auto pm = static_cast(power_mode); + _power_mode_pin.write(pm); +} diff --git a/drivers/CoreTouchSensor/tests/CoreTouchSensor_test.cpp b/drivers/CoreTouchSensor/tests/CoreTouchSensor_test.cpp new file mode 100644 index 0000000000..f416bfa4d3 --- /dev/null +++ b/drivers/CoreTouchSensor/tests/CoreTouchSensor_test.cpp @@ -0,0 +1,84 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "CoreTouchSensor.h" + +#include "IOKit/DigitalIn.h" +#include "IOKit/DigitalOut.h" +#include "MathUtils.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "mocks/leka/CoreI2C.h" +#include "mocks/leka/CoreQDAC.h" +#include "mocks/leka/IOExpander.h" + +using namespace leka; + +using ::testing::Args; +using ::testing::DoAll; +using ::testing::ElementsAre; +using ::testing::Return; +using ::testing::SetArrayArgument; + +class CoreTouchSensorTest : public ::testing::Test +{ + protected: + // void SetUp() override {} + // void TearDown() override {} + + mock::IOExpander mockIOExpander {}; + uint8_t pin_number {0x0001}; + + leka::io::expanded::DigitalIn<> in {mockIOExpander, pin_number}; + leka::io::expanded::DigitalOut<> out {mockIOExpander, pin_number}; + mock::CoreQDAC dac {}; + uint8_t channel {0x01}; + + CoreTouchSensor sensor {in, out, dac, channel}; +}; + +TEST_F(CoreTouchSensorTest, initializationDefault) +{ + auto new_sensor = CoreTouchSensor {in, out, dac, channel}; + ASSERT_NE(&new_sensor, nullptr); +} + +TEST_F(CoreTouchSensorTest, init) +{ + EXPECT_CALL(dac, init).Times(1); + + sensor.init(); +} + +TEST_F(CoreTouchSensorTest, read) +{ + auto expected_read = bool {false}; + EXPECT_CALL(mockIOExpander, readPin(pin_number)).Times(1).WillOnce(Return(expected_read)); + auto actual_read = sensor.read(); + + EXPECT_EQ(actual_read, expected_read); +} + +TEST_F(CoreTouchSensorTest, reset) +{ + auto expected_value = uint8_t {}; + + expected_value = 0x00; + EXPECT_CALL(mockIOExpander, writePin(pin_number, expected_value)).Times(1); + + expected_value = 0x01; + EXPECT_CALL(mockIOExpander, writePin(pin_number, expected_value)).Times(1); + + sensor.reset(); +} + +TEST_F(CoreTouchSensorTest, setSensitivity) +{ + auto value = 0x0ABC; + auto expected_value = utils::math::map( + value, CoreTouchSensor::default_min_sensitivity_value, CoreTouchSensor::default_max_sensitivity_value, + CoreTouchSensor::default_max_sensitivity_value, CoreTouchSensor::default_min_sensitivity_value); + EXPECT_CALL(dac, write(channel, expected_value)).Times(1); + sensor.setSensitivity(value); +} diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index f3e8052a2b..3aa0b7c802 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -264,6 +264,7 @@ leka_register_unit_tests_for_driver(CoreRFIDReader) leka_register_unit_tests_for_driver(CoreSPI) leka_register_unit_tests_for_driver(CoreSTM32Hal) leka_register_unit_tests_for_driver(CoreTicker) +leka_register_unit_tests_for_driver(CoreTouchSensor) leka_register_unit_tests_for_driver(CoreVideo) # Register libraries From 14245cc876addf9901cc42cae5025a32f1ca3051 Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Mon, 5 Sep 2022 16:27:04 +0200 Subject: [PATCH 060/130] :clown_face: (mocks): Mock CoreTouchSensor --- tests/unit/mocks/mocks/leka/CoreTouchSensor.h | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/unit/mocks/mocks/leka/CoreTouchSensor.h diff --git a/tests/unit/mocks/mocks/leka/CoreTouchSensor.h b/tests/unit/mocks/mocks/leka/CoreTouchSensor.h new file mode 100644 index 0000000000..453fa5a245 --- /dev/null +++ b/tests/unit/mocks/mocks/leka/CoreTouchSensor.h @@ -0,0 +1,21 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "gmock/gmock.h" +#include "interface/drivers/TouchSensor.h" + +namespace leka::mock { + +class CoreTouchSensor : public interface::TouchSensor +{ + public: + MOCK_METHOD(void, init, (), (override)); + MOCK_METHOD(bool, read, (), (override)); + MOCK_METHOD(void, reset, (), (override)); + MOCK_METHOD(void, setSensitivity, (uint16_t), (override)); +}; + +} // namespace leka::mock From 1904ae5d0b926ebb3866d27e8ef82e5846738670 Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Mon, 5 Sep 2022 16:43:34 +0200 Subject: [PATCH 061/130] :bug: (CommanKit): Add rainbow as default reinforcer --- libs/CommandKit/include/commands/ReinforcerCommand.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/CommandKit/include/commands/ReinforcerCommand.h b/libs/CommandKit/include/commands/ReinforcerCommand.h index ce7f7fabeb..5bbf68fcb9 100644 --- a/libs/CommandKit/include/commands/ReinforcerCommand.h +++ b/libs/CommandKit/include/commands/ReinforcerCommand.h @@ -52,6 +52,7 @@ struct ReinforcerCommand : interface::Command { _behaviorkit.blinkGreen(); break; default: + _behaviorkit.rainbow(); break; } From e5c217cffb20075c4f2cb8f9484849df1f147d03 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Thu, 25 Aug 2022 19:55:02 +0200 Subject: [PATCH 062/130] :sparkles: (libs): Add ReinforcerKit --- libs/CMakeLists.txt | 1 + libs/ReinforcerKit/CMakeLists.txt | 25 +++ libs/ReinforcerKit/include/ReinforcerKit.h | 52 ++++++ libs/ReinforcerKit/source/ReinforcerKit.cpp | 100 +++++++++++ .../tests/ReinforcerKit_test.cpp | 164 ++++++++++++++++++ tests/unit/CMakeLists.txt | 1 + 6 files changed, 343 insertions(+) create mode 100644 libs/ReinforcerKit/CMakeLists.txt create mode 100644 libs/ReinforcerKit/include/ReinforcerKit.h create mode 100644 libs/ReinforcerKit/source/ReinforcerKit.cpp create mode 100644 libs/ReinforcerKit/tests/ReinforcerKit_test.cpp diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index cb6aaad167..78dc8f6941 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -15,6 +15,7 @@ add_subdirectory(${LIBS_DIR}/FileManagerKit) add_subdirectory(${LIBS_DIR}/FirmwareKit) add_subdirectory(${LIBS_DIR}/IOKit) add_subdirectory(${LIBS_DIR}/LedKit) +add_subdirectory(${LIBS_DIR}/ReinforcerKit) add_subdirectory(${LIBS_DIR}/RobotKit) add_subdirectory(${LIBS_DIR}/RFIDKit) add_subdirectory(${LIBS_DIR}/SerialNumberKit) diff --git a/libs/ReinforcerKit/CMakeLists.txt b/libs/ReinforcerKit/CMakeLists.txt new file mode 100644 index 0000000000..5c50fd14da --- /dev/null +++ b/libs/ReinforcerKit/CMakeLists.txt @@ -0,0 +1,25 @@ +# Leka - LekaOS +# Copyright 2022 APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +add_library(ReinforcerKit STATIC) + +target_include_directories(ReinforcerKit + PUBLIC + include +) + +target_sources(ReinforcerKit + PRIVATE + source/ReinforcerKit.cpp +) + +target_link_libraries(ReinforcerKit + LedKit +) + +if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") + leka_unit_tests_sources( + tests/ReinforcerKit_test.cpp + ) +endif() diff --git a/libs/ReinforcerKit/include/ReinforcerKit.h b/libs/ReinforcerKit/include/ReinforcerKit.h new file mode 100644 index 0000000000..bc9ba17ac6 --- /dev/null +++ b/libs/ReinforcerKit/include/ReinforcerKit.h @@ -0,0 +1,52 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +#include "LedKit.h" + +namespace leka { + +class ReinforcerKit +{ + public: + explicit ReinforcerKit(interface::VideoKit &videokit, LedKit &ledkit, interface::Motor &motor_left, + interface::Motor &motor_right) + : _videokit(videokit), _ledkit(ledkit), _motor_left(motor_left), _motor_right(motor_right) + { + // nothing do to + } + + enum class Reinforcer + { + BlinkGreen = 0, + SpinBlink = 1, + Fire = 2, + Sprinkles = 3, + Rainbow = 4 + }; + + void playDefault(); + void play(const Reinforcer &reinforcer); + void setDefaultReinforcer(const Reinforcer &reinforcer); + void stop(); + + private: + interface::VideoKit &_videokit; + LedKit &_ledkit; + interface::Motor &_motor_left; + interface::Motor &_motor_right; + Reinforcer _default_reinforcer = Reinforcer::Rainbow; + + void playBlinkGreen(); + void playSpinBlink(); + void playFire(); + void playSprinkles(); + void playRainbow(); +}; + +} // namespace leka diff --git a/libs/ReinforcerKit/source/ReinforcerKit.cpp b/libs/ReinforcerKit/source/ReinforcerKit.cpp new file mode 100644 index 0000000000..582836dea8 --- /dev/null +++ b/libs/ReinforcerKit/source/ReinforcerKit.cpp @@ -0,0 +1,100 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "ReinforcerKit.h" + +using namespace leka; +using namespace std::chrono; + +static constexpr auto reinforcer_duration = 2700ms; + +void ReinforcerKit::setDefaultReinforcer(const Reinforcer &reinforcer) +{ + _default_reinforcer = reinforcer; +} + +void ReinforcerKit::play(const Reinforcer &reinforcer) +{ + switch (reinforcer) { + case Reinforcer::Rainbow: + playRainbow(); + break; + case Reinforcer::BlinkGreen: + playBlinkGreen(); + break; + case Reinforcer::SpinBlink: + playSpinBlink(); + break; + case Reinforcer::Fire: + playFire(); + break; + case Reinforcer::Sprinkles: + playSprinkles(); + break; + default: + playRainbow(); + break; + } +} + +void ReinforcerKit::playDefault() +{ + play(_default_reinforcer); +} + +void ReinforcerKit::stop() +{ + _ledkit.stop(); + _motor_left.stop(); + _motor_right.stop(); + _videokit.stopVideo(); +} + +void ReinforcerKit::playBlinkGreen() +{ + _motor_left.spin(Rotation::clockwise, 1); + _motor_right.spin(Rotation::clockwise, 1); + _ledkit.start(&LedKit::animation::blink_green); + _videokit.playVideoOnce("/fs/home/vid/system/robot-system-reinforcer-happy-no_eyebrows.avi"); + rtos::ThisThread::sleep_for(reinforcer_duration); + _ledkit.stop(); + _motor_left.stop(); + _motor_right.stop(); +} + +void ReinforcerKit::playSpinBlink() +{ + _motor_left.spin(Rotation::counterClockwise, 1); + _motor_right.spin(Rotation::counterClockwise, 1); + _ledkit.start(&LedKit::animation::spin_blink); + _videokit.playVideoOnce("/fs/home/vid/system/robot-system-reinforcer-happy-no_eyebrows.avi"); + rtos::ThisThread::sleep_for(reinforcer_duration); + _ledkit.stop(); + _motor_left.stop(); + _motor_right.stop(); +} + +void ReinforcerKit::playFire() +{ + _ledkit.start(&LedKit::animation::fire); + _videokit.playVideoOnce("/fs/home/vid/system/robot-system-reinforcer-happy-no_eyebrows.avi"); + rtos::ThisThread::sleep_for(reinforcer_duration); + _ledkit.stop(); +} + +void ReinforcerKit::playSprinkles() +{ + _ledkit.start(&LedKit::animation::sprinkles); + _videokit.playVideoOnce("/fs/home/vid/system/robot-system-reinforcer-happy-no_eyebrows.avi"); + rtos::ThisThread::sleep_for(reinforcer_duration); + _ledkit.stop(); +} + +void ReinforcerKit::playRainbow() +{ + _ledkit.start(&LedKit::animation::rainbow); + _videokit.playVideoOnce("/fs/home/vid/system/robot-system-reinforcer-happy-no_eyebrows.avi"); + rtos::ThisThread::sleep_for(reinforcer_duration); + _ledkit.stop(); +} diff --git a/libs/ReinforcerKit/tests/ReinforcerKit_test.cpp b/libs/ReinforcerKit/tests/ReinforcerKit_test.cpp new file mode 100644 index 0000000000..edd3574fe4 --- /dev/null +++ b/libs/ReinforcerKit/tests/ReinforcerKit_test.cpp @@ -0,0 +1,164 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "ReinforcerKit.h" + +#include "LedKit.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "mocks/leka/CoreLED.h" +#include "mocks/leka/CoreMotor.h" +#include "mocks/leka/LEDAnimation.h" +#include "mocks/leka/VideoKit.h" +#include "stubs/leka/EventLoopKit.h" + +using namespace leka; + +using ::testing::InSequence; + +class ReinforcerkitTest : public ::testing::Test +{ + protected: + ReinforcerkitTest() : reinforcerkit(mock_videokit, ledkit, mock_motor_left, mock_motor_right) {}; + + // void SetUp() override {} + // void TearDown() override {} + + mock::VideoKit mock_videokit {}; + + mock::CoreLED mock_ears; + mock::CoreLED mock_belt; + + stub::EventLoopKit stub_event_loop; + + LedKit ledkit {stub_event_loop, mock_ears, mock_belt}; + + mock::LEDAnimation mock_animation {}; + + mock::CoreMotor mock_motor_left {}; + mock::CoreMotor mock_motor_right {}; + + ReinforcerKit reinforcerkit; +}; + +TEST_F(ReinforcerkitTest, initialization) +{ + ASSERT_NE(&reinforcerkit, nullptr); +} + +TEST_F(ReinforcerkitTest, playBlinkGreen) +{ + auto expected_speed = 1; + + EXPECT_CALL(mock_videokit, playVideoOnce); + { + InSequence seq; + + EXPECT_CALL(mock_motor_left, spin(Rotation::clockwise, expected_speed)); + EXPECT_CALL(mock_motor_right, spin(Rotation::clockwise, expected_speed)); + + EXPECT_CALL(mock_motor_left, stop()); + EXPECT_CALL(mock_motor_right, stop()); + } + + reinforcerkit.play(ReinforcerKit::Reinforcer::BlinkGreen); +} + +TEST_F(ReinforcerkitTest, playSpinBlink) +{ + auto expected_speed = 1; + + EXPECT_CALL(mock_videokit, playVideoOnce); + + { + InSequence seq; + + EXPECT_CALL(mock_motor_left, spin(Rotation::counterClockwise, expected_speed)); + EXPECT_CALL(mock_motor_right, spin(Rotation::counterClockwise, expected_speed)); + + EXPECT_CALL(mock_motor_left, stop()); + EXPECT_CALL(mock_motor_right, stop()); + } + + reinforcerkit.play(ReinforcerKit::Reinforcer::SpinBlink); +} + +TEST_F(ReinforcerkitTest, playFire) +{ + EXPECT_CALL(mock_videokit, playVideoOnce); + reinforcerkit.play(ReinforcerKit::Reinforcer::Fire); +} + +TEST_F(ReinforcerkitTest, playSprinkles) +{ + EXPECT_CALL(mock_videokit, playVideoOnce); + reinforcerkit.play(ReinforcerKit::Reinforcer::Sprinkles); +} + +TEST_F(ReinforcerkitTest, playRainbow) +{ + EXPECT_CALL(mock_videokit, playVideoOnce); + reinforcerkit.play(ReinforcerKit::Reinforcer::Rainbow); +} + +TEST_F(ReinforcerkitTest, PlayDefaultReinforcer) +{ + auto expected_speed = 1; + + EXPECT_CALL(mock_videokit, playVideoOnce); + + reinforcerkit.playDefault(); +} + +TEST_F(ReinforcerkitTest, SetSpinBlinkAndPlayDefaultReinforcer) +{ + auto expected_speed = 1; + + EXPECT_CALL(mock_videokit, playVideoOnce); + { + InSequence seq; + + EXPECT_CALL(mock_motor_left, spin(Rotation::counterClockwise, expected_speed)); + EXPECT_CALL(mock_motor_right, spin(Rotation::counterClockwise, expected_speed)); + + EXPECT_CALL(mock_motor_left, stop()); + EXPECT_CALL(mock_motor_right, stop()); + } + + reinforcerkit.setDefaultReinforcer(ReinforcerKit::Reinforcer::SpinBlink); + reinforcerkit.playDefault(); +} + +TEST_F(ReinforcerkitTest, SetFireAndPlayDefaultReinforcer) +{ + EXPECT_CALL(mock_videokit, playVideoOnce); + + reinforcerkit.setDefaultReinforcer(ReinforcerKit::Reinforcer::Fire); + reinforcerkit.playDefault(); +} + +TEST_F(ReinforcerkitTest, SetSprinklesAndPlayDefaultReinforcer) +{ + EXPECT_CALL(mock_videokit, playVideoOnce); + + reinforcerkit.setDefaultReinforcer(ReinforcerKit::Reinforcer::Sprinkles); + reinforcerkit.playDefault(); +} + +TEST_F(ReinforcerkitTest, SetRainbowAndPlayDefaultReinforcer) +{ + EXPECT_CALL(mock_videokit, playVideoOnce); + + reinforcerkit.setDefaultReinforcer(ReinforcerKit::Reinforcer::Rainbow); + reinforcerkit.playDefault(); +} + +TEST_F(ReinforcerkitTest, stop) +{ + EXPECT_CALL(mock_videokit, stopVideo); + EXPECT_CALL(mock_motor_left, stop); + EXPECT_CALL(mock_motor_right, stop); + + reinforcerkit.stop(); +} diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 3aa0b7c802..752f0fd365 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -282,6 +282,7 @@ leka_register_unit_tests_for_library(FirmwareKit) leka_register_unit_tests_for_library(WebKit) leka_register_unit_tests_for_library(IOKit) leka_register_unit_tests_for_library(LedKit) +leka_register_unit_tests_for_library(ReinforcerKit) leka_register_unit_tests_for_library(RobotKit) leka_register_unit_tests_for_library(RFIDKit) leka_register_unit_tests_for_library(SerialNumberKit) From 1bde9a51396b864b1eaf68e9eaa42b33840f5576 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Thu, 25 Aug 2022 20:44:39 +0200 Subject: [PATCH 063/130] :recycle: (CommandKit): Update ReinforcerCommand to use ReinforcerKit --- app/os/main.cpp | 6 ++++-- libs/CommandKit/CMakeLists.txt | 2 +- .../include/commands/ReinforcerCommand.h | 18 +++++++++--------- spikes/lk_command_kit/main.cpp | 8 ++++---- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/app/os/main.cpp b/app/os/main.cpp index c98436b575..ed7c15689f 100644 --- a/app/os/main.cpp +++ b/app/os/main.cpp @@ -42,6 +42,7 @@ #include "LogKit.h" #include "QSPIFBlockDevice.h" #include "RFIDKit.h" +#include "ReinforcerKit.h" #include "RobotController.h" #include "SDBlockDevice.h" #include "SerialNumberKit.h" @@ -242,7 +243,8 @@ namespace display { } // namespace display -auto behaviorkit = BehaviorKit {display::videokit, leds::kit, motors::left::motor, motors::right::motor}; +auto behaviorkit = BehaviorKit {display::videokit, leds::kit, motors::left::motor, motors::right::motor}; +auto reinforcerkit = ReinforcerKit {display::videokit, leds::kit, motors::left::motor, motors::right::motor}; namespace command { @@ -254,7 +256,7 @@ namespace command { auto led_full = LedFullCommand {leds::ears, leds::belt}; auto led_range = LedRangeCommand {leds::ears, leds::belt}; auto motors = MotorsCommand {motors::left::motor, motors::right::motor}; - auto reinforcer = ReinforcerCommand {behaviorkit}; + auto reinforcer = ReinforcerCommand {reinforcerkit}; } // namespace internal diff --git a/libs/CommandKit/CMakeLists.txt b/libs/CommandKit/CMakeLists.txt index d17bf3ab64..e45b24d8e4 100644 --- a/libs/CommandKit/CMakeLists.txt +++ b/libs/CommandKit/CMakeLists.txt @@ -18,7 +18,7 @@ target_link_libraries(CommandKit CoreEventQueue CoreLED CoreMotor - BehaviorKit + ReinforcerKit EventLoopKit mbed-os ) diff --git a/libs/CommandKit/include/commands/ReinforcerCommand.h b/libs/CommandKit/include/commands/ReinforcerCommand.h index 5bbf68fcb9..2e1a16fbc4 100644 --- a/libs/CommandKit/include/commands/ReinforcerCommand.h +++ b/libs/CommandKit/include/commands/ReinforcerCommand.h @@ -6,14 +6,14 @@ #include -#include "BehaviorKit.h" +#include "ReinforcerKit.h" #include "Utils.h" #include "interface/Command.h" namespace leka { struct ReinforcerCommand : interface::Command { - explicit ReinforcerCommand(BehaviorKit &kit) : _behaviorkit(kit) {} + explicit ReinforcerCommand(ReinforcerKit &kit) : _reinforcerkit(kit) {} auto id() -> uint8_t override { return cmd::id; } @@ -37,22 +37,22 @@ struct ReinforcerCommand : interface::Command { switch (id) { case cmd::motivator::rainbow: - _behaviorkit.rainbow(); + _reinforcerkit.play(ReinforcerKit::Reinforcer::Rainbow); break; case cmd::motivator::fire: - _behaviorkit.fire(); + _reinforcerkit.play(ReinforcerKit::Reinforcer::Fire); break; case cmd::motivator::sprinkles: - _behaviorkit.sprinkles(); + _reinforcerkit.play(ReinforcerKit::Reinforcer::Sprinkles); break; case cmd::motivator::spinblink: - _behaviorkit.spinBlink(); + _reinforcerkit.play(ReinforcerKit::Reinforcer::SpinBlink); break; case cmd::motivator::blinkgreen: - _behaviorkit.blinkGreen(); + _reinforcerkit.play(ReinforcerKit::Reinforcer::BlinkGreen); break; default: - _behaviorkit.rainbow(); + _reinforcerkit.play(ReinforcerKit::Reinforcer::Rainbow); break; } @@ -74,7 +74,7 @@ struct ReinforcerCommand : interface::Command { }; std::array args {}; - BehaviorKit &_behaviorkit; + ReinforcerKit &_reinforcerkit; }; } // namespace leka diff --git a/spikes/lk_command_kit/main.cpp b/spikes/lk_command_kit/main.cpp index e4cec62175..7d71b7b817 100644 --- a/spikes/lk_command_kit/main.cpp +++ b/spikes/lk_command_kit/main.cpp @@ -7,7 +7,6 @@ #include "rtos/ThisThread.h" -#include "BehaviorKit.h" #include "CommandKit.h" #include "CoreDMA2D.hpp" #include "CoreDSI.hpp" @@ -32,6 +31,7 @@ #include "FATFileSystem.h" #include "HelloWorld.h" #include "LogKit.h" +#include "ReinforcerKit.h" #include "SDBlockDevice.h" #include "VideoKit.h" #include "commands/LedFullCommand.h" @@ -121,7 +121,7 @@ auto videokit = VideoKit {internal::event_flags, internal::corevideo}; } // namespace display -auto behaviorkit = BehaviorKit {display::videokit, ledkit, motor::left, motor::right}; +auto reinforcerkit = ReinforcerKit {display::videokit, ledkit, motor::left, motor::right}; namespace command { @@ -134,7 +134,7 @@ namespace internal { auto led_full = LedFullCommand {leds::ears, leds::belt}; auto led_range = LedRangeCommand {leds::ears, leds::belt}; auto motors = MotorsCommand {motor::left, motor::right}; - auto reinforcer = ReinforcerCommand {behaviorkit}; + auto reinforcer = ReinforcerCommand {reinforcerkit}; } // namespace internal @@ -196,7 +196,7 @@ void turnOff() leds::ears.show(); leds::belt.setColor(RGB::black); leds::belt.show(); - behaviorkit.stop(); + reinforcerkit.stop(); log_debug("turn off end"); }; From 0afcd0beba83276fa8306fc2ac499519dff17959 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Thu, 25 Aug 2022 19:55:41 +0200 Subject: [PATCH 064/130] :fire: (BehaviorKit): Remove reinforcer methods --- libs/BehaviorKit/include/BehaviorKit.h | 6 --- libs/BehaviorKit/source/BehaviorKit.cpp | 48 ----------------- libs/BehaviorKit/tests/BehaviorKit_test.cpp | 60 --------------------- spikes/lk_behavior_kit/main.cpp | 25 --------- 4 files changed, 139 deletions(-) diff --git a/libs/BehaviorKit/include/BehaviorKit.h b/libs/BehaviorKit/include/BehaviorKit.h index e46f970918..d6b9aea6b6 100644 --- a/libs/BehaviorKit/include/BehaviorKit.h +++ b/libs/BehaviorKit/include/BehaviorKit.h @@ -35,12 +35,6 @@ class BehaviorKit void chargingHigh(); void chargingFull(); - void blinkGreen(); - void spinBlink(); - void fire(); - void sprinkles(); - void rainbow(); - void bleConnection(bool with_video); void working(); void chooseActivity(); diff --git a/libs/BehaviorKit/source/BehaviorKit.cpp b/libs/BehaviorKit/source/BehaviorKit.cpp index 1643f79e94..282c6eca52 100644 --- a/libs/BehaviorKit/source/BehaviorKit.cpp +++ b/libs/BehaviorKit/source/BehaviorKit.cpp @@ -10,8 +10,6 @@ namespace leka { using namespace std::chrono; -inline constexpr auto reinforcer_duration = 2700ms; - void BehaviorKit::spinLeft(float speed) { _motor_left.spin(Rotation::clockwise, speed); @@ -74,52 +72,6 @@ void BehaviorKit::chargingFull() _videokit.displayImage("/fs/home/img/system/robot-battery-charging-quarter_4-green.jpg"); } -void BehaviorKit::blinkGreen() -{ - spinLeft(1); - _ledkit.start(&LedKit::animation::blink_green); - _videokit.playVideoOnce("/fs/home/vid/system/robot-system-reinforcer-happy-no_eyebrows.avi"); - rtos::ThisThread::sleep_for(reinforcer_duration); - _ledkit.stop(); - _motor_left.stop(); - _motor_right.stop(); -} - -void BehaviorKit::spinBlink() -{ - spinRight(1); - _ledkit.start(&LedKit::animation::spin_blink); - _videokit.playVideoOnce("/fs/home/vid/system/robot-system-reinforcer-happy-no_eyebrows.avi"); - rtos::ThisThread::sleep_for(reinforcer_duration); - _ledkit.stop(); - _motor_left.stop(); - _motor_right.stop(); -} - -void BehaviorKit::fire() -{ - _ledkit.start(&LedKit::animation::fire); - _videokit.playVideoOnce("/fs/home/vid/system/robot-system-reinforcer-happy-no_eyebrows.avi"); - rtos::ThisThread::sleep_for(reinforcer_duration); - _ledkit.stop(); -} - -void BehaviorKit::sprinkles() -{ - _ledkit.start(&LedKit::animation::sprinkles); - _videokit.playVideoOnce("/fs/home/vid/system/robot-system-reinforcer-happy-no_eyebrows.avi"); - rtos::ThisThread::sleep_for(reinforcer_duration); - _ledkit.stop(); -} - -void BehaviorKit::rainbow() -{ - _ledkit.start(&LedKit::animation::rainbow); - _videokit.playVideoOnce("/fs/home/vid/system/robot-system-reinforcer-happy-no_eyebrows.avi"); - rtos::ThisThread::sleep_for(reinforcer_duration); - _ledkit.stop(); -} - void BehaviorKit::bleConnection(bool with_video) { _ledkit.start(&LedKit::animation::ble_connection); diff --git a/libs/BehaviorKit/tests/BehaviorKit_test.cpp b/libs/BehaviorKit/tests/BehaviorKit_test.cpp index 6a7304a23a..bdb39948d2 100644 --- a/libs/BehaviorKit/tests/BehaviorKit_test.cpp +++ b/libs/BehaviorKit/tests/BehaviorKit_test.cpp @@ -4,18 +4,13 @@ #include "BehaviorKit.h" -#include "rtos/tests/UNITTESTS/doubles/Thread_stub.h" - -#include "CorePwm.h" #include "LedKit.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "mocks/leka/CoreLED.h" #include "mocks/leka/CoreMotor.h" #include "mocks/leka/LEDAnimation.h" -#include "mocks/leka/PwmOut.h" #include "mocks/leka/VideoKit.h" -#include "mocks/mbed/DigitalOut.h" #include "stubs/leka/EventLoopKit.h" using namespace leka; @@ -104,61 +99,6 @@ TEST_F(BehaviorKitTest, batteryBehaviors) behaviorkit.chargingFull(); } -TEST_F(BehaviorKitTest, blinkGreen) -{ - auto expected_speed = 1; - - EXPECT_CALL(mock_videokit, playVideoOnce); - { - InSequence seq; - - EXPECT_CALL(mock_motor_left, spin(Rotation::clockwise, expected_speed)); - EXPECT_CALL(mock_motor_right, spin(Rotation::clockwise, expected_speed)); - - EXPECT_CALL(mock_motor_left, stop()); - EXPECT_CALL(mock_motor_right, stop()); - } - - behaviorkit.blinkGreen(); -} - -TEST_F(BehaviorKitTest, spinBlink) -{ - auto expected_speed = 1; - - EXPECT_CALL(mock_videokit, playVideoOnce); - - { - InSequence seq; - - EXPECT_CALL(mock_motor_left, spin(Rotation::counterClockwise, expected_speed)); - EXPECT_CALL(mock_motor_right, spin(Rotation::counterClockwise, expected_speed)); - - EXPECT_CALL(mock_motor_left, stop()); - EXPECT_CALL(mock_motor_right, stop()); - } - - behaviorkit.spinBlink(); -} - -TEST_F(BehaviorKitTest, fire) -{ - EXPECT_CALL(mock_videokit, playVideoOnce); - behaviorkit.fire(); -} - -TEST_F(BehaviorKitTest, sprinkles) -{ - EXPECT_CALL(mock_videokit, playVideoOnce); - behaviorkit.sprinkles(); -} - -TEST_F(BehaviorKitTest, rainbow) -{ - EXPECT_CALL(mock_videokit, playVideoOnce); - behaviorkit.rainbow(); -} - TEST_F(BehaviorKitTest, bleConnectionWhileCharging) { EXPECT_CALL(mock_videokit, playVideoOnce).Times(0); diff --git a/spikes/lk_behavior_kit/main.cpp b/spikes/lk_behavior_kit/main.cpp index 79a0c87a1b..fb4995c544 100644 --- a/spikes/lk_behavior_kit/main.cpp +++ b/spikes/lk_behavior_kit/main.cpp @@ -204,30 +204,5 @@ auto main() -> int rtos::ThisThread::sleep_for(10s); behaviorkit.stop(); rtos::ThisThread::sleep_for(3s); - - behaviorkit.blinkGreen(); - rtos::ThisThread::sleep_for(10s); - behaviorkit.stop(); - rtos::ThisThread::sleep_for(3s); - - behaviorkit.spinBlink(); - rtos::ThisThread::sleep_for(10s); - behaviorkit.stop(); - rtos::ThisThread::sleep_for(3s); - - behaviorkit.fire(); - rtos::ThisThread::sleep_for(10s); - behaviorkit.stop(); - rtos::ThisThread::sleep_for(3s); - - behaviorkit.sprinkles(); - rtos::ThisThread::sleep_for(10s); - behaviorkit.stop(); - rtos::ThisThread::sleep_for(3s); - - behaviorkit.rainbow(); - rtos::ThisThread::sleep_for(10s); - behaviorkit.stop(); - rtos::ThisThread::sleep_for(3s); } } From 76aba982885e1116eee10b85f61bca8f8ec20716 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Mon, 5 Sep 2022 15:47:15 +0200 Subject: [PATCH 065/130] :white_check_mark: (tests): Add EXPECT_CALL at RC's initialization test --- libs/RobotKit/tests/RobotController_test.h | 5 +++++ .../tests/RobotController_test_initializeComponents.cpp | 5 +++++ libs/RobotKit/tests/RobotController_test_stateIdle.cpp | 7 ++++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/libs/RobotKit/tests/RobotController_test.h b/libs/RobotKit/tests/RobotController_test.h index d1185d841d..1b54411ddd 100644 --- a/libs/RobotKit/tests/RobotController_test.h +++ b/libs/RobotKit/tests/RobotController_test.h @@ -139,6 +139,11 @@ class RobotControllerTest : public testing::Test expectedCallsStopMotors(); + EXPECT_CALL(mock_ears, setColor); + EXPECT_CALL(mock_belt, setColor); + EXPECT_CALL(mock_ears, show); + EXPECT_CALL(mock_belt, show); + EXPECT_CALL(mock_videokit, initializeScreen).Times(1); EXPECT_CALL(mock_lcd, turnOff).Times(1); EXPECT_CALL(mock_videokit, stopVideo).Times(1); diff --git a/libs/RobotKit/tests/RobotController_test_initializeComponents.cpp b/libs/RobotKit/tests/RobotController_test_initializeComponents.cpp index 9b3228a8fd..2a16a592a0 100644 --- a/libs/RobotKit/tests/RobotController_test_initializeComponents.cpp +++ b/libs/RobotKit/tests/RobotController_test_initializeComponents.cpp @@ -30,6 +30,11 @@ TEST_F(RobotControllerTest, initializeComponents) expectedCallsStopMotors(); + EXPECT_CALL(mock_ears, setColor).Times(1); + EXPECT_CALL(mock_belt, setColor).Times(1); + EXPECT_CALL(mock_ears, show).Times(1); + EXPECT_CALL(mock_belt, show).Times(1); + EXPECT_CALL(mock_videokit, initializeScreen).Times(1); EXPECT_CALL(mock_lcd, turnOff).Times(1); EXPECT_CALL(mock_videokit, stopVideo).Times(1); diff --git a/libs/RobotKit/tests/RobotController_test_stateIdle.cpp b/libs/RobotKit/tests/RobotController_test_stateIdle.cpp index ea44695a0d..52369aa3d7 100644 --- a/libs/RobotKit/tests/RobotController_test_stateIdle.cpp +++ b/libs/RobotKit/tests/RobotController_test_stateIdle.cpp @@ -13,6 +13,11 @@ TEST_F(RobotControllerTest, stateIdleEventTimeout) EXPECT_CALL(mock_videokit, stopVideo).InSequence(on_exit_idle_sequence); expectedCallsStopMotors(); + EXPECT_CALL(mock_belt, setColor).Times(AtLeast(1)); + EXPECT_CALL(mock_ears, setColor).Times(AtLeast(1)); + EXPECT_CALL(mock_belt, show).Times(AtLeast(1)); + EXPECT_CALL(mock_ears, show).Times(AtLeast(1)); + Sequence on_sleeping_sequence; EXPECT_CALL(mock_videokit, playVideoOnce).InSequence(on_sleeping_sequence); EXPECT_CALL(mock_lcd, turnOn).InSequence(on_sleeping_sequence); @@ -44,9 +49,9 @@ TEST_F(RobotControllerTest, stateIdleEventBleConnection) EXPECT_CALL(mock_motor_left, stop).Times(AtLeast(1)); EXPECT_CALL(mock_motor_right, stop).Times(AtLeast(1)); - EXPECT_CALL(mock_videokit, playVideoOnce).Times(AtLeast(1)); EXPECT_CALL(mock_belt, setColor).Times(AtLeast(1)); EXPECT_CALL(mock_belt, show).Times(AtLeast(1)); + EXPECT_CALL(mock_videokit, playVideoOnce).Times(AtLeast(1)); Sequence on_working_entry_sequence; EXPECT_CALL(timeout, onTimeout).InSequence(on_working_entry_sequence); From 4f618a06c227eeb66811521ffa582142717aa348 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Tue, 6 Sep 2022 17:59:58 +0200 Subject: [PATCH 066/130] :sparkles: (ActivityKit): Add isPlaying method --- libs/ActivityKit/include/ActivityKit.h | 2 ++ libs/ActivityKit/source/ActivityKit.cpp | 6 ++++++ libs/ActivityKit/tests/ActivityKit_test.cpp | 20 ++++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/libs/ActivityKit/include/ActivityKit.h b/libs/ActivityKit/include/ActivityKit.h index 2aadd6ebf8..9b9d27ba58 100644 --- a/libs/ActivityKit/include/ActivityKit.h +++ b/libs/ActivityKit/include/ActivityKit.h @@ -20,6 +20,8 @@ class ActivityKit void start(const MagicCard &card); void stop(); + [[nodiscard]] auto isPlaying() const -> bool; + private: interface::Activity *_current_activity = nullptr; std::unordered_map _activities {}; diff --git a/libs/ActivityKit/source/ActivityKit.cpp b/libs/ActivityKit/source/ActivityKit.cpp index 87307acab2..c2b4e2f446 100644 --- a/libs/ActivityKit/source/ActivityKit.cpp +++ b/libs/ActivityKit/source/ActivityKit.cpp @@ -31,4 +31,10 @@ void ActivityKit::stop() } _current_activity->stop(); + _current_activity = nullptr; +} + +auto ActivityKit::isPlaying() const -> bool +{ + return _current_activity != nullptr; } diff --git a/libs/ActivityKit/tests/ActivityKit_test.cpp b/libs/ActivityKit/tests/ActivityKit_test.cpp index 895318b28d..4333c58d09 100644 --- a/libs/ActivityKit/tests/ActivityKit_test.cpp +++ b/libs/ActivityKit/tests/ActivityKit_test.cpp @@ -79,3 +79,23 @@ TEST_F(ActivityKitTest, startNewActivitySequence) activitykit.start(MagicCard::number_1); } + +TEST_F(ActivityKitTest, isPlayingActivityNullPtr) +{ + EXPECT_FALSE(activitykit.isPlaying()); +} + +TEST_F(ActivityKitTest, isPlayingActivityStarted) +{ + activitykit.start(MagicCard::number_0); + + EXPECT_TRUE(activitykit.isPlaying()); +} + +TEST_F(ActivityKitTest, isPlayingActivityStopped) +{ + activitykit.start(MagicCard::number_0); + activitykit.stop(); + + EXPECT_FALSE(activitykit.isPlaying()); +} From 7f36fa4a459cd2047be86508cb2bdac85003d1ee Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Tue, 6 Sep 2022 18:00:48 +0200 Subject: [PATCH 067/130] :recycle: (Activity): Call backup RFID callback when redefining it --- libs/ActivityKit/source/activities/DisplayTags.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/libs/ActivityKit/source/activities/DisplayTags.cpp b/libs/ActivityKit/source/activities/DisplayTags.cpp index ae1023f3ab..956d6eeba3 100644 --- a/libs/ActivityKit/source/activities/DisplayTags.cpp +++ b/libs/ActivityKit/source/activities/DisplayTags.cpp @@ -14,13 +14,11 @@ void DisplayTags::start() _backup_callback = _rfidkit.getCallback(); - auto on_tag_detected_callback = [this](const MagicCard &card) { - if (card == MagicCard::remote_standard) { - stop(); - } else { - snprintf(_path_buffer.data(), _path_buffer.size(), "fs/home/img/id/%.4x.jpg", card.getId()); - _videokit.displayImage(_path_buffer.data()); - } + auto on_tag_detected_callback = [this](MagicCard &card) { + snprintf(_path_buffer.data(), _path_buffer.size(), "fs/home/img/id/%.4x.jpg", card.getId()); + _videokit.displayImage(_path_buffer.data()); + + _backup_callback(card); }; _rfidkit.onTagActivated(on_tag_detected_callback); From 3ad240c6a41e0de3f7f7f89657f259385f9ad36f Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Fri, 2 Sep 2022 18:53:35 +0200 Subject: [PATCH 068/130] :recycle: (RobotKit): Add autonomous activity mode to RC & SM --- libs/BehaviorKit/include/BehaviorKit.h | 2 +- libs/BehaviorKit/source/BehaviorKit.cpp | 2 +- libs/BehaviorKit/tests/BehaviorKit_test.cpp | 4 +- libs/RobotKit/CMakeLists.txt | 2 + libs/RobotKit/include/RobotController.h | 40 ++++- libs/RobotKit/include/StateMachine.h | 69 ++++++--- .../include/interface/RobotController.h | 3 + libs/RobotKit/tests/RobotController_test.h | 9 +- ...troller_test_stateAutonomousActivities.cpp | 146 ++++++++++++++++++ .../RobotController_test_stateCharging.cpp | 15 ++ ...tController_test_stateEmergencyStopped.cpp | 11 ++ .../tests/RobotController_test_stateIdle.cpp | 18 ++- .../RobotController_test_stateSleeping.cpp | 16 ++ .../RobotController_test_stateWorking.cpp | 14 ++ libs/RobotKit/tests/StateMachine_test.cpp | 129 ++++++++++++++++ libs/RobotKit/tests/mocks/RobotController.h | 3 + 16 files changed, 452 insertions(+), 31 deletions(-) create mode 100644 libs/RobotKit/tests/RobotController_test_stateAutonomousActivities.cpp diff --git a/libs/BehaviorKit/include/BehaviorKit.h b/libs/BehaviorKit/include/BehaviorKit.h index d6b9aea6b6..669c643725 100644 --- a/libs/BehaviorKit/include/BehaviorKit.h +++ b/libs/BehaviorKit/include/BehaviorKit.h @@ -37,7 +37,7 @@ class BehaviorKit void bleConnection(bool with_video); void working(); - void chooseActivity(); + void displayAutonomousActivitiesPrompt(); void stop(); diff --git a/libs/BehaviorKit/source/BehaviorKit.cpp b/libs/BehaviorKit/source/BehaviorKit.cpp index 282c6eca52..a064da2025 100644 --- a/libs/BehaviorKit/source/BehaviorKit.cpp +++ b/libs/BehaviorKit/source/BehaviorKit.cpp @@ -85,7 +85,7 @@ void BehaviorKit::working() _videokit.displayImage("/fs/home/img/system/robot-face-smiling-slightly.jpg"); } -void BehaviorKit::chooseActivity() +void BehaviorKit::displayAutonomousActivitiesPrompt() { _videokit.displayImage("/fs/home/img/system/robot-misc-choose_activity-fr_FR.jpg"); } diff --git a/libs/BehaviorKit/tests/BehaviorKit_test.cpp b/libs/BehaviorKit/tests/BehaviorKit_test.cpp index bdb39948d2..d2b23d6294 100644 --- a/libs/BehaviorKit/tests/BehaviorKit_test.cpp +++ b/libs/BehaviorKit/tests/BehaviorKit_test.cpp @@ -117,10 +117,10 @@ TEST_F(BehaviorKitTest, working) behaviorkit.working(); } -TEST_F(BehaviorKitTest, chooseActivity) +TEST_F(BehaviorKitTest, displayAutonomousActivitiesPrompt) { EXPECT_CALL(mock_videokit, displayImage); - behaviorkit.chooseActivity(); + behaviorkit.displayAutonomousActivitiesPrompt(); } TEST_F(BehaviorKitTest, stop) diff --git a/libs/RobotKit/CMakeLists.txt b/libs/RobotKit/CMakeLists.txt index 8365b56c91..1009604e3d 100644 --- a/libs/RobotKit/CMakeLists.txt +++ b/libs/RobotKit/CMakeLists.txt @@ -29,6 +29,7 @@ target_link_libraries(RobotKit BehaviorKit CommandKit RFIDKit + ActivityKit ) if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") @@ -44,5 +45,6 @@ if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") tests/RobotController_test_stateDisconnected.cpp tests/RobotController_test_stateWorking.cpp tests/RobotController_test_stateEmergencyStopped.cpp + tests/RobotController_test_stateAutonomousActivities.cpp ) endif() diff --git a/libs/RobotKit/include/RobotController.h b/libs/RobotKit/include/RobotController.h index e58c137fdf..e905fd3f59 100644 --- a/libs/RobotKit/include/RobotController.h +++ b/libs/RobotKit/include/RobotController.h @@ -15,6 +15,7 @@ #include "BLEServiceMonitoring.h" #include "BLEServiceUpdate.h" +#include "ActivityKit.h" #include "BatteryKit.h" #include "BehaviorKit.h" #include "CommandKit.h" @@ -47,7 +48,7 @@ class RobotController : public interface::RobotController interface::FirmwareUpdate &firmware_update, interface::Motor &motor_left, interface::Motor &motor_right, interface::LED &ears, interface::LED &belt, LedKit &ledkit, interface::LCD &lcd, interface::VideoKit &videokit, BehaviorKit &behaviorkit, - CommandKit &cmdkit, RFIDKit &rfidkit) + CommandKit &cmdkit, RFIDKit &rfidkit, ActivityKit &activitykit) : _timeout(timeout), _battery(battery), _serialnumberkit(serialnumberkit), @@ -61,7 +62,8 @@ class RobotController : public interface::RobotController _videokit(videokit), _behaviorkit(behaviorkit), _cmdkit(cmdkit), - _rfidkit(rfidkit) + _rfidkit(rfidkit), + _activitykit(activitykit) { // nothing to do } @@ -191,6 +193,18 @@ class RobotController : public interface::RobotController void startDisconnectionBehavior() final { stopActuators(); } + void startAutonomousActivityMode() final + { + _lcd.turnOn(); + _behaviorkit.displayAutonomousActivitiesPrompt(); + } + + void stopAutonomousActivityMode() final + { + _behaviorkit.stop(); + _activitykit.stop(); + } + auto isReadyToUpdate() -> bool final { return (_battery.isCharging() && _battery.level() > _minimal_battery_level_to_update); @@ -267,10 +281,31 @@ class RobotController : public interface::RobotController } } + void raiseAutonomousActivityModeRequested() + { + raise(system::robot::sm::event::autonomous_activities_mode_requested {}); + } + void onMagicCardAvailable(const MagicCard &card) { if (card == MagicCard::emergency_stop) { raiseEmergencyStop(); + return; + } + + if (card == MagicCard::dice_roll) { + raiseAutonomousActivityModeRequested(); + if (_activitykit.isPlaying()) { + _activitykit.stop(); + } + return; + } + + auto is_not_playing = !_activitykit.isPlaying(); + auto is_autonomous_mode = state_machine.is(system::robot::sm::state::autonomous_activities); + + if (is_not_playing && is_autonomous_mode) { + _activitykit.start(card); } } @@ -385,6 +420,7 @@ class RobotController : public interface::RobotController interface::LCD &_lcd; interface::VideoKit &_videokit; RFIDKit &_rfidkit; + ActivityKit &_activitykit; BehaviorKit &_behaviorkit; CommandKit &_cmdkit; diff --git a/libs/RobotKit/include/StateMachine.h b/libs/RobotKit/include/StateMachine.h index 022e10067e..2d239bed33 100644 --- a/libs/RobotKit/include/StateMachine.h +++ b/libs/RobotKit/include/StateMachine.h @@ -31,18 +31,21 @@ namespace sm::event { }; struct ble_disconnection { }; + struct autonomous_activities_mode_requested { + }; } // namespace sm::event namespace sm::state { - inline auto setup = boost::sml::state; - inline auto idle = boost::sml::state; - inline auto working = boost::sml::state; - inline auto sleeping = boost::sml::state; - inline auto charging = boost::sml::state; - inline auto updating = boost::sml::state; - inline auto emergency_stopped = boost::sml::state; + inline auto setup = boost::sml::state; + inline auto idle = boost::sml::state; + inline auto working = boost::sml::state; + inline auto sleeping = boost::sml::state; + inline auto charging = boost::sml::state; + inline auto updating = boost::sml::state; + inline auto emergency_stopped = boost::sml::state; + inline auto autonomous_activities = boost::sml::state; inline auto connected = boost::sml::state; inline auto disconnected = boost::sml::state; @@ -147,6 +150,14 @@ namespace sm::action { auto operator()(irc &rc) const { rc.resetEmergencyStopCounter(); } }; + struct start_autonomous_activities_mode { + auto operator()(irc &rc) const { rc.startAutonomousActivityMode(); } + }; + + struct stop_autonomous_activities_mode { + auto operator()(irc &rc) const { rc.stopAutonomousActivityMode(); } + }; + } // namespace sm::action struct StateMachine { @@ -163,27 +174,30 @@ struct StateMachine { , sm::state::idle + boost::sml::on_entry<_> / (sm::action::start_sleep_timeout {}, sm::action::start_waiting_behavior {}) , sm::state::idle + boost::sml::on_exit<_> / (sm::action::stop_sleep_timeout {}, sm::action::stop_waiting_behavior {}) - , sm::state::idle + event = sm::state::working - , sm::state::idle + event [sm::guard::is_connected {}] = sm::state::working - , sm::state::idle + event = sm::state::sleeping - , sm::state::idle + event [sm::guard::is_charging {}] = sm::state::charging - , sm::state::idle + event = sm::state::emergency_stopped + , sm::state::idle + event = sm::state::working + , sm::state::idle + event [sm::guard::is_connected {}] = sm::state::working + , sm::state::idle + event = sm::state::sleeping + , sm::state::idle + event [sm::guard::is_charging {}] = sm::state::charging + , sm::state::idle + event = sm::state::emergency_stopped + , sm::state::idle + event = sm::state::autonomous_activities - , sm::state::working + boost::sml::on_entry<_> / (sm::action::start_idle_timeout {}, sm::action::start_working_behavior {}) - , sm::state::working + boost::sml::on_exit<_> / sm::action::stop_idle_timeout {} + , sm::state::working + boost::sml::on_entry<_> / (sm::action::start_idle_timeout {}, sm::action::start_working_behavior {}) + , sm::state::working + boost::sml::on_exit<_> / sm::action::stop_idle_timeout {} - , sm::state::working + event = sm::state::idle - , sm::state::working + event = sm::state::idle - , sm::state::working + event [sm::guard::is_charging {}] = sm::state::charging - , sm::state::working + event = sm::state::emergency_stopped + , sm::state::working + event = sm::state::idle + , sm::state::working + event = sm::state::idle + , sm::state::working + event [sm::guard::is_charging {}] = sm::state::charging + , sm::state::working + event = sm::state::emergency_stopped + , sm::state::working + event = sm::state::autonomous_activities , sm::state::sleeping + boost::sml::on_entry<_> / sm::action::start_sleeping_behavior {} , sm::state::sleeping + boost::sml::on_exit<_> / sm::action::stop_sleeping_behavior {} - , sm::state::sleeping + event [sm::guard::is_connected {}] = sm::state::working - , sm::state::sleeping + event = sm::state::working - , sm::state::sleeping + event [sm::guard::is_charging {}] = sm::state::charging - , sm::state::sleeping + event = sm::state::emergency_stopped + , sm::state::sleeping + event [sm::guard::is_connected {}] = sm::state::working + , sm::state::sleeping + event = sm::state::working + , sm::state::sleeping + event [sm::guard::is_charging {}] = sm::state::charging + , sm::state::sleeping + event = sm::state::emergency_stopped + , sm::state::sleeping + event = sm::state::autonomous_activities , sm::state::charging + boost::sml::on_entry<_> / sm::action::start_charging_behavior {} , sm::state::charging + boost::sml::on_exit<_> / sm::action::stop_charging_behavior {} @@ -195,6 +209,7 @@ struct StateMachine { , sm::state::charging + event = sm::state::charging , sm::state::charging + event = sm::state::charging , sm::state::charging + event = sm::state::emergency_stopped + , sm::state::charging + event = sm::state::charging , sm::state::updating + boost::sml::on_entry<_> / sm::action::apply_update {} @@ -206,6 +221,16 @@ struct StateMachine { , sm::state::emergency_stopped + event [sm::guard::is_charging {}] = sm::state::charging , sm::state::emergency_stopped + event [sm::guard::is_charging {} && sm::guard::is_connected {}] = sm::state::charging , sm::state::emergency_stopped + event [sm::guard::is_charging {}] = sm::state::charging + , sm::state::emergency_stopped + event = sm::state::autonomous_activities + + , sm::state::autonomous_activities + boost::sml::on_entry<_> / sm::action::start_autonomous_activities_mode {} + , sm::state::autonomous_activities + boost::sml::on_exit<_> / sm::action::stop_autonomous_activities_mode {} + + , sm::state::autonomous_activities + event [sm::guard::is_connected {}] = sm::state::working + , sm::state::autonomous_activities + event = sm::state::working + , sm::state::autonomous_activities + event [sm::guard::is_charging {}] = sm::state::charging + , sm::state::autonomous_activities + event = sm::state::emergency_stopped + , sm::state::autonomous_activities + event = sm::state::autonomous_activities , diff --git a/libs/RobotKit/include/interface/RobotController.h b/libs/RobotKit/include/interface/RobotController.h index 34d0918502..c663537a29 100644 --- a/libs/RobotKit/include/interface/RobotController.h +++ b/libs/RobotKit/include/interface/RobotController.h @@ -36,6 +36,9 @@ class RobotController virtual void startConnectionBehavior() = 0; virtual void startDisconnectionBehavior() = 0; + virtual void startAutonomousActivityMode() = 0; + virtual void stopAutonomousActivityMode() = 0; + virtual void startWorkingBehavior() = 0; virtual auto isReadyToUpdate() -> bool = 0; diff --git a/libs/RobotKit/tests/RobotController_test.h b/libs/RobotKit/tests/RobotController_test.h index 1b54411ddd..6377a723d7 100644 --- a/libs/RobotKit/tests/RobotController_test.h +++ b/libs/RobotKit/tests/RobotController_test.h @@ -9,11 +9,13 @@ #include "ble_mocks.h" +#include "ActivityKit.h" #include "BehaviorKit.h" #include "CommandKit.h" #include "CoreBufferedSerial.h" #include "CorePwm.h" #include "CoreRFIDReaderCR95HF.h" +#include "DisplayTags.h" #include "RFIDKit.h" #include "SerialNumberKit.h" #include "gmock/gmock.h" @@ -93,12 +95,15 @@ class RobotControllerTest : public testing::Test CoreRFIDReaderCR95HF reader {serial}; RFIDKit rfidkit {reader}; + ActivityKit activitykit; + activity::DisplayTags display_tag {rfidkit, mock_videokit}; + stub::EventLoopKit event_loop {}; CommandKit cmdkit {event_loop}; RobotController> rc { - timeout, battery, serialnumberkit, firmware_update, mock_motor_left, mock_motor_right, mock_ears, - mock_belt, ledkit, mock_lcd, mock_videokit, bhvkit, cmdkit, rfidkit}; + timeout, battery, serialnumberkit, firmware_update, mock_motor_left, mock_motor_right, mock_ears, mock_belt, + ledkit, mock_lcd, mock_videokit, bhvkit, cmdkit, rfidkit, activitykit}; ble::GapMock &mbed_mock_gap = ble::gap_mock(); ble::GattServerMock &mbed_mock_gatt = ble::gatt_server_mock(); diff --git a/libs/RobotKit/tests/RobotController_test_stateAutonomousActivities.cpp b/libs/RobotKit/tests/RobotController_test_stateAutonomousActivities.cpp new file mode 100644 index 0000000000..1f3a15eaaf --- /dev/null +++ b/libs/RobotKit/tests/RobotController_test_stateAutonomousActivities.cpp @@ -0,0 +1,146 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "./RobotController_test.h" + +TEST_F(RobotControllerTest, stateGameConnectedEventCommandReceived) +{ + rc.state_machine.set_current_states(lksm::state::autonomous_activities, lksm::state::connected); + + Sequence on_game_exit_sequence; + EXPECT_CALL(mock_videokit, stopVideo).InSequence(on_game_exit_sequence); + EXPECT_CALL(mock_motor_left, stop).InSequence(on_game_exit_sequence); + EXPECT_CALL(mock_motor_right, stop).InSequence(on_game_exit_sequence); + + Sequence on_working_entry_sequence; + EXPECT_CALL(timeout, onTimeout).InSequence(on_working_entry_sequence); + EXPECT_CALL(timeout, start).InSequence(on_working_entry_sequence); + EXPECT_CALL(mock_videokit, displayImage).InSequence(on_working_entry_sequence); + EXPECT_CALL(mock_lcd, turnOn).InSequence(on_working_entry_sequence); + + rc.state_machine.process_event(lksm::event::command_received {}); + + EXPECT_TRUE(rc.state_machine.is(lksm::state::working, lksm::state::connected)); +} + +TEST_F(RobotControllerTest, stateGameDisconnectedEventCommandReceived) +{ + rc.state_machine.set_current_states(lksm::state::autonomous_activities, lksm::state::disconnected); + + rc.state_machine.process_event(lksm::event::command_received {}); + + EXPECT_TRUE(rc.state_machine.is(lksm::state::autonomous_activities, lksm::state::disconnected)); +} + +TEST_F(RobotControllerTest, stateGameEventBleConnection) +{ + rc.state_machine.set_current_states(lksm::state::autonomous_activities, lksm::state::disconnected); + + EXPECT_CALL(battery, isCharging).WillRepeatedly(Return(false)); + + EXPECT_CALL(mock_videokit, stopVideo).Times(AtLeast(1)); + EXPECT_CALL(mock_motor_left, stop).Times(AtLeast(1)); + EXPECT_CALL(mock_motor_right, stop).Times(AtLeast(1)); + + EXPECT_CALL(mock_belt, setColor).Times(AtLeast(1)); + EXPECT_CALL(mock_belt, show).Times(AtLeast(1)); + EXPECT_CALL(mock_videokit, playVideoOnce).Times(1); + Sequence on_working_entry_sequence; + EXPECT_CALL(timeout, onTimeout).InSequence(on_working_entry_sequence); + EXPECT_CALL(timeout, start).InSequence(on_working_entry_sequence); + EXPECT_CALL(mock_videokit, displayImage).InSequence(on_working_entry_sequence); + EXPECT_CALL(mock_lcd, turnOn).Times(2).InSequence(on_working_entry_sequence); + + rc.state_machine.process_event(lksm::event::ble_connection {}); + + EXPECT_TRUE(rc.state_machine.is(lksm::state::working, lksm::state::connected)); +} + +TEST_F(RobotControllerTest, stateGameEventChargeDidStartGuardIsChargingTrue) +{ + rc.state_machine.set_current_states(lksm::state::autonomous_activities); + + EXPECT_CALL(battery, isCharging).WillRepeatedly(Return(true)); + + Sequence on_game_exit_sequence; + EXPECT_CALL(mock_videokit, stopVideo).InSequence(on_game_exit_sequence); + EXPECT_CALL(mock_motor_left, stop).InSequence(on_game_exit_sequence); + EXPECT_CALL(mock_motor_right, stop).InSequence(on_game_exit_sequence); + + Sequence start_charging_behavior_sequence; + EXPECT_CALL(battery, level).InSequence(start_charging_behavior_sequence); + EXPECT_CALL(mock_videokit, displayImage).InSequence(start_charging_behavior_sequence); + EXPECT_CALL(mock_lcd, turnOn).InSequence(start_charging_behavior_sequence); + EXPECT_CALL(timeout, onTimeout).InSequence(start_charging_behavior_sequence); + EXPECT_CALL(timeout, start).InSequence(start_charging_behavior_sequence); + + // TODO: Specify which BLE service and what is expected if necessary + EXPECT_CALL(mbed_mock_gatt, write(_, _, _, _)).Times(AtLeast(1)); + + on_charge_did_start(); + + EXPECT_TRUE(rc.state_machine.is(lksm::state::charging)); +} + +TEST_F(RobotControllerTest, stateGameEventChargeDidStartGuardIsChargingFalse) +{ + rc.state_machine.set_current_states(lksm::state::autonomous_activities); + + EXPECT_CALL(battery, isCharging).WillRepeatedly(Return(false)); + + // TODO: Specify which BLE service and what is expected if necessary + EXPECT_CALL(mbed_mock_gatt, write(_, _, _, _)); + + on_charge_did_start(); + + EXPECT_TRUE(rc.state_machine.is(lksm::state::autonomous_activities)); +} + +TEST_F(RobotControllerTest, stateGameEventAutonomousActivityRequested) +{ + rc.state_machine.set_current_states(lksm::state::autonomous_activities); + + EXPECT_CALL(mock_videokit, stopVideo).Times(AtLeast(1)); + EXPECT_CALL(mock_motor_left, stop).Times(AtLeast(1)); + EXPECT_CALL(mock_motor_right, stop).Times(AtLeast(1)); + EXPECT_CALL(mock_videokit, displayImage).Times(1); + + rc.onMagicCardAvailable(MagicCard::dice_roll); + + EXPECT_TRUE(rc.state_machine.is(lksm::state::autonomous_activities)); +} + +TEST_F(RobotControllerTest, stateGameActivityDisplayTagsChosen) +{ + rc.state_machine.set_current_states(lksm::state::autonomous_activities); + + const std::unordered_map activities {{MagicCard::number_10, &display_tag}}; + + activitykit.registerActivities(activities); + + EXPECT_CALL(mock_videokit, displayImage).Times(1); + + rc.onMagicCardAvailable(MagicCard::number_10); + + EXPECT_TRUE(rc.state_machine.is(lksm::state::autonomous_activities)); +} + +TEST_F(RobotControllerTest, stateGameMagicCardAvailableActivityAlreadyStarted) +{ + rc.state_machine.set_current_states(lksm::state::autonomous_activities); + + const std::unordered_map activities {{MagicCard::number_10, &display_tag}}; + + activitykit.registerActivities(activities); + + EXPECT_CALL(mock_videokit, displayImage).Times(1); + + activitykit.start(MagicCard::number_10); + + EXPECT_CALL(mock_videokit, displayImage).Times(0); + + rc.onMagicCardAvailable(MagicCard::number_10); + + EXPECT_TRUE(rc.state_machine.is(lksm::state::autonomous_activities)); +} diff --git a/libs/RobotKit/tests/RobotController_test_stateCharging.cpp b/libs/RobotKit/tests/RobotController_test_stateCharging.cpp index e990e0f58d..87ba6994cf 100644 --- a/libs/RobotKit/tests/RobotController_test_stateCharging.cpp +++ b/libs/RobotKit/tests/RobotController_test_stateCharging.cpp @@ -183,3 +183,18 @@ TEST_F(RobotControllerTest, stateChargingEventEmergencyStop) EXPECT_TRUE(rc.state_machine.is(lksm::state::emergency_stopped)); } + +TEST_F(RobotControllerTest, stateChargingEventAutonomousActivityRequested) +{ + rc.state_machine.set_current_states(lksm::state::charging); + + EXPECT_CALL(battery, level); + EXPECT_CALL(mock_videokit, displayImage).Times(1); + EXPECT_CALL(mock_lcd, turnOn).Times(AnyNumber()); + EXPECT_CALL(timeout, onTimeout).WillOnce(GetCallback(&on_charging_start_timeout)); + EXPECT_CALL(timeout, start).Times(AnyNumber()); + + rc.onMagicCardAvailable(MagicCard::dice_roll); + + EXPECT_TRUE(rc.state_machine.is(lksm::state::charging)); +} diff --git a/libs/RobotKit/tests/RobotController_test_stateEmergencyStopped.cpp b/libs/RobotKit/tests/RobotController_test_stateEmergencyStopped.cpp index 9b9f045482..06ddd6e1ff 100644 --- a/libs/RobotKit/tests/RobotController_test_stateEmergencyStopped.cpp +++ b/libs/RobotKit/tests/RobotController_test_stateEmergencyStopped.cpp @@ -167,3 +167,14 @@ TEST_F(RobotControllerTest, stateEmergencyStoppedEventBleConnectionGuardIsChargi EXPECT_TRUE(rc.state_machine.is(lksm::state::charging)); } + +TEST_F(RobotControllerTest, stateEmergencyStoppedEventAutonomousActivityRequested) +{ + rc.state_machine.set_current_states(lksm::state::emergency_stopped); + + EXPECT_CALL(mock_videokit, displayImage).Times(1); + + rc.state_machine.process_event(lksm::event::autonomous_activities_mode_requested {}); + + EXPECT_TRUE(rc.state_machine.is(lksm::state::autonomous_activities)); +} diff --git a/libs/RobotKit/tests/RobotController_test_stateIdle.cpp b/libs/RobotKit/tests/RobotController_test_stateIdle.cpp index 52369aa3d7..2fd1081fae 100644 --- a/libs/RobotKit/tests/RobotController_test_stateIdle.cpp +++ b/libs/RobotKit/tests/RobotController_test_stateIdle.cpp @@ -124,7 +124,7 @@ TEST_F(RobotControllerTest, stateIdleEventChargeDidStartGuardIsChargingFalse) TEST_F(RobotControllerTest, stateIdleEventEmergencyStop) { - rc.state_machine.set_current_states(lksm::state::sleeping); + rc.state_machine.set_current_states(lksm::state::idle); Sequence on_exit_idle_sequence; EXPECT_CALL(timeout, stop).InSequence(on_exit_idle_sequence); @@ -140,3 +140,19 @@ TEST_F(RobotControllerTest, stateIdleEventEmergencyStop) EXPECT_TRUE(rc.state_machine.is(lksm::state::emergency_stopped)); } + +TEST_F(RobotControllerTest, stateIdleEventAutonomousActivityRequested) +{ + rc.state_machine.set_current_states(lksm::state::idle); + + Sequence on_exit_idle_sequence; + EXPECT_CALL(timeout, stop).InSequence(on_exit_idle_sequence); + EXPECT_CALL(mock_videokit, stopVideo).InSequence(on_exit_idle_sequence); + expectedCallsStopMotors(); + + EXPECT_CALL(mock_videokit, displayImage).Times(1); + + rc.onMagicCardAvailable(MagicCard::dice_roll); + + EXPECT_TRUE(rc.state_machine.is(lksm::state::autonomous_activities)); +} diff --git a/libs/RobotKit/tests/RobotController_test_stateSleeping.cpp b/libs/RobotKit/tests/RobotController_test_stateSleeping.cpp index b16db56662..42275bf91e 100644 --- a/libs/RobotKit/tests/RobotController_test_stateSleeping.cpp +++ b/libs/RobotKit/tests/RobotController_test_stateSleeping.cpp @@ -110,3 +110,19 @@ TEST_F(RobotControllerTest, stateSleepingEventEmergencyStop) EXPECT_TRUE(rc.state_machine.is(lksm::state::emergency_stopped)); } + +TEST_F(RobotControllerTest, stateSleepingEventAutonomousActivityRequested) +{ + rc.state_machine.set_current_states(lksm::state::sleeping); + + Sequence on_exit_sleeping_sequence; + EXPECT_CALL(timeout, stop).InSequence(on_exit_sleeping_sequence); + EXPECT_CALL(mock_videokit, stopVideo).InSequence(on_exit_sleeping_sequence); + expectedCallsStopMotors(); + + EXPECT_CALL(mock_videokit, displayImage).Times(1); + + rc.onMagicCardAvailable(MagicCard::dice_roll); + + EXPECT_TRUE(rc.state_machine.is(lksm::state::autonomous_activities)); +} diff --git a/libs/RobotKit/tests/RobotController_test_stateWorking.cpp b/libs/RobotKit/tests/RobotController_test_stateWorking.cpp index 6487598caa..7ca3bc7fe1 100644 --- a/libs/RobotKit/tests/RobotController_test_stateWorking.cpp +++ b/libs/RobotKit/tests/RobotController_test_stateWorking.cpp @@ -78,3 +78,17 @@ TEST_F(RobotControllerTest, stateWorkingEventEmergencyStop) EXPECT_TRUE(rc.state_machine.is(lksm::state::emergency_stopped)); } + +TEST_F(RobotControllerTest, stateWorkingEventAutonomousActivityRequested) +{ + rc.state_machine.set_current_states(lksm::state::working); + + Sequence on_exit_working_sequence; + EXPECT_CALL(timeout, stop).InSequence(on_exit_working_sequence); + + EXPECT_CALL(mock_videokit, displayImage).Times(1); + + rc.onMagicCardAvailable(MagicCard::dice_roll); + + EXPECT_TRUE(rc.state_machine.is(lksm::state::autonomous_activities)); +} diff --git a/libs/RobotKit/tests/StateMachine_test.cpp b/libs/RobotKit/tests/StateMachine_test.cpp index d76183a55a..30c5291587 100644 --- a/libs/RobotKit/tests/StateMachine_test.cpp +++ b/libs/RobotKit/tests/StateMachine_test.cpp @@ -108,6 +108,20 @@ TEST_F(StateMachineTest, stateIdleEventCommandReceived) EXPECT_TRUE(sm.is(lksm::state::working)); } +TEST_F(StateMachineTest, stateIdleEventAutonomousActivityRequested) +{ + sm.set_current_states(lksm::state::idle); + + EXPECT_CALL(mock_rc, stopSleepTimeout).Times(1); + EXPECT_CALL(mock_rc, stopWaitingBehavior).Times(1); + + EXPECT_CALL(mock_rc, startAutonomousActivityMode).Times(1); + + sm.process_event(lksm::event::autonomous_activities_mode_requested {}); + + EXPECT_TRUE(sm.is(lksm::state::autonomous_activities)); +} + TEST_F(StateMachineTest, stateWorkingEventTimeout) { sm.set_current_states(lksm::state::working); @@ -146,6 +160,18 @@ TEST_F(StateMachineTest, stateWorkingEventEmergencyStop) EXPECT_TRUE(sm.is(lksm::state::emergency_stopped)); } +TEST_F(StateMachineTest, stateWorkingEventAutonomousActivityRequested) +{ + sm.set_current_states(lksm::state::working); + + EXPECT_CALL(mock_rc, stopIdleTimeout).Times(1); + EXPECT_CALL(mock_rc, startAutonomousActivityMode).Times(1); + + sm.process_event(lksm::event::autonomous_activities_mode_requested {}); + + EXPECT_TRUE(sm.is(lksm::state::autonomous_activities)); +} + TEST_F(StateMachineTest, stateSleepEventCommandReceived) { sm.set_current_states(lksm::state::sleeping); @@ -185,6 +211,19 @@ TEST_F(StateMachineTest, stateSleepEventEmergencyStop) EXPECT_TRUE(sm.is(lksm::state::emergency_stopped)); } +TEST_F(StateMachineTest, stateSleepEventAutonomousActivityRequested) +{ + sm.set_current_states(lksm::state::sleeping); + + EXPECT_CALL(mock_rc, stopSleepingBehavior).Times(1); + + EXPECT_CALL(mock_rc, startAutonomousActivityMode).Times(1); + + sm.process_event(lksm::event::autonomous_activities_mode_requested {}); + + EXPECT_TRUE(sm.is(lksm::state::autonomous_activities)); +} + TEST_F(StateMachineTest, stateIdleEventChargeDidStart) { sm.set_current_states(lksm::state::idle); @@ -279,6 +318,18 @@ TEST_F(StateMachineTest, stateChargingEventEmergencyStop) EXPECT_TRUE(sm.is(lksm::state::emergency_stopped)); } +TEST_F(StateMachineTest, stateChargingEventAutonomousActivityRequested) +{ + sm.set_current_states(lksm::state::charging); + + EXPECT_CALL(mock_rc, stopChargingBehavior).Times(1); + EXPECT_CALL(mock_rc, startAutonomousActivityMode).Times(0); + + sm.process_event(lksm::event::autonomous_activities_mode_requested {}); + + EXPECT_TRUE(sm.is(lksm::state::charging)); +} + TEST_F(StateMachineTest, stateSleepingEventBleConnection) { sm.set_current_states(lksm::state::sleeping, lksm::state::disconnected); @@ -472,3 +523,81 @@ TEST_F(StateMachineTest, stateEmergencyStoppedEventBleConnectionGuardIsCharging) EXPECT_TRUE(sm.is(lksm::state::charging)); } + +TEST_F(StateMachineTest, stateGameEventBleConnection) +{ + sm.set_current_states(lksm::state::autonomous_activities); + + EXPECT_CALL(mock_rc, stopAutonomousActivityMode).Times(1); + EXPECT_CALL(mock_rc, startConnectionBehavior).Times(1); + EXPECT_CALL(mock_rc, startWorkingBehavior).Times(1); + + sm.process_event(lksm::event::ble_connection {}); + + EXPECT_TRUE(sm.is(lksm::state::working)); +} + +TEST_F(StateMachineTest, stateGameEventCommandReceivedConnected) +{ + sm.set_current_states(lksm::state::autonomous_activities); + + EXPECT_CALL(mock_rc, isBleConnected).WillRepeatedly(Return(true)); + + EXPECT_CALL(mock_rc, stopAutonomousActivityMode).Times(1); + EXPECT_CALL(mock_rc, startWorkingBehavior); + EXPECT_CALL(mock_rc, startIdleTimeout); + + sm.process_event(lksm::event::command_received {}); + + EXPECT_TRUE(sm.is(lksm::state::working)); +} + +TEST_F(StateMachineTest, stateGameEventCommandReceivedDisconnected) +{ + sm.set_current_states(lksm::state::autonomous_activities); + + EXPECT_CALL(mock_rc, isBleConnected).WillRepeatedly(Return(false)); + + sm.process_event(lksm::event::command_received {}); + + EXPECT_TRUE(sm.is(lksm::state::autonomous_activities)); +} + +TEST_F(StateMachineTest, stateGameEventChargeDidStartGuardIsNotCharging) +{ + sm.set_current_states(lksm::state::autonomous_activities); + + EXPECT_CALL(mock_rc, isCharging).WillRepeatedly(Return(false)); + + EXPECT_CALL(mock_rc, startChargingBehavior).Times(0); + + sm.process_event(lksm::event::charge_did_start {}); + + EXPECT_TRUE(sm.is(lksm::state::autonomous_activities)); +} + +TEST_F(StateMachineTest, stateGameEventChargeDidStartGuardIsCharging) +{ + sm.set_current_states(lksm::state::autonomous_activities); + + EXPECT_CALL(mock_rc, isCharging).WillRepeatedly(Return(true)); + + EXPECT_CALL(mock_rc, stopAutonomousActivityMode).Times(1); + EXPECT_CALL(mock_rc, startChargingBehavior).Times(1); + + sm.process_event(lksm::event::charge_did_start {}); + + EXPECT_TRUE(sm.is(lksm::state::charging)); +} + +TEST_F(StateMachineTest, stateGameEventAutonomousActivityRequested) +{ + sm.set_current_states(lksm::state::autonomous_activities); + + EXPECT_CALL(mock_rc, stopAutonomousActivityMode).Times(1); + EXPECT_CALL(mock_rc, startAutonomousActivityMode).Times(1); + + sm.process_event(lksm::event::autonomous_activities_mode_requested {}); + + EXPECT_TRUE(sm.is(lksm::state::autonomous_activities)); +} diff --git a/libs/RobotKit/tests/mocks/RobotController.h b/libs/RobotKit/tests/mocks/RobotController.h index c9d5f1ff8f..d54a4077c5 100644 --- a/libs/RobotKit/tests/mocks/RobotController.h +++ b/libs/RobotKit/tests/mocks/RobotController.h @@ -35,6 +35,9 @@ struct RobotController : public interface::RobotController { MOCK_METHOD(void, startConnectionBehavior, (), (override)); MOCK_METHOD(void, startDisconnectionBehavior, (), (override)); + MOCK_METHOD(void, startAutonomousActivityMode, (), (override)); + MOCK_METHOD(void, stopAutonomousActivityMode, (), (override)); + MOCK_METHOD(bool, isReadyToUpdate, (), (override)); MOCK_METHOD(void, applyUpdate, (), (override)); From dc9466ca3323e6fd2bb6823d042e8faf83a7fb90 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Fri, 2 Sep 2022 18:54:12 +0200 Subject: [PATCH 069/130] :recycle: (os): Define, register activity list --- app/os/CMakeLists.txt | 1 + app/os/main.cpp | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/app/os/CMakeLists.txt b/app/os/CMakeLists.txt index f96365fc7c..9aa13c4a27 100644 --- a/app/os/CMakeLists.txt +++ b/app/os/CMakeLists.txt @@ -35,6 +35,7 @@ target_link_libraries(LekaOS CoreBufferedSerial CoreRFIDReader RFIDKit + ActivityKit ) target_link_custom_leka_targets(LekaOS) diff --git a/app/os/main.cpp b/app/os/main.cpp index ed7c15689f..990c96a6de 100644 --- a/app/os/main.cpp +++ b/app/os/main.cpp @@ -9,6 +9,7 @@ #include "rtos/ThisThread.h" #include "rtos/Thread.h" +#include "ActivityKit.h" #include "CoreBattery.h" #include "CoreBufferedSerial.h" #include "CoreDMA2D.hpp" @@ -35,6 +36,7 @@ #include "CoreSTM32Hal.h" #include "CoreTimeout.h" #include "CoreVideo.hpp" +#include "DisplayTags.h" #include "EventLoopKit.h" #include "FATFileSystem.h" #include "FirmwareKit.h" @@ -324,6 +326,21 @@ namespace mcuboot { } // namespace mcuboot +namespace activities { + + namespace internal { + + auto display_tag = leka::activity::DisplayTags(rfidkit, display::videokit); + + } + + inline const std::unordered_map activities { + {MagicCard::number_10, &internal::display_tag}}; + +} // namespace activities + +auto activitykit = ActivityKit {}; + namespace robot { namespace internal { @@ -350,6 +367,7 @@ namespace robot { behaviorkit, commandkit, rfidkit, + activitykit, }; } // namespace robot @@ -477,6 +495,7 @@ auto main() -> int firmware::initializeFlash(); commandkit.registerCommand(command::list); + activitykit.registerActivities(activities::activities); robot::controller.initializeComponents(); robot::controller.registerOnUpdateLoadedCallback(firmware::setPendingUpdate); From 400749f4c9916cee0e4dab15cab9ee20b153ce1a Mon Sep 17 00:00:00 2001 From: Mourad Latoundji Date: Thu, 1 Sep 2022 18:01:28 +0200 Subject: [PATCH 070/130] :heavy_plus_sign: (tests): Add boost::ut for functional unit testing --- include/boost/ut.hpp | 2465 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2465 insertions(+) create mode 100644 include/boost/ut.hpp diff --git a/include/boost/ut.hpp b/include/boost/ut.hpp new file mode 100644 index 0000000000..b4347d8c30 --- /dev/null +++ b/include/boost/ut.hpp @@ -0,0 +1,2465 @@ +// +// Copyright (c) 2019-2021 Kris Jusiak (kris at jusiak dot net) +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#if defined(__cpp_modules) && !defined(BOOST_UT_DISABLE_MODULE) +export module boost.ut; +export import std; +#else +#pragma once +#endif + +#if __has_include() +#include // and, or, not, ... +#endif + +#if not defined(__cpp_rvalue_references) +#error "[Boost::ext].UT requires support for rvalue references"; +#elif not defined(__cpp_decltype) +#error "[Boost::ext].UT requires support for decltype"; +#elif not defined(__cpp_return_type_deduction) +#error "[Boost::ext].UT requires support for return type deduction"; +#elif not defined(__cpp_deduction_guides) +#error "[Boost::ext].UT requires support for return deduction guides"; +#elif not defined(__cpp_generic_lambdas) +#error "[Boost::ext].UT requires support for generic lambdas"; +#elif not defined(__cpp_constexpr) +#error "[Boost::ext].UT requires support for constexpr"; +#elif not defined(__cpp_alias_templates) +#error "[Boost::ext].UT requires support for alias templates"; +#elif not defined(__cpp_variadic_templates) +#error "[Boost::ext].UT requires support for variadic templates"; +#elif not defined(__cpp_fold_expressions) +#error "[Boost::ext].UT requires support for return fold expressions"; +#elif not defined(__cpp_static_assert) +#error "[Boost::ext].UT requires support for static assert"; +#else +#define BOOST_UT_VERSION 1'1'9 + +#if defined(__has_builtin) and defined(__GNUC__) and (__GNUC__ < 10) and \ + not defined(__clang__) +#undef __has_builtin +#endif + +#if not defined(__has_builtin) +#if defined(__GNUC__) and (__GNUC__ >= 9) +#define __has___builtin_FILE 1 +#define __has___builtin_LINE 1 +#endif +#define __has_builtin(...) __has_##__VA_ARGS__ +#endif + +#include +#include +#include +#include +#include +#include +#include +#if __has_include() and __has_include() +#include +#include +#endif +#if defined(__cpp_exceptions) +#include +#endif + +#if __has_include() +#include +#endif + +#if defined(__cpp_modules) && !defined(BOOST_UT_DISABLE_MODULE) +export +#endif + namespace boost::inline ext::ut::inline v1_1_9 { +namespace utility { +template +class function; +template +class function { + public: + constexpr function() = default; + template + constexpr /*explicit(false)*/ function(T data) + : invoke_{invoke_impl}, + destroy_{destroy_impl}, + data_{new T{static_cast(data)}} {} + constexpr function(function&& other) noexcept + : invoke_{static_cast(other.invoke_)}, + destroy_{static_cast(other.destroy_)}, + data_{static_cast(other.data_)} { + other.data_ = {}; + } + constexpr function(const function&) = delete; + ~function() { destroy_(data_); } + + constexpr function& operator=(const function&) = delete; + constexpr function& operator=(function&&) = delete; + [[nodiscard]] constexpr auto operator()(TArgs... args) -> R { + return invoke_(data_, args...); + } + [[nodiscard]] constexpr auto operator()(TArgs... args) const -> R { + return invoke_(data_, args...); + } + + private: + template + [[nodiscard]] static auto invoke_impl(void* data, TArgs... args) -> R { + return (*static_cast(data))(args...); + } + + template + static auto destroy_impl(void* data) -> void { + delete static_cast(data); + } + + R (*invoke_)(void*, TArgs...){}; + void (*destroy_)(void*){}; + void* data_{}; +}; + +[[nodiscard]] inline auto is_match(std::string_view input, + std::string_view pattern) -> bool { + if (std::empty(pattern)) { + return std::empty(input); + } + + if (std::empty(input)) { + return pattern[0] == '*' ? is_match(input, pattern.substr(1)) : false; + } + + if (pattern[0] != '?' and pattern[0] != '*' and pattern[0] != input[0]) { + return false; + } + + if (pattern[0] == '*') { + for (decltype(std::size(input)) i = 0u; i <= std::size(input); ++i) { + if (is_match(input.substr(i), pattern.substr(1))) { + return true; + } + } + return false; + } + + return is_match(input.substr(1), pattern.substr(1)); +} + +template +[[nodiscard]] constexpr auto match(const TPattern& pattern, const TStr& str) + -> std::vector { + std::vector groups{}; + auto pi = 0u; + auto si = 0u; + + const auto matcher = [&](char b, char e, char c = 0) { + const auto match = si; + while (str[si] and str[si] != b and str[si] != c) { + ++si; + } + groups.emplace_back(str.substr(match, si - match)); + while (pattern[pi] and pattern[pi] != e) { + ++pi; + } + pi++; + }; + + while (pi < std::size(pattern) && si < std::size(str)) { + if (pattern[pi] == '\'' and str[si] == '\'' and pattern[pi + 1] == '{') { + ++si; + matcher('\'', '}'); + } else if (pattern[pi] == '{') { + matcher(' ', '}', ','); + } else if (pattern[pi] != str[si]) { + return {}; + } + ++pi; + ++si; + } + + if (si < str.size() or pi < std::size(pattern)) { + return {}; + } + + return groups; +} + +template +[[nodiscard]] inline auto split(T input, TDelim delim) -> std::vector { + std::vector output{}; + std::size_t first{}; + while (first < std::size(input)) { + const auto second = input.find_first_of(delim, first); + if (first != second) { + output.emplace_back(input.substr(first, second - first)); + } + if (second == T::npos) { + break; + } + first = second + 1; + } + return output; +} +} // namespace utility + +namespace reflection { +#if defined(__cpp_lib_source_location) +using source_location = std::source_location; +#else +class source_location { + public: + [[nodiscard]] static constexpr auto current( +#if (__has_builtin(__builtin_FILE) and __has_builtin(__builtin_LINE)) + const char* file = __builtin_FILE(), int line = __builtin_LINE() +#else + const char* file = "unknown", int line = {} +#endif + ) noexcept { + source_location sl{}; + sl.file_ = file; + sl.line_ = line; + return sl; + } + [[nodiscard]] constexpr auto file_name() const noexcept { return file_; } + [[nodiscard]] constexpr auto line() const noexcept { return line_; } + + private: + const char* file_{"unknown"}; + int line_{}; +}; +#endif + +template +[[nodiscard]] constexpr auto type_name() -> std::string_view { +#if defined(_MSC_VER) and not defined(__clang__) + return {&__FUNCSIG__[120], sizeof(__FUNCSIG__) - 128}; +#elif defined(__clang_analyzer__) + return {&__PRETTY_FUNCTION__[57], sizeof(__PRETTY_FUNCTION__) - 59}; +#elif defined(__clang__) and (__clang_major__ >= 13) and defined(__APPLE__) + return {&__PRETTY_FUNCTION__[57], sizeof(__PRETTY_FUNCTION__) - 59}; +#elif defined(__clang__) and (__clang_major__ >= 12) and not defined(__APPLE__) + return {&__PRETTY_FUNCTION__[57], sizeof(__PRETTY_FUNCTION__) - 59}; +#elif defined(__clang__) + return {&__PRETTY_FUNCTION__[70], sizeof(__PRETTY_FUNCTION__) - 72}; +#elif defined(__GNUC__) + return {&__PRETTY_FUNCTION__[85], sizeof(__PRETTY_FUNCTION__) - 136}; +#endif +} +} // namespace reflection + +namespace math { +template +[[nodiscard]] constexpr auto abs(const T t) -> T { + return t < T{} ? -t : t; +} + +template +[[nodiscard]] constexpr auto abs_diff(const T t, const U u) + -> decltype(t < u ? u - t : t - u) { + return t < u ? u - t : t - u; +} + +template +[[nodiscard]] constexpr auto min_value(const T& lhs, const T& rhs) -> const T& { + return (rhs < lhs) ? rhs : lhs; +} + +template +[[nodiscard]] constexpr auto pow(const T base, const TExp exp) -> T { + return exp ? T(base * pow(base, exp - TExp(1))) : T(1); +} + +template +[[nodiscard]] constexpr auto num() -> T { + static_assert( + ((Cs == '.' or Cs == '\'' or (Cs >= '0' and Cs <= '9')) and ...)); + T result{}; + for (const char c : std::array{Cs...}) { + if (c == '.') { + break; + } + if (c >= '0' and c <= '9') { + result = result * T(10) + T(c - '0'); + } + } + return result; +} + +template +[[nodiscard]] constexpr auto den() -> T { + constexpr const std::array cs{Cs...}; + T result{}; + auto i = 0u; + while (cs[i++] != '.') { + } + + for (auto j = i; j < sizeof...(Cs); ++j) { + result += pow(T(10), sizeof...(Cs) - j) * T(cs[j] - '0'); + } + return result; +} + +template +[[nodiscard]] constexpr auto den_size() -> T { + constexpr const std::array cs{Cs...}; + T i{}; + while (cs[i++] != '.') { + } + + return T(sizeof...(Cs)) - i + T(1); +} + +template +[[nodiscard]] constexpr auto den_size(TValue value) -> T { + constexpr auto precision = TValue(1e-7); + T result{}; + TValue tmp{}; + do { + value *= 10; + tmp = value - T(value); + ++result; + } while (tmp > precision); + + return result; +} + +} // namespace math + +namespace type_traits { +template +struct list {}; + +template +struct identity { + using type = T; +}; + +template +struct function_traits : function_traits {}; + +template +struct function_traits { + using result_type = R; + using args = list; +}; + +template +struct function_traits { + using result_type = R; + using args = list; +}; + +template +struct function_traits { + using result_type = R; + using args = list; +}; + +template +struct function_traits { + using result_type = R; + using args = list; +}; + +template +T&& declval(); +template +constexpr auto is_valid(TExpr expr) + -> decltype(expr(declval()), bool()) { + return true; +} +template +constexpr auto is_valid(...) -> bool { + return false; +} + +template +static constexpr auto is_container_v = + is_valid([](auto t) -> decltype(t.begin(), t.end(), void()) {}); + +template +static constexpr auto has_user_print = is_valid( + [](auto t) -> decltype(void(declval() << t)) {}); + +template +struct has_static_member_object_value : std::false_type {}; + +template +struct has_static_member_object_value().value)>> + : std::bool_constant && + !std::is_function_v> {}; + +template +inline constexpr bool has_static_member_object_value_v = + has_static_member_object_value::value; + +template +struct has_static_member_object_epsilon : std::false_type {}; + +template +struct has_static_member_object_epsilon< + T, std::void_t().epsilon)>> + : std::bool_constant && + !std::is_function_v> {}; + +template +inline constexpr bool has_static_member_object_epsilon_v = + has_static_member_object_epsilon::value; + +template +inline constexpr auto is_floating_point_v = false; +template <> +inline constexpr auto is_floating_point_v = true; +template <> +inline constexpr auto is_floating_point_v = true; +template <> +inline constexpr auto is_floating_point_v = true; + +#if defined(__clang__) or defined(_MSC_VER) +template +static constexpr auto is_convertible_v = __is_convertible_to(From, To); +#else +template +constexpr auto is_convertible(int) -> decltype(bool(To(declval()))) { + return true; +} +template +constexpr auto is_convertible(...) { + return false; +} +template +constexpr auto is_convertible_v = is_convertible(0); +#endif + +template +struct requires_ {}; +template <> +struct requires_ { + using type = int; +}; + +template +using requires_t = typename requires_::type; +} // namespace type_traits + +struct none {}; + +namespace events { +struct test_begin { + std::string_view type{}; + std::string_view name{}; + reflection::source_location location{}; +}; +template +struct test { + std::string_view type{}; + std::string_view name{}; + std::vector tag{}; + reflection::source_location location{}; + TArg arg{}; + Test run{}; + + constexpr auto operator()() { run_impl(static_cast(run), arg); } + constexpr auto operator()() const { run_impl(static_cast(run), arg); } + + private: + static constexpr auto run_impl(Test test, const none&) { test(); } + + template + static constexpr auto run_impl(T test, const TArg& arg) + -> decltype(test(arg), void()) { + test(arg); + } + + template + static constexpr auto run_impl(T test, const TArg&) + -> decltype(test.template operator()(), void()) { + test.template operator()(); + } +}; +template +test(std::string_view, std::string_view, std::string_view, + reflection::source_location, TArg, Test) -> test; +template +struct suite { + TSuite run{}; + constexpr auto operator()() { run(); } + constexpr auto operator()() const { run(); } +}; +template +suite(TSuite) -> suite; +struct test_run { + std::string_view type{}; + std::string_view name{}; +}; +template +struct skip { + std::string_view type{}; + std::string_view name{}; + TArg arg{}; +}; +template +skip(std::string_view, std::string_view, TArg) -> skip; +struct test_skip { + std::string_view type{}; + std::string_view name{}; +}; +template +struct assertion { + TExpr expr{}; + reflection::source_location location{}; +}; +template +assertion(TExpr, reflection::source_location) -> assertion; +template +struct assertion_pass { + TExpr expr{}; + reflection::source_location location{}; +}; +template +assertion_pass(TExpr) -> assertion_pass; +template +struct assertion_fail { + TExpr expr{}; + reflection::source_location location{}; +}; +template +assertion_fail(TExpr) -> assertion_fail; +struct test_end { + std::string_view type{}; + std::string_view name{}; +}; +template +struct log { + TMsg msg{}; +}; +template +log(TMsg) -> log; +struct fatal_assertion {}; +struct exception { + const char* msg{}; + [[nodiscard]] auto what() const -> const char* { return msg; } +}; +struct summary {}; +} // namespace events + +namespace detail { +struct op {}; +struct fatal {}; +struct cfg { + static inline reflection::source_location location{}; + static inline bool wip{}; +}; + +template +[[nodiscard]] constexpr auto get_impl(const T& t, int) -> decltype(t.get()) { + return t.get(); +} +template +[[nodiscard]] constexpr auto get_impl(const T& t, ...) -> decltype(auto) { + return t; +} +template +[[nodiscard]] constexpr auto get(const T& t) { + return get_impl(t, 0); +} + +template +struct type_ : op { + template + [[nodiscard]] constexpr auto operator()(const TOther&) const + -> const type_ { + return {}; + } + [[nodiscard]] constexpr auto operator==(type_) -> bool { return true; } + template + [[nodiscard]] constexpr auto operator==(type_) -> bool { + return false; + } + template + [[nodiscard]] constexpr auto operator==(const TOther&) -> bool { + return std::is_same_v; + } + [[nodiscard]] constexpr auto operator!=(type_) -> bool { return false; } + template + [[nodiscard]] constexpr auto operator!=(type_) -> bool { + return true; + } + template + [[nodiscard]] constexpr auto operator!=(const TOther&) -> bool { + return not std::is_same_v; + } +}; + +template +struct value : op { + using value_type = T; + + constexpr /*explicit(false)*/ value(const T& _value) : value_{_value} {} + [[nodiscard]] constexpr explicit operator T() const { return value_; } + [[nodiscard]] constexpr decltype(auto) get() const { return value_; } + + T value_{}; +}; + +template +struct value>> + : op { + using value_type = T; + static inline auto epsilon = T{}; + + constexpr value(const T& _value, const T precision) : value_{_value} { + epsilon = precision; + } + + constexpr /*explicit(false)*/ value(const T& val) + : value{val, T(1) / math::pow(T(10), + math::den_size(val))} {} + [[nodiscard]] constexpr explicit operator T() const { return value_; } + [[nodiscard]] constexpr decltype(auto) get() const { return value_; } + + T value_{}; +}; + +template +class value_location : public detail::value { + public: + constexpr /*explicit(false)*/ value_location( + const T& t, const reflection::source_location& sl = + reflection::source_location::current()) + : detail::value{t} { + cfg::location = sl; + } + + constexpr value_location(const T& t, const T precision, + const reflection::source_location& sl = + reflection::source_location::current()) + : detail::value{t, precision} { + cfg::location = sl; + } +}; + +template +struct integral_constant : op { + using value_type = decltype(N); + static constexpr auto value = N; + + [[nodiscard]] constexpr auto operator-() const { + return integral_constant<-N>{}; + } + [[nodiscard]] constexpr explicit operator value_type() const { return N; } + [[nodiscard]] constexpr auto get() const { return N; } +}; + +template +struct floating_point_constant : op { + using value_type = T; + + static constexpr auto epsilon = T(1) / math::pow(T(10), Size - 1); + static constexpr auto value = T(P) * (T(N) + (T(D) / math::pow(T(10), Size))); + + [[nodiscard]] constexpr auto operator-() const { + return floating_point_constant{}; + } + [[nodiscard]] constexpr explicit operator value_type() const { return value; } + [[nodiscard]] constexpr auto get() const { return value; } +}; + +template +struct eq_ : op { + constexpr eq_(const TLhs& lhs = {}, const TRhs& rhs = {}) + : lhs_{lhs}, rhs_{rhs}, value_{[&] { + using std::operator==; + using std::operator<; + + if constexpr (type_traits::has_static_member_object_value_v and + type_traits::has_static_member_object_value_v) { + return TLhs::value == TRhs::value; + } else if constexpr (type_traits::has_static_member_object_epsilon_v< + TLhs> and + type_traits::has_static_member_object_epsilon_v< + TRhs>) { + return math::abs(get(lhs) - get(rhs)) < + math::min_value(TLhs::epsilon, TRhs::epsilon); + } else if constexpr (type_traits::has_static_member_object_epsilon_v< + TLhs>) { + return math::abs(get(lhs) - get(rhs)) < TLhs::epsilon; + } else if constexpr (type_traits::has_static_member_object_epsilon_v< + TRhs>) { + return math::abs(get(lhs) - get(rhs)) < TRhs::epsilon; + } else { + return get(lhs) == get(rhs); + } + }()} {} + + [[nodiscard]] constexpr operator bool() const { return value_; } + [[nodiscard]] constexpr auto lhs() const { return get(lhs_); } + [[nodiscard]] constexpr auto rhs() const { return get(rhs_); } + + const TLhs lhs_{}; + const TRhs rhs_{}; + const bool value_{}; +}; + +template +struct approx_ : op { + constexpr approx_(const TLhs& lhs = {}, const TRhs& rhs = {}, + const TEpsilon& epsilon = {}) + : lhs_{lhs}, rhs_{rhs}, epsilon_{epsilon}, value_{[&] { + using std::operator<; + + if constexpr (type_traits::has_static_member_object_value_v and + type_traits::has_static_member_object_value_v and + type_traits::has_static_member_object_value_v< + TEpsilon>) { + return math::abs_diff(TLhs::value, TRhs::value) < TEpsilon::value; + } else { + return math::abs_diff(get(lhs), get(rhs)) < get(epsilon); + } + }()} {} + + [[nodiscard]] constexpr operator bool() const { return value_; } + [[nodiscard]] constexpr auto lhs() const { return get(lhs_); } + [[nodiscard]] constexpr auto rhs() const { return get(rhs_); } + [[nodiscard]] constexpr auto epsilon() const { return get(epsilon_); } + + const TLhs lhs_{}; + const TRhs rhs_{}; + const TEpsilon epsilon_{}; + const bool value_{}; +}; + +template +struct neq_ : op { + constexpr neq_(const TLhs& lhs = {}, const TRhs& rhs = {}) + : lhs_{lhs}, rhs_{rhs}, value_{[&] { + using std::operator==; + using std::operator!=; + using std::operator>; + + if constexpr (type_traits::has_static_member_object_value_v and + type_traits::has_static_member_object_value_v) { + return TLhs::value != TRhs::value; + } else if constexpr (type_traits::has_static_member_object_epsilon_v< + TLhs> and + type_traits::has_static_member_object_epsilon_v< + TRhs>) { + return math::abs(get(lhs_) - get(rhs_)) > + math::min_value(TLhs::epsilon, TRhs::epsilon); + } else if constexpr (type_traits::has_static_member_object_epsilon_v< + TLhs>) { + return math::abs(get(lhs_) - get(rhs_)) > TLhs::epsilon; + } else if constexpr (type_traits::has_static_member_object_epsilon_v< + TRhs>) { + return math::abs(get(lhs_) - get(rhs_)) > TRhs::epsilon; + } else { + return get(lhs_) != get(rhs_); + } + }()} {} + + [[nodiscard]] constexpr operator bool() const { return value_; } + [[nodiscard]] constexpr auto lhs() const { return get(lhs_); } + [[nodiscard]] constexpr auto rhs() const { return get(rhs_); } + + const TLhs lhs_{}; + const TRhs rhs_{}; + const bool value_{}; +}; + +template +struct gt_ : op { + constexpr gt_(const TLhs& lhs = {}, const TRhs& rhs = {}) + : lhs_{lhs}, rhs_{rhs}, value_{[&] { + using std::operator>; + + if constexpr (type_traits::has_static_member_object_value_v and + type_traits::has_static_member_object_value_v) { + return TLhs::value > TRhs::value; + } else { + return get(lhs_) > get(rhs_); + } + }()} {} + + [[nodiscard]] constexpr operator bool() const { return value_; } + [[nodiscard]] constexpr auto lhs() const { return get(lhs_); } + [[nodiscard]] constexpr auto rhs() const { return get(rhs_); } + + const TLhs lhs_{}; + const TRhs rhs_{}; + const bool value_{}; +}; + +template +struct ge_ : op { + constexpr ge_(const TLhs& lhs = {}, const TRhs& rhs = {}) + : lhs_{lhs}, rhs_{rhs}, value_{[&] { + using std::operator>=; + + if constexpr (type_traits::has_static_member_object_value_v and + type_traits::has_static_member_object_value_v) { + return TLhs::value >= TRhs::value; + } else { + return get(lhs_) >= get(rhs_); + } + }()} {} + + [[nodiscard]] constexpr operator bool() const { return value_; } + [[nodiscard]] constexpr auto lhs() const { return get(lhs_); } + [[nodiscard]] constexpr auto rhs() const { return get(rhs_); } + + const TLhs lhs_{}; + const TRhs rhs_{}; + const bool value_{}; +}; + +template +struct lt_ : op { + constexpr lt_(const TLhs& lhs = {}, const TRhs& rhs = {}) + : lhs_{lhs}, rhs_{rhs}, value_{[&] { + using std::operator<; + + if constexpr (type_traits::has_static_member_object_value_v and + type_traits::has_static_member_object_value_v) { + return TLhs::value < TRhs::value; + } else { + return get(lhs_) < get(rhs_); + } + }()} {} + + [[nodiscard]] constexpr operator bool() const { return value_; } + [[nodiscard]] constexpr auto lhs() const { return get(lhs_); } + [[nodiscard]] constexpr auto rhs() const { return get(rhs_); } + + private: + const TLhs lhs_{}; + const TRhs rhs_{}; + const bool value_{}; +}; + +template +struct le_ : op { + constexpr le_(const TLhs& lhs = {}, const TRhs& rhs = {}) + : lhs_{lhs}, rhs_{rhs}, value_{[&] { + using std::operator<=; + + if constexpr (type_traits::has_static_member_object_value_v and + type_traits::has_static_member_object_value_v) { + return TLhs::value <= TRhs::value; + } else { + return get(lhs_) <= get(rhs_); + } + }()} {} + + [[nodiscard]] constexpr operator bool() const { return value_; } + [[nodiscard]] constexpr auto lhs() const { return get(lhs_); } + [[nodiscard]] constexpr auto rhs() const { return get(rhs_); } + + const TLhs lhs_{}; + const TRhs rhs_{}; + const bool value_{}; +}; + +template +struct and_ : op { + constexpr and_(const TLhs& lhs = {}, const TRhs& rhs = {}) + : lhs_{lhs}, + rhs_{rhs}, + value_{static_cast(lhs) and static_cast(rhs)} {} + + [[nodiscard]] constexpr operator bool() const { return value_; } + [[nodiscard]] constexpr auto lhs() const { return get(lhs_); } + [[nodiscard]] constexpr auto rhs() const { return get(rhs_); } + + const TLhs lhs_{}; + const TRhs rhs_{}; + const bool value_{}; +}; + +template +struct or_ : op { + constexpr or_(const TLhs& lhs = {}, const TRhs& rhs = {}) + : lhs_{lhs}, + rhs_{rhs}, + value_{static_cast(lhs) or static_cast(rhs)} {} + + [[nodiscard]] constexpr operator bool() const { return value_; } + [[nodiscard]] constexpr auto lhs() const { return get(lhs_); } + [[nodiscard]] constexpr auto rhs() const { return get(rhs_); } + + const TLhs lhs_{}; + const TRhs rhs_{}; + const bool value_{}; +}; + +template +struct not_ : op { + explicit constexpr not_(const T& t = {}) + : t_{t}, value_{not static_cast(t)} {} + + [[nodiscard]] constexpr operator bool() const { return value_; } + [[nodiscard]] constexpr auto value() const { return get(t_); } + + const T t_{}; + const bool value_{}; +}; + +template +struct fatal_; + +#if defined(__cpp_exceptions) +template +struct throws_ : op { + constexpr explicit throws_(const TExpr& expr) + : value_{[&expr] { + try { + expr(); + } catch (const TException&) { + return true; + } catch (...) { + return false; + } + return false; + }()} {} + + [[nodiscard]] constexpr operator bool() const { return value_; } + + const bool value_{}; +}; + +template +struct throws_ : op { + constexpr explicit throws_(const TExpr& expr) + : value_{[&expr] { + try { + expr(); + } catch (...) { + return true; + } + return false; + }()} {} + + [[nodiscard]] constexpr operator bool() const { return value_; } + + const bool value_{}; +}; + +template +struct nothrow_ : op { + constexpr explicit nothrow_(const TExpr& expr) + : value_{[&expr] { + try { + expr(); + } catch (...) { + return false; + } + return true; + }()} {} + + [[nodiscard]] constexpr operator bool() const { return value_; } + + const bool value_{}; +}; +#endif + +#if __has_include() and __has_include() +template +struct aborts_ : op { + constexpr explicit aborts_(const TExpr& expr) + : value_{[&expr]() -> bool { + if (const auto pid = fork(); not pid) { + expr(); + exit(0); + } + auto exit_status = 0; + wait(&exit_status); + return exit_status; + }()} {} + + [[nodiscard]] constexpr operator bool() const { return value_; } + + const bool value_{}; +}; +#endif +} // namespace detail + +namespace type_traits { +template +inline constexpr auto is_op_v = __is_base_of(detail::op, T); +} // namespace type_traits + +struct colors { + std::string_view none = "\033[0m"; + std::string_view pass = "\033[32m"; + std::string_view fail = "\033[31m"; +}; + +class printer { + [[nodiscard]] inline auto color(const bool cond) { + return cond ? colors_.pass : colors_.fail; + } + + public: + printer() = default; + /*explicit(false)*/ printer(const colors colors) : colors_{colors} {} + + template + auto& operator<<(const T& t) { + out_ << detail::get(t); + return *this; + } + + template and + type_traits::is_container_v> = 0> + auto& operator<<(T&& t) { + *this << '{'; + auto first = true; + for (const auto& arg : t) { + *this << (first ? "" : ", ") << arg; + first = false; + } + *this << '}'; + return *this; + } + + auto& operator<<(std::string_view sv) { + out_ << sv; + return *this; + } + + template + auto& operator<<(const detail::eq_& op) { + return (*this << color(op) << op.lhs() << " == " << op.rhs() + << colors_.none); + } + + template + auto& operator<<(const detail::approx_& op) { + return (*this << color(op) << op.lhs() << " ~ (" << op.rhs() << " +/- " + << op.epsilon() << ')' << colors_.none); + } + + template + auto& operator<<(const detail::neq_& op) { + return (*this << color(op) << op.lhs() << " != " << op.rhs() + << colors_.none); + } + + template + auto& operator<<(const detail::gt_& op) { + return (*this << color(op) << op.lhs() << " > " << op.rhs() + << colors_.none); + } + + template + auto& operator<<(const detail::ge_& op) { + return (*this << color(op) << op.lhs() << " >= " << op.rhs() + << colors_.none); + } + + template + auto& operator<<(const detail::lt_& op) { + return (*this << color(op) << op.lhs() << " < " << op.rhs() + << colors_.none); + } + + template + auto& operator<<(const detail::le_& op) { + return (*this << color(op) << op.lhs() << " <= " << op.rhs() + << colors_.none); + } + + template + auto& operator<<(const detail::and_& op) { + return (*this << '(' << op.lhs() << color(op) << " and " << colors_.none + << op.rhs() << ')'); + } + + template + auto& operator<<(const detail::or_& op) { + return (*this << '(' << op.lhs() << color(op) << " or " << colors_.none + << op.rhs() << ')'); + } + + template + auto& operator<<(const detail::not_& op) { + return (*this << color(op) << "not " << op.value() << colors_.none); + } + + template + auto& operator<<(const detail::fatal_& fatal) { + return (*this << fatal.get()); + } + +#if defined(__cpp_exceptions) + template + auto& operator<<(const detail::throws_& op) { + return (*this << color(op) << "throws<" + << reflection::type_name() << ">" + << colors_.none); + } + + template + auto& operator<<(const detail::throws_& op) { + return (*this << color(op) << "throws" << colors_.none); + } + + template + auto& operator<<(const detail::nothrow_& op) { + return (*this << color(op) << "nothrow" << colors_.none); + } +#endif + +#if __has_include() and __has_include() + template + auto& operator<<(const detail::aborts_& op) { + return (*this << color(op) << "aborts" << colors_.none); + } +#endif + + template + auto& operator<<(const detail::type_&) { + return (*this << reflection::type_name()); + } + + auto str() const { return out_.str(); } + const auto& colors() const { return colors_; } + + private: + ut::colors colors_{}; + std::ostringstream out_{}; +}; + +template +class reporter { + public: + constexpr auto operator=(TPrinter printer) { + printer_ = static_cast(printer); + } + + auto on(events::test_begin test_begin) -> void { + printer_ << "Running \"" << test_begin.name << "\"..."; + fails_ = asserts_.fail; + } + + auto on(events::test_run test_run) -> void { + printer_ << "\n \"" << test_run.name << "\"..."; + } + + auto on(events::test_skip test_skip) -> void { + printer_ << test_skip.name << "...SKIPPED\n"; + ++tests_.skip; + } + + auto on(events::test_end) -> void { + if (asserts_.fail > fails_) { + ++tests_.fail; + printer_ << '\n' + << printer_.colors().fail << "FAILED" << printer_.colors().none + << '\n'; + } else { + ++tests_.pass; + printer_ << printer_.colors().pass << "PASSED" << printer_.colors().none + << '\n'; + } + } + + template + auto on(events::log l) -> void { + printer_ << l.msg; + } + + auto on(events::exception exception) -> void { + printer_ << "\n " << printer_.colors().fail + << "Unexpected exception with message:\n" + << exception.what() << printer_.colors().none; + ++asserts_.fail; + } + + template + auto on(events::assertion_pass) -> void { + ++asserts_.pass; + } + + template + auto on(events::assertion_fail assertion) -> void { + constexpr auto short_name = [](std::string_view name) { + return name.rfind('/') != std::string_view::npos + ? name.substr(name.rfind('/') + 1) + : name; + }; + printer_ << "\n " << short_name(assertion.location.file_name()) << ':' + << assertion.location.line() << ':' << printer_.colors().fail + << "FAILED" << printer_.colors().none << " [" << std::boolalpha + << assertion.expr << printer_.colors().none << ']'; + ++asserts_.fail; + } + + auto on(events::fatal_assertion) -> void {} + + auto on(events::summary) -> void { + if (tests_.fail or asserts_.fail) { + printer_ << "\n========================================================" + "=======================\n" + << "tests: " << (tests_.pass + tests_.fail) << " | " + << printer_.colors().fail << tests_.fail << " failed" + << printer_.colors().none << '\n' + << "asserts: " << (asserts_.pass + asserts_.fail) << " | " + << asserts_.pass << " passed" + << " | " << printer_.colors().fail << asserts_.fail << " failed" + << printer_.colors().none << '\n'; + std::cerr << printer_.str() << std::endl; + } else { + std::cout << printer_.colors().pass << "All tests passed" + << printer_.colors().none << " (" << asserts_.pass + << " asserts in " << tests_.pass << " tests)\n"; + + if (tests_.skip) { + std::cout << tests_.skip << " tests skipped\n"; + } + + std::cout.flush(); + } + } + + protected: + struct { + std::size_t pass{}; + std::size_t fail{}; + std::size_t skip{}; + } tests_{}; + + struct { + std::size_t pass{}; + std::size_t fail{}; + } asserts_{}; + + std::size_t fails_{}; + + TPrinter printer_{}; +}; + +struct options { + std::string_view filter{}; + std::vector tag{}; + ut::colors colors{}; + bool dry_run{}; +}; + +struct run_cfg { + bool report_errors{false}; +}; + +template , auto MaxPathSize = 16> +class runner { + class filter { + static constexpr auto delim = "."; + + public: + constexpr /*explicit(false)*/ filter(std::string_view _filter = {}) + : path_{utility::split(_filter, delim)} {} + + template + constexpr auto operator()(const std::size_t level, const TPath& path) const + -> bool { + for (auto i = 0u; i < math::min_value(level + 1, std::size(path_)); ++i) { + if (not utility::is_match(path[i], path_[i])) { + return false; + } + } + return true; + } + + private: + std::vector path_{}; + }; + + public: + constexpr runner() = default; + constexpr runner(TReporter reporter, std::size_t suites_size) + : reporter_{std::move(reporter)}, suites_(suites_size) {} + + ~runner() { + const auto should_run = not run_; + + if (should_run) { + static_cast(run()); + } + + if (not dry_run_) { + report_summary(); + } + + if (should_run and fails_) { + std::exit(-1); + } + } + + auto operator=(const options& options) { + filter_ = options.filter; + tag_ = options.tag; + dry_run_ = options.dry_run; + reporter_ = {options.colors}; + } + + template + auto on(events::suite suite) { + suites_.push_back(suite.run); + } + + template + auto on(events::test test) { + path_[level_] = test.name; + + auto execute = std::empty(test.tag); + for (const auto& tag_element : test.tag) { + if (utility::is_match(tag_element, "skip")) { + on(events::skip<>{.type = test.type, .name = test.name}); + return; + } + + for (const auto& ftag : tag_) { + if (utility::is_match(tag_element, ftag)) { + execute = true; + break; + } + } + } + + if (not execute) { + on(events::skip<>{.type = test.type, .name = test.name}); + return; + } + + if (filter_(level_, path_)) { + if (not level_++) { + reporter_.on(events::test_begin{ + .type = test.type, .name = test.name, .location = test.location}); + } else { + reporter_.on(events::test_run{.type = test.type, .name = test.name}); + } + + if (dry_run_) { + for (auto i = 0u; i < level_; ++i) { + std::cout << (i ? "." : "") << path_[i]; + } + std::cout << '\n'; + } + +#if defined(__cpp_exceptions) + try { +#endif + test(); +#if defined(__cpp_exceptions) + } catch (const events::fatal_assertion&) { + } catch (const std::exception& exception) { + ++fails_; + reporter_.on(events::exception{exception.what()}); + } catch (...) { + ++fails_; + reporter_.on(events::exception{"Unknown exception"}); + } +#endif + + if (not --level_) { + reporter_.on(events::test_end{.type = test.type, .name = test.name}); + } + } + } + + template + auto on(events::skip test) { + reporter_.on(events::test_skip{.type = test.type, .name = test.name}); + } + + template + [[nodiscard]] auto on(events::assertion assertion) -> bool { + if (dry_run_) { + return true; + } + + if (static_cast(assertion.expr)) { + reporter_.on(events::assertion_pass{ + .expr = assertion.expr, .location = assertion.location}); + return true; + } + + ++fails_; + reporter_.on(events::assertion_fail{.expr = assertion.expr, + .location = assertion.location}); + return false; + } + + auto on(events::fatal_assertion fatal_assertion) { + reporter_.on(fatal_assertion); + +#if defined(__cpp_exceptions) + if (not level_) { + report_summary(); + } + throw fatal_assertion; +#else + if (level_) { + reporter_.on(events::test_end{}); + } + report_summary(); + std::abort(); +#endif + } + + template + auto on(events::log l) { + reporter_.on(l); + } + + [[nodiscard]] auto run(run_cfg rc = {}) -> bool { + run_ = true; + for (const auto& suite : suites_) { + suite(); + } + suites_.clear(); + + if (rc.report_errors) { + report_summary(); + } + + return fails_ > 0; + } + + auto report_summary() -> void { + if (static auto once = true; once) { + once = false; + reporter_.on(events::summary{}); + } + } + + protected: + TReporter reporter_{}; + std::vector suites_{}; + std::size_t level_{}; + bool run_{}; + std::size_t fails_{}; + std::array path_{}; + filter filter_{}; + std::vector tag_{}; + bool dry_run_{}; +}; + +struct override {}; + +template +[[maybe_unused]] inline auto cfg = runner>{}; + +namespace detail { +struct tag { + std::vector name{}; +}; + +template +[[nodiscard]] constexpr decltype(auto) on(TEvent&& event) { + return ut::cfg::type>.on( + static_cast(event)); +} + +template +struct test_location { + template + constexpr test_location(const T& t, + const reflection::source_location& sl = + reflection::source_location::current()) + : test{t}, location{sl} {} + + Test test{}; + reflection::source_location location{}; +}; + +struct test { + std::string_view type{}; + std::string_view name{}; + std::vector tag{}; + + template + constexpr auto operator=(test_location _test) { + on(events::test{.type = type, + .name = name, + .tag = tag, + .location = _test.location, + .arg = none{}, + .run = _test.test}); + return _test.test; + } + + template > = 0> + constexpr auto operator=(Test _test) -> + typename type_traits::identity::type { + on(events::test{.type = type, + .name = name, + .tag = tag, + .location = {}, + .arg = none{}, + .run = static_cast(_test)}); + return _test; + } + + constexpr auto operator=(void (*_test)(std::string_view)) const { + return _test(name); + } + + template > = 0> + constexpr auto operator=(Test _test) + -> decltype(_test(type_traits::declval())) { + return _test(name); + } +}; + +struct log { + struct next { + template + auto& operator<<(const TMsg& msg) { + on(events::log{' '}); + on(events::log{msg}); + return *this; + } + }; + + template + auto operator<<(const TMsg& msg) -> next { + on(events::log{'\n'}); + on(events::log{msg}); + return next{}; + } +}; + +template +class terse_ { + public: + constexpr explicit terse_(const TExpr& expr) : expr_{expr} { cfg::wip = {}; } + + ~terse_() noexcept(false) { + if (static auto once = true; once and not cfg::wip) { + once = {}; + } else { + return; + } + + cfg::wip = true; + + void(detail::on( + events::assertion{.expr = expr_, .location = cfg::location})); + } + + private: + const TExpr& expr_; +}; + +struct that_ { + template + struct expr { + using type = expr; + + constexpr explicit expr(const T& t) : t_{t} {} + + [[nodiscard]] constexpr auto operator!() const { return not_{*this}; } + + template + [[nodiscard]] constexpr auto operator==(const TRhs& rhs) const { + return eq_{t_, rhs}; + } + + template + [[nodiscard]] constexpr auto operator!=(const TRhs& rhs) const { + return neq_{t_, rhs}; + } + + template + [[nodiscard]] constexpr auto operator>(const TRhs& rhs) const { + return gt_{t_, rhs}; + } + + template + [[nodiscard]] constexpr auto operator>=(const TRhs& rhs) const { + return ge_{t_, rhs}; + } + + template + [[nodiscard]] constexpr auto operator<(const TRhs& rhs) const { + return lt_{t_, rhs}; + } + + template + [[nodiscard]] constexpr auto operator<=(const TRhs& rhs) const { + return le_{t_, rhs}; + } + + [[nodiscard]] constexpr operator bool() const { + return static_cast(t_); + } + + const T t_{}; + }; + + template + [[nodiscard]] constexpr auto operator%(const T& t) const { + return expr{t}; + } +}; + +template +struct fatal_ : op { + using type = fatal_; + + constexpr explicit fatal_(const TExpr& expr) : expr_{expr} {} + + [[nodiscard]] constexpr operator bool() const { + if (static_cast(expr_)) { + } else { + cfg::wip = true; + void(on( + events::assertion{.expr = expr_, .location = cfg::location})); + on(events::fatal_assertion{}); + } + return static_cast(expr_); + } + + [[nodiscard]] constexpr decltype(auto) get() const { return expr_; } + + TExpr expr_{}; +}; + +template +struct expect_ { + constexpr explicit expect_(bool value) : value_{value} { cfg::wip = {}; } + + template + auto& operator<<(const TMsg& msg) { + if (not value_) { + on(events::log{' '}); + on(events::log{msg}); + } + return *this; + } + + [[nodiscard]] constexpr operator bool() const { return value_; } + + bool value_{}; +}; +} // namespace detail + +namespace literals { +[[nodiscard]] inline auto operator""_test(const char* name, + decltype(sizeof("")) size) { + return detail::test{"test", std::string_view{name, size}}; +} + +template +[[nodiscard]] constexpr auto operator""_i() { + return detail::integral_constant()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_s() { + return detail::integral_constant()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_c() { + return detail::integral_constant()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_sc() { + return detail::integral_constant()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_l() { + return detail::integral_constant()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_ll() { + return detail::integral_constant()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_u() { + return detail::integral_constant()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_uc() { + return detail::integral_constant()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_us() { + return detail::integral_constant()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_ul() { + return detail::integral_constant()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_ull() { + return detail::integral_constant()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_i8() { + return detail::integral_constant()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_i16() { + return detail::integral_constant()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_i32() { + return detail::integral_constant()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_i64() { + return detail::integral_constant()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_u8() { + return detail::integral_constant()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_u16() { + return detail::integral_constant()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_u32() { + return detail::integral_constant()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_u64() { + return detail::integral_constant()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_f() { + return detail::floating_point_constant< + float, math::num(), + math::den(), + math::den_size()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_d() { + return detail::floating_point_constant< + double, math::num(), + math::den(), + math::den_size()>{}; +} + +template +[[nodiscard]] constexpr auto operator""_ld() { + return detail::floating_point_constant< + long double, math::num(), + math::den(), + math::den_size()>{}; +} + +constexpr auto operator""_b(const char* name, decltype(sizeof("")) size) { + struct named : std::string_view, detail::op { + using value_type = bool; + [[nodiscard]] constexpr operator value_type() const { return true; } + [[nodiscard]] constexpr auto operator==(const named&) const { return true; } + [[nodiscard]] constexpr auto operator==(const bool other) const { + return other; + } + }; + return named{{name, size}, {}}; +} +} // namespace literals + +namespace operators { +[[nodiscard]] constexpr auto operator==(std::string_view lhs, + std::string_view rhs) { + return detail::eq_{lhs, rhs}; +} + +[[nodiscard]] constexpr auto operator!=(std::string_view lhs, + std::string_view rhs) { + return detail::neq_{lhs, rhs}; +} + +template > = 0> +[[nodiscard]] constexpr auto operator==(T&& lhs, T&& rhs) { + return detail::eq_{static_cast(lhs), static_cast(rhs)}; +} + +template > = 0> +[[nodiscard]] constexpr auto operator!=(T&& lhs, T&& rhs) { + return detail::neq_{static_cast(lhs), static_cast(rhs)}; +} + +template or + type_traits::is_op_v> = 0> +[[nodiscard]] constexpr auto operator==(const TLhs& lhs, const TRhs& rhs) { + return detail::eq_{lhs, rhs}; +} + +template or + type_traits::is_op_v> = 0> +[[nodiscard]] constexpr auto operator!=(const TLhs& lhs, const TRhs& rhs) { + return detail::neq_{lhs, rhs}; +} + +template or + type_traits::is_op_v> = 0> +[[nodiscard]] constexpr auto operator>(const TLhs& lhs, const TRhs& rhs) { + return detail::gt_{lhs, rhs}; +} + +template or + type_traits::is_op_v> = 0> +[[nodiscard]] constexpr auto operator>=(const TLhs& lhs, const TRhs& rhs) { + return detail::ge_{lhs, rhs}; +} + +template or + type_traits::is_op_v> = 0> +[[nodiscard]] constexpr auto operator<(const TLhs& lhs, const TRhs& rhs) { + return detail::lt_{lhs, rhs}; +} + +template or + type_traits::is_op_v> = 0> +[[nodiscard]] constexpr auto operator<=(const TLhs& lhs, const TRhs& rhs) { + return detail::le_{lhs, rhs}; +} + +template or + type_traits::is_op_v> = 0> +[[nodiscard]] constexpr auto operator and(const TLhs& lhs, const TRhs& rhs) { + return detail::and_{lhs, rhs}; +} + +template or + type_traits::is_op_v> = 0> +[[nodiscard]] constexpr auto operator or(const TLhs& lhs, const TRhs& rhs) { + return detail::or_{lhs, rhs}; +} + +template > = 0> +[[nodiscard]] constexpr auto operator not(const T& t) { + return detail::not_{t}; +} + +template +[[nodiscard]] inline auto operator>>( + const T& t, const detail::value_location&) { + return detail::fatal_{t}; +} + +template +[[nodiscard]] auto operator/(const detail::tag& tag, Test test) { + for (const auto& name : tag.name) { + test.tag.push_back(name); + } + return test; +} + +[[nodiscard]] inline auto operator/(const detail::tag& lhs, + const detail::tag& rhs) { + std::vector tag{}; + for (const auto& name : lhs.name) { + tag.push_back(name); + } + for (const auto& name : rhs.name) { + tag.push_back(name); + } + return detail::tag{tag}; +} + +template > = 0> +[[nodiscard]] constexpr auto operator|(const F& f, const T& t) { + return [f, t](const auto name) { + for (const auto& arg : t) { + detail::on(events::test{.type = "test", + .name = name, + .tag = {}, + .location = {}, + .arg = arg, + .run = f}); + } + }; +} + +template < + class F, template class T, class... Ts, + type_traits::requires_t>> = 0> +[[nodiscard]] constexpr auto operator|(const F& f, const T& t) { + return [f, t](const auto name) { + apply( + [f, name](const auto&... args) { + (detail::on(events::test{.type = "test", + .name = name, + .tag = {}, + .location = {}, + .arg = args, + .run = f}), + ...); + }, + t); + }; +} + +namespace terse { +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wunused-comparison" +#endif + +[[maybe_unused]] constexpr struct { +} _t; + +template +constexpr auto operator%(const T& t, const decltype(_t)&) { + return detail::value{t}; +} + +template +inline auto operator>>(const T& t, + const detail::value_location&) { + using fatal_t = detail::fatal_; + struct fatal_ : fatal_t, detail::log { + using type = fatal_t; + using fatal_t::fatal_t; + const detail::terse_ _{*this}; + }; + return fatal_{t}; +} + +template > = 0> +constexpr auto operator==( + const T& lhs, const detail::value_location& rhs) { + using eq_t = detail::eq_>; + struct eq_ : eq_t, detail::log { + using type = eq_t; + using eq_t::eq_t; + const detail::terse_ _{*this}; + }; + return eq_{lhs, rhs}; +} + +template > = 0> +constexpr auto operator==( + const detail::value_location& lhs, const T& rhs) { + using eq_t = detail::eq_, T>; + struct eq_ : eq_t, detail::log { + using type = eq_t; + using eq_t::eq_t; + const detail::terse_ _{*this}; + }; + return eq_{lhs, rhs}; +} + +template > = 0> +constexpr auto operator!=( + const T& lhs, const detail::value_location& rhs) { + using neq_t = detail::neq_>; + struct neq_ : neq_t, detail::log { + using type = neq_t; + using neq_t::neq_t; + const detail::terse_ _{*this}; + }; + return neq_{lhs, rhs}; +} + +template > = 0> +constexpr auto operator!=( + const detail::value_location& lhs, const T& rhs) { + using neq_t = detail::neq_, T>; + struct neq_ : neq_t { + using type = neq_t; + using neq_t::neq_t; + const detail::terse_ _{*this}; + }; + return neq_{lhs, rhs}; +} + +template > = 0> +constexpr auto operator>( + const T& lhs, const detail::value_location& rhs) { + using gt_t = detail::gt_>; + struct gt_ : gt_t, detail::log { + using type = gt_t; + using gt_t::gt_t; + const detail::terse_ _{*this}; + }; + return gt_{lhs, rhs}; +} + +template > = 0> +constexpr auto operator>( + const detail::value_location& lhs, const T& rhs) { + using gt_t = detail::gt_, T>; + struct gt_ : gt_t, detail::log { + using type = gt_t; + using gt_t::gt_t; + const detail::terse_ _{*this}; + }; + return gt_{lhs, rhs}; +} + +template > = 0> +constexpr auto operator>=( + const T& lhs, const detail::value_location& rhs) { + using ge_t = detail::ge_>; + struct ge_ : ge_t, detail::log { + using type = ge_t; + using ge_t::ge_t; + const detail::terse_ _{*this}; + }; + return ge_{lhs, rhs}; +} + +template > = 0> +constexpr auto operator>=( + const detail::value_location& lhs, const T& rhs) { + using ge_t = detail::ge_, T>; + struct ge_ : ge_t, detail::log { + using type = ge_t; + using ge_t::ge_t; + const detail::terse_ _{*this}; + }; + return ge_{lhs, rhs}; +} + +template > = 0> +constexpr auto operator<( + const T& lhs, const detail::value_location& rhs) { + using lt_t = detail::lt_>; + struct lt_ : lt_t, detail::log { + using type = lt_t; + using lt_t::lt_t; + const detail::terse_ _{*this}; + }; + return lt_{lhs, rhs}; +} + +template > = 0> +constexpr auto operator<( + const detail::value_location& lhs, const T& rhs) { + using lt_t = detail::lt_, T>; + struct lt_ : lt_t, detail::log { + using type = lt_t; + using lt_t::lt_t; + const detail::terse_ _{*this}; + }; + return lt_{lhs, rhs}; +} + +template > = 0> +constexpr auto operator<=( + const T& lhs, const detail::value_location& rhs) { + using le_t = detail::le_>; + struct le_ : le_t, detail::log { + using type = le_t; + using le_t::le_t; + const detail::terse_ _{*this}; + }; + return le_{lhs, rhs}; +} + +template > = 0> +constexpr auto operator<=( + const detail::value_location& lhs, const T& rhs) { + using le_t = detail::le_, T>; + struct le_ : le_t { + using type = le_t; + using le_t::le_t; + const detail::terse_ _{*this}; + }; + return le_{lhs, rhs}; +} + +template or + type_traits::is_op_v> = 0> +constexpr auto operator and(const TLhs& lhs, const TRhs& rhs) { + using and_t = detail::and_; + struct and_ : and_t, detail::log { + using type = and_t; + using and_t::and_t; + const detail::terse_ _{*this}; + }; + return and_{lhs, rhs}; +} + +template or + type_traits::is_op_v> = 0> +constexpr auto operator or(const TLhs& lhs, const TRhs& rhs) { + using or_t = detail::or_; + struct or_ : or_t, detail::log { + using type = or_t; + using or_t::or_t; + const detail::terse_ _{*this}; + }; + return or_{lhs, rhs}; +} + +template > = 0> +constexpr auto operator not(const T& t) { + using not_t = detail::not_; + struct not_ : not_t, detail::log { + using type = not_t; + using not_t::not_t; + const detail::terse_ _{*this}; + }; + return not_{t}; +} + +} // namespace terse +} // namespace operators + +template or + type_traits::is_convertible_v> = 0> +constexpr auto expect(const TExpr& expr, + const reflection::source_location& sl = + reflection::source_location::current()) { + return detail::expect_{detail::on( + events::assertion{.expr = expr, .location = sl})}; +} + +[[maybe_unused]] constexpr auto fatal = detail::fatal{}; + +#if defined(__cpp_nontype_template_parameter_class) +template +#else +template +#endif +constexpr auto constant = Constant; + +#if defined(__cpp_exceptions) +template +[[nodiscard]] constexpr auto throws(const TExpr& expr) { + return detail::throws_{expr}; +} + +template +[[nodiscard]] constexpr auto throws(const TExpr& expr) { + return detail::throws_{expr}; +} + +template +[[nodiscard]] constexpr auto nothrow(const TExpr& expr) { + return detail::nothrow_{expr}; +} +#endif + +#if __has_include() and __has_include() +template +[[nodiscard]] constexpr auto aborts(const TExpr& expr) { + return detail::aborts_{expr}; +} +#endif + +using _b = detail::value; +using _c = detail::value; +using _sc = detail::value; +using _s = detail::value; +using _i = detail::value; +using _l = detail::value; +using _ll = detail::value; +using _u = detail::value; +using _uc = detail::value; +using _us = detail::value; +using _ul = detail::value; +using _ull = detail::value; +using _i8 = detail::value; +using _i16 = detail::value; +using _i32 = detail::value; +using _i64 = detail::value; +using _u8 = detail::value; +using _u16 = detail::value; +using _u32 = detail::value; +using _u64 = detail::value; +using _f = detail::value; +using _d = detail::value; +using _ld = detail::value; + +template +struct _t : detail::value { + constexpr explicit _t(const T& t) : detail::value{t} {} +}; + +struct suite { + template + constexpr /*explicit(false)*/ suite(TSuite _suite) { + static_assert(1 == sizeof(_suite)); + detail::on( + events::suite{.run = +_suite}); + } +}; + +[[maybe_unused]] inline auto log = detail::log{}; +[[maybe_unused]] inline auto that = detail::that_{}; +[[maybe_unused]] constexpr auto test = [](const auto name) { + return detail::test{"test", name}; +}; +[[maybe_unused]] constexpr auto should = test; +[[maybe_unused]] inline auto tag = [](const auto name) { + return detail::tag{{name}}; +}; +[[maybe_unused]] inline auto skip = tag("skip"); +template +[[maybe_unused]] constexpr auto type = detail::type_(); + +template +[[nodiscard]] constexpr auto eq(const TLhs& lhs, const TRhs& rhs) { + return detail::eq_{lhs, rhs}; +} +template +[[nodiscard]] constexpr auto approx(const TLhs& lhs, const TRhs& rhs, + const TEpsilon& epsilon) { + return detail::approx_{lhs, rhs, epsilon}; +} +template +[[nodiscard]] constexpr auto neq(const TLhs& lhs, const TRhs& rhs) { + return detail::neq_{lhs, rhs}; +} +template +[[nodiscard]] constexpr auto gt(const TLhs& lhs, const TRhs& rhs) { + return detail::gt_{lhs, rhs}; +} +template +[[nodiscard]] constexpr auto ge(const TLhs& lhs, const TRhs& rhs) { + return detail::ge_{lhs, rhs}; +} +template +[[nodiscard]] constexpr auto lt(const TLhs& lhs, const TRhs& rhs) { + return detail::lt_{lhs, rhs}; +} +template +[[nodiscard]] constexpr auto le(const TLhs& lhs, const TRhs& rhs) { + return detail::le_{lhs, rhs}; +} + +template +[[nodiscard]] constexpr auto mut(const T& t) noexcept -> T& { + return const_cast(t); +} + +namespace bdd { +[[maybe_unused]] constexpr auto feature = [](const auto name) { + return detail::test{"feature", name}; +}; +[[maybe_unused]] constexpr auto scenario = [](const auto name) { + return detail::test{"scenario", name}; +}; +[[maybe_unused]] constexpr auto given = [](const auto name) { + return detail::test{"given", name}; +}; +[[maybe_unused]] constexpr auto when = [](const auto name) { + return detail::test{"when", name}; +}; +[[maybe_unused]] constexpr auto then = [](const auto name) { + return detail::test{"then", name}; +}; + +namespace gherkin { +class steps { + using step_t = std::string; + using steps_t = void (*)(steps&); + using gherkin_t = std::vector; + using call_step_t = utility::function; + using call_steps_t = std::vector>; + + class step { + public: + template + step(steps& steps, const TPattern& pattern) + : steps_{steps}, pattern_{pattern} {} + + ~step() { steps_.next(pattern_); } + + template + auto operator=(const TExpr& expr) -> void { + for (const auto& [pattern, _] : steps_.call_steps()) { + if (pattern_ == pattern) { + return; + } + } + + steps_.call_steps().emplace_back( + pattern_, [expr, pattern = pattern_](const auto& _step) { + [=](type_traits::list) { + log << _step; + auto i = 0u; + const auto& ms = utility::match(pattern, _step); + expr(lexical_cast(ms[i++])...); + }(typename type_traits::function_traits::args{}); + }); + } + + private: + template + static auto lexical_cast(const std::string& str) { + T t{}; + std::istringstream iss{}; + iss.str(str); + if constexpr (std::is_same_v) { + t = iss.str(); + } else { + iss >> t; + } + return t; + } + + steps& steps_; + std::string pattern_{}; + }; + + public: + template + constexpr /*explicit(false)*/ steps(const TSteps& _steps) : steps_{_steps} {} + + template + auto operator|(const TGherkin& gherkin) { + gherkin_ = utility::split(gherkin, '\n'); + for (auto& _step : gherkin_) { + _step.erase(0, _step.find_first_not_of(" \t")); + } + + return [this] { + step_ = {}; + steps_(*this); + }; + } + auto feature(const std::string& pattern) { + return step{*this, "Feature: " + pattern}; + } + auto scenario(const std::string& pattern) { + return step{*this, "Scenario: " + pattern}; + } + auto given(const std::string& pattern) { + return step{*this, "Given " + pattern}; + } + auto when(const std::string& pattern) { + return step{*this, "When " + pattern}; + } + auto then(const std::string& pattern) { + return step{*this, "Then " + pattern}; + } + + private: + template + auto next(const TPattern& pattern) -> void { + const auto is_scenario = [&pattern](const auto& _step) { + constexpr auto scenario = "Scenario"; + return pattern.find(scenario) == std::string::npos and + _step.find(scenario) != std::string::npos; + }; + + const auto call_steps = [this, is_scenario](const auto& _step, + const auto i) { + for (const auto& [name, call] : call_steps_) { + if (is_scenario(_step)) { + break; + } + + if (utility::is_match(_step, name) or + not std::empty(utility::match(name, _step))) { + step_ = i; + call(_step); + } + } + }; + + decltype(step_) i{}; + for (const auto& _step : gherkin_) { + if (i++ == step_) { + call_steps(_step, i); + } + } + } + + auto call_steps() -> call_steps_t& { return call_steps_; } + + steps_t steps_{}; + gherkin_t gherkin_{}; + call_steps_t call_steps_{}; + decltype(sizeof("")) step_{}; +}; +} // namespace gherkin +} // namespace bdd + +namespace spec { +[[maybe_unused]] constexpr auto describe = [](const auto name) { + return detail::test{"describe", name}; +}; +[[maybe_unused]] constexpr auto it = [](const auto name) { + return detail::test{"it", name}; +}; +} // namespace spec + +using literals::operator""_test; + +using literals::operator""_b; +using literals::operator""_i; +using literals::operator""_s; +using literals::operator""_c; +using literals::operator""_sc; +using literals::operator""_l; +using literals::operator""_ll; +using literals::operator""_u; +using literals::operator""_uc; +using literals::operator""_us; +using literals::operator""_ul; +using literals::operator""_i8; +using literals::operator""_i16; +using literals::operator""_i32; +using literals::operator""_i64; +using literals::operator""_u8; +using literals::operator""_u16; +using literals::operator""_u32; +using literals::operator""_u64; +using literals::operator""_f; +using literals::operator""_d; +using literals::operator""_ld; +using literals::operator""_ull; + +using operators::operator==; +using operators::operator!=; +using operators::operator>; +using operators::operator>=; +using operators::operator<; +using operators::operator<=; +using operators::operator and; +using operators::operator or; +using operators::operator not; +using operators::operator|; +using operators::operator/; +using operators::operator>>; +} // namespace boost::inline ext::ut::inline v1_1_9 +#endif \ No newline at end of file From 49dacfcf57995da2195172ab68b539b032058ae8 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 7 Sep 2022 11:32:52 +0200 Subject: [PATCH 071/130] :recycle: (RFIDKit): onTagActivated - pass MagicCard as `const &` to callback --- libs/ActivityKit/include/activities/DisplayTags.h | 2 +- libs/ActivityKit/source/activities/DisplayTags.cpp | 2 +- libs/RFIDKit/include/RFIDKit.h | 6 +++--- libs/RFIDKit/source/RFIDKit.cpp | 4 ++-- libs/RFIDKit/tests/RFIDKit_test.cpp | 12 ++++++------ 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/libs/ActivityKit/include/activities/DisplayTags.h b/libs/ActivityKit/include/activities/DisplayTags.h index a695fb9675..28c22bd0b9 100644 --- a/libs/ActivityKit/include/activities/DisplayTags.h +++ b/libs/ActivityKit/include/activities/DisplayTags.h @@ -24,7 +24,7 @@ class DisplayTags : public interface::Activity RFIDKit &_rfidkit; interface::VideoKit &_videokit; std::array _path_buffer = {}; - std::function _backup_callback {}; + std::function _backup_callback {}; }; } // namespace leka::activity diff --git a/libs/ActivityKit/source/activities/DisplayTags.cpp b/libs/ActivityKit/source/activities/DisplayTags.cpp index 956d6eeba3..55201dad60 100644 --- a/libs/ActivityKit/source/activities/DisplayTags.cpp +++ b/libs/ActivityKit/source/activities/DisplayTags.cpp @@ -14,7 +14,7 @@ void DisplayTags::start() _backup_callback = _rfidkit.getCallback(); - auto on_tag_detected_callback = [this](MagicCard &card) { + auto on_tag_detected_callback = [this](const MagicCard &card) { snprintf(_path_buffer.data(), _path_buffer.size(), "fs/home/img/id/%.4x.jpg", card.getId()); _videokit.displayImage(_path_buffer.data()); diff --git a/libs/RFIDKit/include/RFIDKit.h b/libs/RFIDKit/include/RFIDKit.h index b655fafd39..3a643a8332 100644 --- a/libs/RFIDKit/include/RFIDKit.h +++ b/libs/RFIDKit/include/RFIDKit.h @@ -18,14 +18,14 @@ class RFIDKit void init(); void registerMagicCard(); [[nodiscard]] auto isTagSignatureValid(rfid::Tag tag) const -> bool; - void onTagActivated(std::function const &callback); + void onTagActivated(std::function const &callback); - [[nodiscard]] auto getCallback() const -> const std::function &; + [[nodiscard]] auto getCallback() const -> const std::function &; private: interface::RFIDReader &_rfid_reader; MagicCard _card = MagicCard::none; - std::function _on_tag_available_callback; + std::function _on_tag_available_callback; boost::sml::sm state_machine {_rfid_reader}; static constexpr std::array leka_tag_header = {0x4C, 0x45, 0x4B, 0x41}; diff --git a/libs/RFIDKit/source/RFIDKit.cpp b/libs/RFIDKit/source/RFIDKit.cpp index fffe0e7dbe..0b38768f42 100644 --- a/libs/RFIDKit/source/RFIDKit.cpp +++ b/libs/RFIDKit/source/RFIDKit.cpp @@ -39,12 +39,12 @@ auto RFIDKit::isTagSignatureValid(rfid::Tag tag) const -> bool return std::equal(std::begin(leka_tag_header), std::end(leka_tag_header), std::begin(tag.data)); } -void RFIDKit::onTagActivated(std::function const &callback) +void RFIDKit::onTagActivated(std::function const &callback) { _on_tag_available_callback = callback; } -[[nodiscard]] auto RFIDKit::getCallback() const -> const std::function & +[[nodiscard]] auto RFIDKit::getCallback() const -> const std::function & { return _on_tag_available_callback; } diff --git a/libs/RFIDKit/tests/RFIDKit_test.cpp b/libs/RFIDKit/tests/RFIDKit_test.cpp index acdc63e70d..0be9d01573 100644 --- a/libs/RFIDKit/tests/RFIDKit_test.cpp +++ b/libs/RFIDKit/tests/RFIDKit_test.cpp @@ -23,7 +23,7 @@ class RFIDKitTest : public ::testing::Test RFIDKit rfid_kit; mock::CoreRFIDReader mock_reader {}; - MockFunction mock_callback; + MockFunction mock_callback; std::function magic_card_callback {}; }; @@ -135,16 +135,16 @@ TEST_F(RFIDKitTest, getNullPtrCallback) TEST_F(RFIDKitTest, getCallback) { - auto card = MagicCard::none; + auto _card = MagicCard::none; - const std::function expected_passed_callback = [](MagicCard &card) { - card = MagicCard::activity_super_simon; + const std::function expected_passed_callback = [&_card](const MagicCard &card) { + _card = card; }; rfid_kit.onTagActivated(expected_passed_callback); auto callback = rfid_kit.getCallback(); - callback(card); + callback(MagicCard::activity_super_simon); - EXPECT_EQ(card, MagicCard::activity_super_simon); + EXPECT_EQ(_card, MagicCard::activity_super_simon); } From f4211fbe8b06bbb2231c2d7aa804faced93f3acc Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Thu, 21 Jul 2022 12:40:19 +0200 Subject: [PATCH 072/130] :wrench: (serialnumber): Add /fs/sys/date_of_test Used for serial number generation --- fs/sys/date_of_test | 1 + 1 file changed, 1 insertion(+) create mode 100755 fs/sys/date_of_test diff --git a/fs/sys/date_of_test b/fs/sys/date_of_test new file mode 100755 index 0000000000..4ad5df7db8 --- /dev/null +++ b/fs/sys/date_of_test @@ -0,0 +1 @@ +220619 From b8a915cd8264578759158085d8a83a8751d6ca6e Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Mon, 18 Jul 2022 16:24:32 +0200 Subject: [PATCH 073/130] :sparkles: (firmware): Get current OS version from os-version file --- include/interface/drivers/FirmwareUpdate.h | 1 + libs/FirmwareKit/include/FirmwareKit.h | 6 +++++ libs/FirmwareKit/source/FirmwareKit.cpp | 24 ++++++++++++++++++++ libs/FirmwareKit/tests/FirmwareKit_test.cpp | 11 +++++++++ tests/unit/mocks/mocks/leka/FirmwareUpdate.h | 1 + 5 files changed, 43 insertions(+) diff --git a/include/interface/drivers/FirmwareUpdate.h b/include/interface/drivers/FirmwareUpdate.h index 6ac4fc194f..95078a8adc 100644 --- a/include/interface/drivers/FirmwareUpdate.h +++ b/include/interface/drivers/FirmwareUpdate.h @@ -13,6 +13,7 @@ class FirmwareUpdate public: virtual ~FirmwareUpdate() = default; + virtual auto getCurrentVersion() -> FirmwareVersion = 0; virtual auto loadUpdate(const FirmwareVersion &version) -> bool = 0; }; diff --git a/libs/FirmwareKit/include/FirmwareKit.h b/libs/FirmwareKit/include/FirmwareKit.h index 1bfc3a0df1..b0619fa1ac 100644 --- a/libs/FirmwareKit/include/FirmwareKit.h +++ b/libs/FirmwareKit/include/FirmwareKit.h @@ -15,6 +15,8 @@ namespace leka { class FirmwareKit : public interface::FirmwareUpdate { + static constexpr auto os_version_path = "fs/sys/os-version"; + public: explicit FirmwareKit(interface::FlashMemory &flash, const char *format = "/fs/usr/os/LekaOS-%i.%i.%i.bin") : _flash(flash), _path_format(format) @@ -22,9 +24,13 @@ class FirmwareKit : public interface::FirmwareUpdate // nothing do to } + auto getCurrentVersion() -> leka::FirmwareVersion final; + auto loadUpdate(const leka::FirmwareVersion &version) -> bool final; private: + auto getCurrentVersionFromFile() -> leka::FirmwareVersion; + auto loadUpdate(const char *path) -> bool; interface::FlashMemory &_flash; diff --git a/libs/FirmwareKit/source/FirmwareKit.cpp b/libs/FirmwareKit/source/FirmwareKit.cpp index ce896230a9..31ffdc266e 100644 --- a/libs/FirmwareKit/source/FirmwareKit.cpp +++ b/libs/FirmwareKit/source/FirmwareKit.cpp @@ -4,8 +4,32 @@ #include "FirmwareKit.h" +#include "semver/semver.hpp" + using namespace leka; +auto FirmwareKit::getCurrentVersion() -> FirmwareVersion +{ + return getCurrentVersionFromFile(); +} + +auto FirmwareKit::getCurrentVersionFromFile() -> FirmwareVersion +{ + auto file_content = std::array {}; + + if (auto is_not_open = !_file.open(os_version_path); is_not_open) { + return FirmwareVersion {.major = 1, .minor = 0, .revision = 0}; + } + + _file.read(file_content); + _file.close(); + + std::replace(std::begin(file_content), std::end(file_content), '\n', '\0'); + auto semversion = semver::version {file_content.data()}; + + return FirmwareVersion {.major = semversion.major, .minor = semversion.minor, .revision = semversion.patch}; +} + auto FirmwareKit::loadUpdate(const FirmwareVersion &version) -> bool { auto path = std::array {}; diff --git a/libs/FirmwareKit/tests/FirmwareKit_test.cpp b/libs/FirmwareKit/tests/FirmwareKit_test.cpp index 0cd3ec3ee7..365c9d3bd4 100644 --- a/libs/FirmwareKit/tests/FirmwareKit_test.cpp +++ b/libs/FirmwareKit/tests/FirmwareKit_test.cpp @@ -32,6 +32,8 @@ class FirmwareKitTest : public ::testing::Test mock::FlashMemory mock_flash; FirmwareKit firmwarekit; + FirmwareVersion default_current_version = FirmwareVersion {1, 0, 0}; + std::array content = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66}; // "abcdef" }; @@ -51,6 +53,15 @@ TEST_F(FirmwareKitTest, instantiation) ASSERT_NE(&firmwarekit, nullptr); } +TEST_F(FirmwareKitTest, getCurrentVersion) +{ + auto actual_version = firmwarekit.getCurrentVersion(); + + EXPECT_EQ(actual_version.major, default_current_version.major); + EXPECT_EQ(actual_version.minor, default_current_version.minor); + EXPECT_EQ(actual_version.revision, default_current_version.revision); +} + TEST_F(FirmwareKitTest, loadUpdate) { auto version = FirmwareVersion {.major = 1, .minor = 2, .revision = 3}; diff --git a/tests/unit/mocks/mocks/leka/FirmwareUpdate.h b/tests/unit/mocks/mocks/leka/FirmwareUpdate.h index afd8c66bc8..ca8e68a6a8 100644 --- a/tests/unit/mocks/mocks/leka/FirmwareUpdate.h +++ b/tests/unit/mocks/mocks/leka/FirmwareUpdate.h @@ -11,6 +11,7 @@ namespace leka::mock { class FirmwareUpdate : public interface::FirmwareUpdate { public: + MOCK_METHOD(FirmwareVersion, getCurrentVersion, (), (override)); MOCK_METHOD(bool, loadUpdate, (const FirmwareVersion &), (override)); }; From f9a920a39240bc557584b0e99de2af29689a5e4e Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Mon, 18 Jul 2022 17:39:40 +0200 Subject: [PATCH 074/130] :children_crossing: (rc): Get current OS version --- libs/RobotKit/include/RobotController.h | 2 +- libs/RobotKit/tests/RobotController_test.h | 1 + .../tests/RobotController_test_initializeComponents.cpp | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/RobotKit/include/RobotController.h b/libs/RobotKit/include/RobotController.h index e905fd3f59..29c696c039 100644 --- a/libs/RobotKit/include/RobotController.h +++ b/libs/RobotKit/include/RobotController.h @@ -245,7 +245,7 @@ class RobotController : public interface::RobotController auto &_serial_number = _serialnumberkit.getSerialNumber(); _service_device_information.setSerialNumber(_serial_number); - auto _os_version = FirmwareVersion {.major = 1, .minor = 1, .revision = 0}; + auto _os_version = _firmware_update.getCurrentVersion(); _service_device_information.setOSVersion(_os_version); auto advertising_data = _ble.getAdvertisingData(); diff --git a/libs/RobotKit/tests/RobotController_test.h b/libs/RobotKit/tests/RobotController_test.h index 6377a723d7..f92f5d14cd 100644 --- a/libs/RobotKit/tests/RobotController_test.h +++ b/libs/RobotKit/tests/RobotController_test.h @@ -136,6 +136,7 @@ class RobotControllerTest : public testing::Test EXPECT_CALL(mock_mcu, getID).Times(1); EXPECT_CALL(mbed_mock_gatt, write(_, _, _, _)).Times(1); + EXPECT_CALL(firmware_update, getCurrentVersion).Times(1); EXPECT_CALL(mbed_mock_gatt, write(_, _, _, _)).Times(1); Sequence set_serial_number_as_ble_device_name; diff --git a/libs/RobotKit/tests/RobotController_test_initializeComponents.cpp b/libs/RobotKit/tests/RobotController_test_initializeComponents.cpp index 2a16a592a0..c94f874492 100644 --- a/libs/RobotKit/tests/RobotController_test_initializeComponents.cpp +++ b/libs/RobotKit/tests/RobotController_test_initializeComponents.cpp @@ -22,6 +22,7 @@ TEST_F(RobotControllerTest, initializeComponents) // TODO: Specify which BLE service and what is expected if necessary EXPECT_CALL(mbed_mock_gatt, write(_, _, _, _)).Times(1); + EXPECT_CALL(firmware_update, getCurrentVersion).Times(1); EXPECT_CALL(mbed_mock_gatt, write(_, _, _, _)).Times(1); Sequence set_serial_number_as_ble_device_name; From 0fa6d5f58a7917aa89fdf4712246bfeed577a159 Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Wed, 20 Jul 2022 19:57:57 +0200 Subject: [PATCH 075/130] :hammer: (tests): Exclude include/semver from gcovr/lcov coverage --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 6d782f85bd..c5a649eeba 100644 --- a/Makefile +++ b/Makefile @@ -56,8 +56,8 @@ CMAKE_TOOLS_CONFIG_DIR := $(CMAKE_TOOLS_BUILD_DIR)/cmake_config # MARK: - Coverage exclusions # -EXCLUDE_FROM_GCOVR_COVERAGE = -e '.*Xcode.*' -e '.*_build.*' -e '.*extern.*' -e '.*tests/unit.*' -e '.*tests/mocks.*' -e '.*_test.*\..*' -e '.*include/boost.*' -e '.*include/interface.*' -e '.*CoreSTM32Hal.*' -EXCLUDE_FROM_LCOV_COVERAGE = '*Xcode*' '*_build*' '*extern*' '*tests/unit*' '*tests/mocks*' '*_test*.*' '*include/boost*' '*include/interface*' '*CoreSTM32Hal*' +EXCLUDE_FROM_GCOVR_COVERAGE = -e '.*Xcode.*' -e '.*_build.*' -e '.*extern.*' -e '.*tests/unit.*' -e '.*tests/mocks.*' -e '.*_test.*\..*' -e '.*include/boost.*' -e '.*include/semver.*' -e '.*include/interface.*' -e '.*CoreSTM32Hal.*' +EXCLUDE_FROM_LCOV_COVERAGE = '*Xcode*' '*_build*' '*extern*' '*tests/unit*' '*tests/mocks*' '*_test*.*' '*include/boost*' '*include/semver*' '*include/interface*' '*CoreSTM32Hal*' # # MARK: - .bin path From b762724b0acdb864d960118593656664cf002b6b Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Thu, 8 Sep 2022 15:23:23 +0200 Subject: [PATCH 076/130] :recycle: (FirmwareKit): Make FirmwareKit independent of path, format - add config to define path and format - changed the ctor to accept config (use DI) - define default config - test case where os version is not available - clean up tests with global variables --- app/bootloader/main.cpp | 2 +- app/os/main.cpp | 2 +- libs/FirmwareKit/include/FirmwareKit.h | 15 ++++-- libs/FirmwareKit/source/FirmwareKit.cpp | 5 +- libs/FirmwareKit/tests/FirmwareKit_test.cpp | 60 ++++++++++++++------- spikes/lk_update_process_app_base/main.cpp | 2 +- 6 files changed, 56 insertions(+), 30 deletions(-) diff --git a/app/bootloader/main.cpp b/app/bootloader/main.cpp index 6862f041a4..be75c9c0d3 100644 --- a/app/bootloader/main.cpp +++ b/app/bootloader/main.cpp @@ -102,7 +102,7 @@ namespace factory_reset { } // namespace internal - auto firmwarekit = FirmwareKit(internal::flash); + auto firmwarekit = FirmwareKit(internal::flash, FirmwareKit::DEFAULT_CONFIG); void initializeExternalFlash() { diff --git a/app/os/main.cpp b/app/os/main.cpp index 990c96a6de..db615bc568 100644 --- a/app/os/main.cpp +++ b/app/os/main.cpp @@ -284,7 +284,7 @@ namespace firmware { } // namespace internal - auto kit = FirmwareKit(internal::flash); + auto kit = FirmwareKit(internal::flash, FirmwareKit::DEFAULT_CONFIG); void initializeFlash() { diff --git a/libs/FirmwareKit/include/FirmwareKit.h b/libs/FirmwareKit/include/FirmwareKit.h index b0619fa1ac..2efda4f05f 100644 --- a/libs/FirmwareKit/include/FirmwareKit.h +++ b/libs/FirmwareKit/include/FirmwareKit.h @@ -15,11 +15,16 @@ namespace leka { class FirmwareKit : public interface::FirmwareUpdate { - static constexpr auto os_version_path = "fs/sys/os-version"; - public: - explicit FirmwareKit(interface::FlashMemory &flash, const char *format = "/fs/usr/os/LekaOS-%i.%i.%i.bin") - : _flash(flash), _path_format(format) + struct Config { + const char *os_version_path; + const char *bin_path_format; + }; + + static constexpr auto DEFAULT_CONFIG = + Config {.os_version_path = "fs/sys/os-version", .bin_path_format = "/fs/usr/os/LekaOS-%i.%i.%i.bin"}; + + explicit FirmwareKit(interface::FlashMemory &flash, Config config) : _flash(flash), _config(config) { // nothing do to } @@ -36,7 +41,7 @@ class FirmwareKit : public interface::FirmwareUpdate interface::FlashMemory &_flash; FileManagerKit::File _file {}; - const char *_path_format; + const Config _config; }; } // namespace leka diff --git a/libs/FirmwareKit/source/FirmwareKit.cpp b/libs/FirmwareKit/source/FirmwareKit.cpp index 31ffdc266e..ad16ddeab7 100644 --- a/libs/FirmwareKit/source/FirmwareKit.cpp +++ b/libs/FirmwareKit/source/FirmwareKit.cpp @@ -17,7 +17,7 @@ auto FirmwareKit::getCurrentVersionFromFile() -> FirmwareVersion { auto file_content = std::array {}; - if (auto is_not_open = !_file.open(os_version_path); is_not_open) { + if (auto is_not_open = !_file.open(_config.os_version_path); is_not_open) { return FirmwareVersion {.major = 1, .minor = 0, .revision = 0}; } @@ -25,6 +25,7 @@ auto FirmwareKit::getCurrentVersionFromFile() -> FirmwareVersion _file.close(); std::replace(std::begin(file_content), std::end(file_content), '\n', '\0'); + auto semversion = semver::version {file_content.data()}; return FirmwareVersion {.major = semversion.major, .minor = semversion.minor, .revision = semversion.patch}; @@ -33,7 +34,7 @@ auto FirmwareKit::getCurrentVersionFromFile() -> FirmwareVersion auto FirmwareKit::loadUpdate(const FirmwareVersion &version) -> bool { auto path = std::array {}; - snprintf(path.data(), std::size(path), _path_format, version.major, version.minor, version.revision); + snprintf(path.data(), std::size(path), _config.bin_path_format, version.major, version.minor, version.revision); return loadUpdate(path.data()); } diff --git a/libs/FirmwareKit/tests/FirmwareKit_test.cpp b/libs/FirmwareKit/tests/FirmwareKit_test.cpp index 365c9d3bd4..56d17ed3b1 100644 --- a/libs/FirmwareKit/tests/FirmwareKit_test.cpp +++ b/libs/FirmwareKit/tests/FirmwareKit_test.cpp @@ -19,22 +19,35 @@ using ::testing::Return; class FirmwareKitTest : public ::testing::Test { protected: - FirmwareKitTest() : firmwarekit(mock_flash, "/tmp/update-v%i.%i.%i") {} + FirmwareKitTest() = default; void SetUp() override { - std::ofstream update {"/tmp/update-v1.2.3", std::ios::binary}; - update.write(content.data(), std::size(content)); - update.close(); + std::ofstream osv_stream {config.os_version_path, std::ios::binary}; + osv_stream << current_version_str; + osv_stream.close(); + + std::ofstream update_stream {bin_update_path.c_str(), std::ios::binary}; + for (const auto &val: bin_update_content) { + update_stream << val; + } + update_stream.close(); } - void TearDown() override { std::remove("/tmp/update-v1.2.3"); } + void TearDown() override { std::filesystem::remove(bin_update_path.c_str()); } - mock::FlashMemory mock_flash; - FirmwareKit firmwarekit; + std::string bin_update_path = "/tmp/update-v2.0.0.bin"; + std::array bin_update_content = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66}; // "abcdef" - FirmwareVersion default_current_version = FirmwareVersion {1, 0, 0}; + FirmwareVersion default_version = FirmwareVersion {1, 0, 0}; + FirmwareVersion current_version = FirmwareVersion {1, 2, 3}; + FirmwareVersion update_version = FirmwareVersion {2, 0, 0}; - std::array content = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66}; // "abcdef" + std::string current_version_str = "1.2.3"; + + mock::FlashMemory mock_flash {}; + FirmwareKit::Config config = {.os_version_path = "/tmp/os-version", .bin_path_format = "/tmp/update-v%i.%i.%i.bin"}; + + FirmwareKit firmwarekit = FirmwareKit {mock_flash, config}; }; MATCHER_P2(compareArray, expected_array, useless_var, "") @@ -57,34 +70,41 @@ TEST_F(FirmwareKitTest, getCurrentVersion) { auto actual_version = firmwarekit.getCurrentVersion(); - EXPECT_EQ(actual_version.major, default_current_version.major); - EXPECT_EQ(actual_version.minor, default_current_version.minor); - EXPECT_EQ(actual_version.revision, default_current_version.revision); + EXPECT_EQ(actual_version.major, current_version.major); + EXPECT_EQ(actual_version.minor, current_version.minor); + EXPECT_EQ(actual_version.revision, current_version.revision); } -TEST_F(FirmwareKitTest, loadUpdate) +TEST_F(FirmwareKitTest, getCurrentVersionFileNotFound) { - auto version = FirmwareVersion {.major = 1, .minor = 2, .revision = 3}; + std::filesystem::remove(config.os_version_path); + + auto actual_version = firmwarekit.getCurrentVersion(); + EXPECT_EQ(actual_version.major, default_version.major); + EXPECT_EQ(actual_version.minor, default_version.minor); + EXPECT_EQ(actual_version.revision, default_version.revision); +} + +TEST_F(FirmwareKitTest, loadUpdate) +{ { InSequence seq; EXPECT_CALL(mock_flash, erase).Times(1); - EXPECT_CALL(mock_flash, write(_, compareArray(content, _), std::size(content))).Times(1); + EXPECT_CALL(mock_flash, write(_, compareArray(bin_update_content, _), std::size(bin_update_content))).Times(1); } - auto did_load_firmware = firmwarekit.loadUpdate(version); + auto did_load_firmware = firmwarekit.loadUpdate(update_version); ASSERT_TRUE(did_load_firmware); } TEST_F(FirmwareKitTest, loadUpdateFileNotFound) { - auto version = FirmwareVersion {.major = 1, .minor = 2, .revision = 3}; - - std::remove("/tmp/update-v1.2.3"); + std::filesystem::remove(bin_update_path.c_str()); - auto did_load_firmware = firmwarekit.loadUpdate(version); + auto did_load_firmware = firmwarekit.loadUpdate(update_version); ASSERT_FALSE(did_load_firmware); } diff --git a/spikes/lk_update_process_app_base/main.cpp b/spikes/lk_update_process_app_base/main.cpp index c8693f89e6..3e42dbda41 100644 --- a/spikes/lk_update_process_app_base/main.cpp +++ b/spikes/lk_update_process_app_base/main.cpp @@ -28,7 +28,7 @@ FATFileSystem fatfs("fs"); auto coreqspi = CoreQSPI(); auto coremanageris25lp = CoreFlashManagerIS25LP016D(coreqspi); auto coreis25lp = CoreFlashIS25LP016D(coreqspi, coremanageris25lp); -auto firmwarekit = FirmwareKit(coreis25lp); +auto firmwarekit = FirmwareKit(coreis25lp, FirmwareKit::DEFAULT_CONFIG); auto get_secondary_bd() -> mbed::BlockDevice * { From 272fc5af0018b4d4b1f5c496e4a4e9ff925eb2b7 Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Mon, 12 Sep 2022 18:35:27 +0200 Subject: [PATCH 077/130] :bento: (spike): Update paths of images and videos in lk_lcd --- spikes/lk_lcd/Images.h | 160 +---------------------------------------- spikes/lk_lcd/Videos.h | 30 ++------ 2 files changed, 7 insertions(+), 183 deletions(-) diff --git a/spikes/lk_lcd/Images.h b/spikes/lk_lcd/Images.h index f0a90b1b20..5b2e1965dc 100644 --- a/spikes/lk_lcd/Images.h +++ b/spikes/lk_lcd/Images.h @@ -5,161 +5,7 @@ #include auto images = std::to_array({ - "/fs/images/activity-color_quest.jpg", - "/fs/images/activity-colors_and_sounds.jpg", - "/fs/images/activity-dance_freeze.jpg", - "/fs/images/activity-hide_and_seek.jpg", - "/fs/images/activity-magic_objects.jpg", - "/fs/images/activity-music_colored_board.jpg", - "/fs/images/activity-music_quest.jpg", - "/fs/images/activity-super_simon.jpg", - "/fs/images/animal-fish-black.jpg", - "/fs/images/animal-fish-blue.jpg", - "/fs/images/animal-fish-green.jpg", - "/fs/images/animal-fish-red.jpg", - "/fs/images/animal-fish-white.jpg", - "/fs/images/animal-fish-yellow.jpg", - "/fs/images/battery_0.jpg", - "/fs/images/battery_green_1.jpg", - "/fs/images/battery_green_2.jpg", - "/fs/images/battery_green_3.jpg", - "/fs/images/battery_green_4.jpg", - "/fs/images/battery_orange_1.jpg", - "/fs/images/battery_orange_2.jpg", - "/fs/images/battery_red.jpg", - "/fs/images/battery_yellow_1.jpg", - "/fs/images/battery_yellow_2.jpg", - "/fs/images/battery_yellow_3.jpg", - "/fs/images/clothes-socks_pair-black.jpg", - "/fs/images/clothes-socks_pair-blue.jpg", - "/fs/images/clothes-socks_pair-green.jpg", - "/fs/images/clothes-socks_pair-red.jpg", - "/fs/images/clothes-socks_pair-white.jpg", - "/fs/images/clothes-socks_pair-yellow.jpg", - "/fs/images/clothes-tshirt-black.jpg", - "/fs/images/clothes-tshirt-blue.jpg", - "/fs/images/clothes-tshirt-green.jpg", - "/fs/images/clothes-tshirt-red.jpg", - "/fs/images/clothes-tshirt-white.jpg", - "/fs/images/clothes-tshirt-yellow.jpg", - "/fs/images/color-black.jpg", - "/fs/images/color-blue.jpg", - "/fs/images/color-green.jpg", - "/fs/images/color-red.jpg", - "/fs/images/color-white.jpg", - "/fs/images/color-yellow.jpg", - "/fs/images/demo-main-menu.jpg", - "/fs/images/emotion-anger-child.jpg", - "/fs/images/emotion-anger-leka.jpg", - "/fs/images/emotion-disgust-child.jpg", - "/fs/images/emotion-disgust-leka.jpg", - "/fs/images/emotion-fear-child.jpg", - "/fs/images/emotion-fear-leka.jpg", - "/fs/images/emotion-happiness-child.jpg", - "/fs/images/emotion-happiness-leka_1.jpg", - "/fs/images/emotion-happiness-leka.jpg", - "/fs/images/emotion-sadness-child.jpg", - "/fs/images/emotion-sadness-leka.jpg", - "/fs/images/loading.jpg", - "/fs/images/loading2.jpg", - "/fs/images/logo-leka-apf-1.jpg", - "/fs/images/logo.jpg", - "/fs/images/low_battery.jpg", - "/fs/images/number-0_zero.jpg", - "/fs/images/number-1_one.jpg", - "/fs/images/number-2_two.jpg", - "/fs/images/number-3_three.jpg", - "/fs/images/number-4_four.jpg", - "/fs/images/number-5_five.jpg", - "/fs/images/number-6_six.jpg", - "/fs/images/number-7_seven.jpg", - "/fs/images/number-8_eight.jpg", - "/fs/images/number-9_nine.jpg", - "/fs/images/number-10_ten.jpg", - "/fs/images/object-balloon-black.jpg", - "/fs/images/object-balloon-blue.jpg", - "/fs/images/object-balloon-green.jpg", - "/fs/images/object-balloon-red.jpg", - "/fs/images/object-balloon-white.jpg", - "/fs/images/object-balloon-yellow.jpg", - "/fs/images/object-beach_bucket-black.jpg", - "/fs/images/object-beach_bucket-blue.jpg", - "/fs/images/object-beach_bucket-green.jpg", - "/fs/images/object-beach_bucket-red.jpg", - "/fs/images/object-beach_bucket-white.jpg", - "/fs/images/object-beach_bucket-yellow.jpg", - "/fs/images/object-house-black.jpg", - "/fs/images/object-house-blue.jpg", - "/fs/images/object-house-green.jpg", - "/fs/images/object-house-red.jpg", - "/fs/images/object-house-white.jpg", - "/fs/images/object-house-yellow.jpg", - "/fs/images/plant-flower-5_petals-black.jpg", - "/fs/images/plant-flower-5_petals-blue.jpg", - "/fs/images/plant-flower-5_petals-green.jpg", - "/fs/images/plant-flower-5_petals-red.jpg", - "/fs/images/plant-flower-5_petals-white.jpg", - "/fs/images/plant-flower-5_petals-yellow.jpg", - "/fs/images/reinforcer-1-green-spin.jpg", - "/fs/images/reinforcer-2-violet_green_blink-spin.jpg", - "/fs/images/reinforcer-3-fire-static.jpg", - "/fs/images/reinforcer-4-glitters-static.jpg", - "/fs/images/reinforcer-5-rainbow-static.jpg", - "/fs/images/remote-colored_arrows.jpg", - "/fs/images/remote-standard.jpg", - "/fs/images/robot-emotion-affraid.jpg", - "/fs/images/robot-emotion-angry.jpg", - "/fs/images/robot-emotion-disgusted.jpg", - "/fs/images/robot-emotion-happy.jpg", - "/fs/images/robot-emotion-neutral.jpg", - "/fs/images/robot-emotion-sad_tears.jpg", - "/fs/images/robot-emotion-sad.jpg", - "/fs/images/robot-emotion-tired_tears.jpg", - "/fs/images/robot-emotion-tired.jpg", - "/fs/images/shape-circle-dotted_line-blue.jpg", - "/fs/images/shape-circle-dotted_line-green.jpg", - "/fs/images/shape-circle-dotted_line-red.jpg", - "/fs/images/shape-circle-dotted_line-yellow.jpg", - "/fs/images/shape-circle-dotted_line.jpg", - "/fs/images/shape-circle-filled-blue.jpg", - "/fs/images/shape-circle-filled-green.jpg", - "/fs/images/shape-circle-filled-red.jpg", - "/fs/images/shape-circle-filled-yellow.jpg", - "/fs/images/shape-circle-plain_line.jpg", - "/fs/images/shape-square-dotted_line-blue.jpg", - "/fs/images/shape-square-dotted_line-green.jpg", - "/fs/images/shape-square-dotted_line-red.jpg", - "/fs/images/shape-square-dotted_line-yellow.jpg", - "/fs/images/shape-square-dotted_line.jpg", - "/fs/images/shape-square-filled-blue.jpg", - "/fs/images/shape-square-filled-green.jpg", - "/fs/images/shape-square-filled-red.jpg", - "/fs/images/shape-square-filled-yellow.jpg", - "/fs/images/shape-square-plain_line.jpg", - "/fs/images/shape-star-dotted_line-blue.jpg", - "/fs/images/shape-star-dotted_line-green.jpg", - "/fs/images/shape-star-dotted_line-red.jpg", - "/fs/images/shape-star-dotted_line-yellow.jpg", - "/fs/images/shape-star-dotted_line.jpg", - "/fs/images/shape-star-filled-blue.jpg", - "/fs/images/shape-star-filled-green.jpg", - "/fs/images/shape-star-filled-red.jpg", - "/fs/images/shape-star-filled-yellow.jpg", - "/fs/images/shape-star-plain_line.jpg", - "/fs/images/shape-triangle-dotted_line-blue.jpg", - "/fs/images/shape-triangle-dotted_line-green.jpg", - "/fs/images/shape-triangle-dotted_line-red.jpg", - "/fs/images/shape-triangle-dotted_line-yellow.jpg", - "/fs/images/shape-triangle-dotted_line.jpg", - "/fs/images/shape-triangle-filled-blue.jpg", - "/fs/images/shape-triangle-filled-green.jpg", - "/fs/images/shape-triangle-filled-red.jpg", - "/fs/images/shape-triangle-filled-yellow.jpg", - "/fs/images/shape-triangle-plain_line.jpg", - "/fs/images/vehicule-aerial-hot_air_balloon-black.jpg", - "/fs/images/vehicule-aerial-hot_air_balloon-blue.jpg", - "/fs/images/vehicule-aerial-hot_air_balloon-green.jpg", - "/fs/images/vehicule-aerial-hot_air_balloon-red.jpg", - "/fs/images/vehicule-aerial-hot_air_balloon-white.jpg", - "/fs/images/vehicule-aerial-hot_air_balloon-yellow.jpg", + "/fs/home/img/system/robot-misc-splash_screen-large-400.jpg", + "/fs/home/img/system/robot-misc-missing_resource.jpg", + "/fs/home/img/id/00D2.jpg", }); diff --git a/spikes/lk_lcd/Videos.h b/spikes/lk_lcd/Videos.h index 9915ecd9ec..4e82e7ea19 100644 --- a/spikes/lk_lcd/Videos.h +++ b/spikes/lk_lcd/Videos.h @@ -5,30 +5,8 @@ #include auto videos = std::to_array({ - "/fs/videos/2022_02_14-animation-face-action-waking-up-without-eyebrows.avi", - "/fs/videos/2022_01_07-animation-face-state-amazed-front-trembling-mouth_without_eyebrows.avi", - "/fs/videos/2022_01_17-animation-face-state-yawning-sleeping_without_eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-action-bubbles-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-action-fly-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-action-singing-color-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-action-sneezing-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-action-wink-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-action-yawning-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-state-affraid-blue-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-state-affraid-trembling-eyes-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-state-affraid-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-state-amazed-up-trembling-mouth-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-state-angry-short-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-state-angry-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-state-disgusted-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-state-happy-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-state-sad-cry-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-state-sad-normal-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-state-sick-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-state-under-water-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-state-waiting-looking-left-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-state-waiting-looking-right-top-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-state-waiting-looking-right-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-state-waiting-looking-top-right-to-left-without-eyebrows.avi", - "/fs/videos/2022_02_14-animation-face-state-waiting-wink-without-eyebrows.avi", + "/fs/home/vid/actions/robot-animation-action-yawning-no_eyebrows.avi", + "/fs/home/vid/emotions/robot-emotion-sad-no_tears-no_eyebrows", + "/fs/home/vid/states/robot-state-looking_center_right-no_eyebrows.avi", + "/fs/home/vid/system/robot-system-ble_connection-wink-no_eyebrows.avi", }); From 6415baf558f8ea8a31323afb4a3e53f34f52ee03 Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Mon, 12 Sep 2022 18:14:17 +0200 Subject: [PATCH 078/130] :bug: (video): Decode first frame of video before getting its properties --- drivers/CoreVideo/source/CoreVideo.cpp | 8 +++++--- drivers/CoreVideo/tests/CoreVideo_test.cpp | 13 +++++++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/CoreVideo/source/CoreVideo.cpp b/drivers/CoreVideo/source/CoreVideo.cpp index 6a09c6c28d..83c0bb38b1 100644 --- a/drivers/CoreVideo/source/CoreVideo.cpp +++ b/drivers/CoreVideo/source/CoreVideo.cpp @@ -91,12 +91,14 @@ void CoreVideo::displayImage(interface::File &file, JPEGImageProperties *image_p void CoreVideo::setVideo(interface::File &file) { - _image_properties = _corejpeg.getImageProperties(); - - file.seek(0, SEEK_SET); _frame_index = 0; _image_size = 0; _is_last_frame = false; + + _frame_index = _corejpeg.findSOIMarker(file, _frame_index); + file.seek(_frame_index, SEEK_SET); + _corejpeg.decodeImage(file); + _image_properties = _corejpeg.getImageProperties(); } void CoreVideo::displayNextFrameVideo(interface::File &file) diff --git a/drivers/CoreVideo/tests/CoreVideo_test.cpp b/drivers/CoreVideo/tests/CoreVideo_test.cpp index 37f1aa826a..55fffa9322 100644 --- a/drivers/CoreVideo/tests/CoreVideo_test.cpp +++ b/drivers/CoreVideo/tests/CoreVideo_test.cpp @@ -218,8 +218,17 @@ TEST_F(CoreVideoTest, displayTextWithColor) TEST_F(CoreVideoTest, setVideo) { - EXPECT_CALL(filemock, seek(0, SEEK_SET)); - EXPECT_CALL(jpegmock, getImageProperties); + const auto start_of_file_index = 0; + const auto any_frame_index = 5668; + + { + InSequence seq; + + EXPECT_CALL(jpegmock, findSOIMarker(_, start_of_file_index)).WillOnce(Return(any_frame_index)); + EXPECT_CALL(filemock, seek(any_frame_index, SEEK_SET)); + EXPECT_CALL(jpegmock, decodeImage).Times(1); + EXPECT_CALL(jpegmock, getImageProperties); + } corevideo.setVideo(filemock); From c5c563b269fba12e00cd64d8f922703f86221433 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 14 Sep 2022 17:42:38 +0200 Subject: [PATCH 079/130] :sparkles: (rgb): Add Orange & Purple --- libs/ColorKit/include/RGB.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libs/ColorKit/include/RGB.h b/libs/ColorKit/include/RGB.h index 70d8730d38..57c2a02fe9 100644 --- a/libs/ColorKit/include/RGB.h +++ b/libs/ColorKit/include/RGB.h @@ -41,6 +41,8 @@ struct RGB { static const RGB yellow; static const RGB cyan; static const RGB magenta; + static const RGB purple; + static const RGB orange; }; constexpr RGB RGB::white {0xFF, 0xFF, 0xFF}; @@ -54,4 +56,7 @@ constexpr RGB RGB::yellow {0xFF, 0xFF, 0x00}; constexpr RGB RGB::cyan {0x00, 0xFF, 0xFF}; constexpr RGB RGB::magenta {0xFF, 0x00, 0xFF}; +constexpr RGB RGB::purple {0x14, 0x00, 0x50}; +constexpr RGB RGB::orange {0xFF, 0x80, 0x00}; + } // namespace leka From d3e763b8a93da755c4018fa7083163a3611f5119 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Tue, 13 Sep 2022 14:44:17 +0200 Subject: [PATCH 080/130] :sparkles: (ActivityKit): Add Color answer data model --- libs/ActivityKit/CMakeLists.txt | 1 + .../include/activities/include/Color.h | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 libs/ActivityKit/include/activities/include/Color.h diff --git a/libs/ActivityKit/CMakeLists.txt b/libs/ActivityKit/CMakeLists.txt index b639d06fc3..3f9b199581 100644 --- a/libs/ActivityKit/CMakeLists.txt +++ b/libs/ActivityKit/CMakeLists.txt @@ -8,6 +8,7 @@ target_include_directories(ActivityKit PUBLIC include include/activities + include/activities/include ) target_sources(ActivityKit diff --git a/libs/ActivityKit/include/activities/include/Color.h b/libs/ActivityKit/include/activities/include/Color.h new file mode 100644 index 0000000000..c1fc047ae6 --- /dev/null +++ b/libs/ActivityKit/include/activities/include/Color.h @@ -0,0 +1,36 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +// LCOV_EXCL_START + +#include "MagicCard.h" +#include "RGB.h" +namespace leka::activity { + +struct Color { + MagicCard card; + RGB color; + const char *id; + + static const Color purple; + static const Color indigo; + static const Color blue; + static const Color green; + static const Color yellow; + static const Color orange; + static const Color red; +}; + +inline constexpr Color Color::purple = Color {MagicCard::color_purple, RGB::purple, "0003"}; +inline constexpr Color Color::blue = Color {MagicCard::color_blue, RGB::pure_blue, "0005"}; +inline constexpr Color Color::green = Color {MagicCard::color_green, RGB::pure_green, "0006"}; +inline constexpr Color Color::yellow = Color {MagicCard::color_yellow, RGB::yellow, "0007"}; +inline constexpr Color Color::orange = Color {MagicCard::color_orange, RGB::orange, "0008"}; +inline constexpr Color Color::red = Color {MagicCard::color_red, RGB::pure_red, "0009"}; + +} // namespace leka::activity + +// LCOV_EXCL_STOP From 73a834a247040717d653150935d806171c89cc73 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Tue, 13 Sep 2022 14:45:11 +0200 Subject: [PATCH 081/130] :sparkles: (ActivityKit): Add Emotion answer data model --- .../include/activities/include/Emotion.h | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 libs/ActivityKit/include/activities/include/Emotion.h diff --git a/libs/ActivityKit/include/activities/include/Emotion.h b/libs/ActivityKit/include/activities/include/Emotion.h new file mode 100644 index 0000000000..78f55647df --- /dev/null +++ b/libs/ActivityKit/include/activities/include/Emotion.h @@ -0,0 +1,37 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +// LCOV_EXCL_START + +#include "MagicCard.h" + +namespace leka::activity { + +struct Emotion { + std::tuple cards; + const char *id; + + static const Emotion fear; + static const Emotion disgust; + static const Emotion anger; + static const Emotion joy; + static const Emotion sadness; +}; + +inline constexpr Emotion Emotion::joy = + Emotion {std::make_tuple(MagicCard::emotion_joy_child, MagicCard::emotion_joy_leka), "004A"}; +inline constexpr Emotion Emotion::sadness = + Emotion {std::make_tuple(MagicCard::emotion_sadness_child, MagicCard::emotion_sadness_leka), "004D"}; +inline constexpr Emotion Emotion::fear = + Emotion {std::make_tuple(MagicCard::emotion_fear_child, MagicCard::emotion_fear_leka), "004F"}; +inline constexpr Emotion Emotion::anger = + Emotion {std::make_tuple(MagicCard::emotion_anger_child, MagicCard::emotion_anger_leka), "0050"}; +inline constexpr Emotion Emotion::disgust = + Emotion {std::make_tuple(MagicCard::emotion_disgust_child, MagicCard::emotion_disgust_leka), "0051"}; + +} // namespace leka::activity + +// LCOV_EXCL_STOP From 36141fa99eac24190355cebe35ea8b93e5020cc0 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Tue, 13 Sep 2022 14:45:33 +0200 Subject: [PATCH 082/130] :sparkles: (ActivityKit): Add Food answer data model --- .../include/activities/include/Food.h | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 libs/ActivityKit/include/activities/include/Food.h diff --git a/libs/ActivityKit/include/activities/include/Food.h b/libs/ActivityKit/include/activities/include/Food.h new file mode 100644 index 0000000000..0ba4f9396e --- /dev/null +++ b/libs/ActivityKit/include/activities/include/Food.h @@ -0,0 +1,40 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +// LCOV_EXCL_START + +#include "MagicCard.h" + +namespace leka::activity { + +struct Food { + MagicCard card; + const char *id; + + static const Food carrot; + static const Food potato; + static const Food salad; + static const Food mushroom; + static const Food strawberry; + static const Food cherry; + static const Food apple; + static const Food banana; + static const Food grapes; +}; + +inline constexpr Food Food::carrot = Food {MagicCard::vegetable_carrot_orange, "0032"}; +inline constexpr Food Food::potato = Food {MagicCard::vegetable_potato_yellow, "0033"}; +inline constexpr Food Food::salad = Food {MagicCard::vegetable_salad_green, "0034"}; +inline constexpr Food Food::mushroom = Food {MagicCard::vegetable_mushroom_grey, "0035"}; +inline constexpr Food Food::strawberry = Food {MagicCard::fruit_strawberry_red, "0036"}; +inline constexpr Food Food::cherry = Food {MagicCard::fruit_cherry_pink, "0037"}; +inline constexpr Food Food::apple = Food {MagicCard::fruit_apple_green, "0038"}; +inline constexpr Food Food::banana = Food {MagicCard::fruit_banana_yellow, "0039"}; +inline constexpr Food Food::grapes = Food {MagicCard::fruit_grapes_black, "003A"}; + +} // namespace leka::activity + +// LCOV_EXCL_STOP From 54023ae3ea4d07a068ad3ec93a7c02f08bb09892 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Tue, 13 Sep 2022 14:45:54 +0200 Subject: [PATCH 083/130] :sparkles: (ActivityKit): Add Number answer data model --- .../include/activities/include/Number.h | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 libs/ActivityKit/include/activities/include/Number.h diff --git a/libs/ActivityKit/include/activities/include/Number.h b/libs/ActivityKit/include/activities/include/Number.h new file mode 100644 index 0000000000..2b38300f61 --- /dev/null +++ b/libs/ActivityKit/include/activities/include/Number.h @@ -0,0 +1,44 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +// LCOV_EXCL_START + +#include "MagicCard.h" + +namespace leka::activity { + +struct Number { + MagicCard card; + const char *id; + + static const Number number_0; + static const Number number_1; + static const Number number_2; + static const Number number_3; + static const Number number_4; + static const Number number_5; + static const Number number_6; + static const Number number_7; + static const Number number_8; + static const Number number_9; + static const Number number_10; +}; + +inline constexpr Number Number::number_0 = Number {MagicCard::number_0, "000A"}; +inline constexpr Number Number::number_1 = Number {MagicCard::number_1, "000B"}; +inline constexpr Number Number::number_2 = Number {MagicCard::number_2, "000C"}; +inline constexpr Number Number::number_3 = Number {MagicCard::number_3, "000D"}; +inline constexpr Number Number::number_4 = Number {MagicCard::number_4, "000E"}; +inline constexpr Number Number::number_5 = Number {MagicCard::number_5, "000F"}; +inline constexpr Number Number::number_6 = Number {MagicCard::number_6, "0010"}; +inline constexpr Number Number::number_7 = Number {MagicCard::number_7, "0011"}; +inline constexpr Number Number::number_8 = Number {MagicCard::number_8, "0012"}; +inline constexpr Number Number::number_9 = Number {MagicCard::number_9, "0013"}; +inline constexpr Number Number::number_10 = Number {MagicCard::number_10, "0014"}; + +} // namespace leka::activity + +// LCOV_EXCL_STOP From 3bcf85b89f39ad52174c5031f298e254b1d9570e Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Tue, 13 Sep 2022 14:46:24 +0200 Subject: [PATCH 084/130] :sparkles: (ActivityKit): Add Shape answer data model --- .../include/activities/include/Shape.h | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 libs/ActivityKit/include/activities/include/Shape.h diff --git a/libs/ActivityKit/include/activities/include/Shape.h b/libs/ActivityKit/include/activities/include/Shape.h new file mode 100644 index 0000000000..644c628907 --- /dev/null +++ b/libs/ActivityKit/include/activities/include/Shape.h @@ -0,0 +1,28 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +// LCOV_EXCL_START + +#include "MagicCard.h" + +namespace leka::activity { + +struct Shape { + MagicCard card; + const char *id; + + static const Shape square; + static const Shape circle; + static const Shape triangle; + static const Shape star; +}; + +inline constexpr Shape Shape::square = Shape {MagicCard::shape_square, "0015"}; +inline constexpr Shape Shape::circle = Shape {MagicCard::shape_circle, "0016"}; +inline constexpr Shape Shape::triangle = Shape {MagicCard::shape_triangle, "0017"}; +inline constexpr Shape Shape::star = Shape {MagicCard::shape_star, "0018"}; + +} // namespace leka::activity From 37276b86f3367e7b1d2a480021c8867499404fda Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 14 Sep 2022 18:02:21 +0200 Subject: [PATCH 085/130] :art: (VideoKit): Add fillWhiteBackgroundAndDisplayImage func in VideoKit --- include/interface/libs/VideoKit.h | 3 ++- libs/VideoKit/include/VideoKit.h | 1 + libs/VideoKit/source/VideoKit.cpp | 22 ++++++++++++++++++++ libs/VideoKit/tests/VideoKit_test.cpp | 28 ++++++++++++++++++++++++++ tests/unit/mocks/mocks/leka/VideoKit.h | 1 + 5 files changed, 54 insertions(+), 1 deletion(-) diff --git a/include/interface/libs/VideoKit.h b/include/interface/libs/VideoKit.h index f6c7fb655a..3c8cc9654c 100644 --- a/include/interface/libs/VideoKit.h +++ b/include/interface/libs/VideoKit.h @@ -15,7 +15,8 @@ class VideoKit virtual void initializeScreen() = 0; - virtual void displayImage(const std::filesystem::path &path) = 0; + virtual void displayImage(const std::filesystem::path &path) = 0; + virtual void fillWhiteBackgroundAndDisplayImage(const std::filesystem::path &path) = 0; virtual void playVideoOnce(const std::filesystem::path &path) = 0; virtual void playVideoOnRepeat(const std::filesystem::path &path) = 0; diff --git a/libs/VideoKit/include/VideoKit.h b/libs/VideoKit/include/VideoKit.h index c0281d8559..861fc3ee12 100644 --- a/libs/VideoKit/include/VideoKit.h +++ b/libs/VideoKit/include/VideoKit.h @@ -24,6 +24,7 @@ class VideoKit : public interface::VideoKit void initializeScreen() final; void displayImage(const std::filesystem::path &path) final; + void fillWhiteBackgroundAndDisplayImage(const std::filesystem::path &path) final; void playVideoOnce(const std::filesystem::path &path) final; void playVideoOnRepeat(const std::filesystem::path &path) final; diff --git a/libs/VideoKit/source/VideoKit.cpp b/libs/VideoKit/source/VideoKit.cpp index 51e1eb5b62..4b5987c2ff 100644 --- a/libs/VideoKit/source/VideoKit.cpp +++ b/libs/VideoKit/source/VideoKit.cpp @@ -50,6 +50,28 @@ void VideoKit::displayImage(const std::filesystem::path &path) } } +void VideoKit::fillWhiteBackgroundAndDisplayImage(const std::filesystem::path &path) +{ + const std::scoped_lock lock(mutex); + + if (path == _current_path) { + return; + } + + if (auto file = FileManagerKit::File {path}; file.is_open()) { + _event_flags.set(flags::STOP_VIDEO_FLAG); + + _current_path = path; + + rtos::ThisThread::sleep_for(100ms); + + _video.clearScreen(); + _video.displayImage(file); + + file.close(); + } +} + void VideoKit::playVideoOnce(const std::filesystem::path &path) { const std::scoped_lock lock(mutex); diff --git a/libs/VideoKit/tests/VideoKit_test.cpp b/libs/VideoKit/tests/VideoKit_test.cpp index dc1ffba6d1..d5dde3d3ff 100644 --- a/libs/VideoKit/tests/VideoKit_test.cpp +++ b/libs/VideoKit/tests/VideoKit_test.cpp @@ -68,6 +68,34 @@ TEST_F(VideoKitTest, displayImageSamePathTwice) video_kit.displayImage(temp_file_path); } +TEST_F(VideoKitTest, fillWhiteBackgroundDisplayImage) +{ + EXPECT_CALL(mock_event_flags, set(VideoKit::flags::STOP_VIDEO_FLAG)); + EXPECT_CALL(mock_corevideo, clearScreen); + EXPECT_CALL(mock_corevideo, displayImage); + + video_kit.fillWhiteBackgroundAndDisplayImage(temp_file_path); +} + +TEST_F(VideoKitTest, fillWhiteBackgroundDisplayImageFileDoesNotExist) +{ + video_kit.fillWhiteBackgroundAndDisplayImage("/unexisting/path"); +} + +TEST_F(VideoKitTest, fillWhiteBackgroundDisplayImageSamePathTwice) +{ + EXPECT_CALL(mock_event_flags, set(VideoKit::flags::STOP_VIDEO_FLAG)); + EXPECT_CALL(mock_corevideo, clearScreen).Times(1); + EXPECT_CALL(mock_corevideo, displayImage).Times(1); + + video_kit.fillWhiteBackgroundAndDisplayImage(temp_file_path); + + EXPECT_CALL(mock_corevideo, clearScreen).Times(0); + EXPECT_CALL(mock_corevideo, displayImage).Times(0); + + video_kit.fillWhiteBackgroundAndDisplayImage(temp_file_path); +} + TEST_F(VideoKitTest, playVideoOnce) { EXPECT_CALL(mock_event_flags, set(VideoKit::flags::STOP_VIDEO_FLAG)); diff --git a/tests/unit/mocks/mocks/leka/VideoKit.h b/tests/unit/mocks/mocks/leka/VideoKit.h index 2cb81365fb..3d3e692e06 100644 --- a/tests/unit/mocks/mocks/leka/VideoKit.h +++ b/tests/unit/mocks/mocks/leka/VideoKit.h @@ -15,6 +15,7 @@ class VideoKit : public interface::VideoKit MOCK_METHOD(void, initializeScreen, (), (override)); MOCK_METHOD(void, displayImage, (const std::filesystem::path &), (override)); + MOCK_METHOD(void, fillWhiteBackgroundAndDisplayImage, (const std::filesystem::path &), (override)); MOCK_METHOD(void, playVideoOnce, (const std::filesystem::path &), (override)); MOCK_METHOD(void, playVideoOnRepeat, (const std::filesystem::path &), (override)); From 24dc8c35a7341425abd37b0b296559ff910a77a2 Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Fri, 16 Sep 2022 18:33:08 +0200 Subject: [PATCH 086/130] :recycle: (serialnumber): Dispatch serial number construction using SerialNumberFormater - Remove extra '-' - Return std::span instead of std::array - Print short serial number in spike --- libs/RobotKit/include/RobotController.h | 2 +- libs/SerialNumberKit/CMakeLists.txt | 2 + .../include/SerialNumberFormater.h | 25 +++++ .../SerialNumberKit/include/SerialNumberKit.h | 20 ++-- .../source/SerialNumberFormater.cpp | 44 +++++++++ .../source/SerialNumberKit.cpp | 31 ++---- .../tests/SerialNumberFormater_test.cpp | 97 +++++++++++++++++++ .../tests/SerialNumberKit_test.cpp | 16 +-- spikes/lk_serial_number/main.cpp | 3 + 9 files changed, 205 insertions(+), 35 deletions(-) create mode 100644 libs/SerialNumberKit/include/SerialNumberFormater.h create mode 100644 libs/SerialNumberKit/source/SerialNumberFormater.cpp create mode 100644 libs/SerialNumberKit/tests/SerialNumberFormater_test.cpp diff --git a/libs/RobotKit/include/RobotController.h b/libs/RobotKit/include/RobotController.h index 29c696c039..3e4db51273 100644 --- a/libs/RobotKit/include/RobotController.h +++ b/libs/RobotKit/include/RobotController.h @@ -242,7 +242,7 @@ class RobotController : public interface::RobotController _ble.setServices(services); _ble.init(); - auto &_serial_number = _serialnumberkit.getSerialNumber(); + auto _serial_number = _serialnumberkit.getSerialNumber(); _service_device_information.setSerialNumber(_serial_number); auto _os_version = _firmware_update.getCurrentVersion(); diff --git a/libs/SerialNumberKit/CMakeLists.txt b/libs/SerialNumberKit/CMakeLists.txt index 605b920c17..e73502a637 100644 --- a/libs/SerialNumberKit/CMakeLists.txt +++ b/libs/SerialNumberKit/CMakeLists.txt @@ -12,6 +12,7 @@ target_include_directories(SerialNumberKit target_sources(SerialNumberKit PRIVATE source/SerialNumberKit.cpp + source/SerialNumberFormater.cpp ) target_link_libraries(SerialNumberKit) @@ -19,5 +20,6 @@ target_link_libraries(SerialNumberKit) if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") leka_unit_tests_sources( tests/SerialNumberKit_test.cpp + tests/SerialNumberFormater_test.cpp ) endif() diff --git a/libs/SerialNumberKit/include/SerialNumberFormater.h b/libs/SerialNumberKit/include/SerialNumberFormater.h new file mode 100644 index 0000000000..4aad43f99d --- /dev/null +++ b/libs/SerialNumberKit/include/SerialNumberFormater.h @@ -0,0 +1,25 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +namespace leka { + +class SerialNumberFormater +{ + public: + explicit SerialNumberFormater(interface::MCU &mcu) : _mcu(mcu) {} + + [[nodiscard]] auto setPrefix(std::span serial_number) const -> std::size_t; + [[nodiscard]] auto setDateOfTest(std::span serial_number, uint32_t offset) const -> std::size_t; + void setMCUID(std::span serial_number, uint32_t offset, uint8_t number_of_digits); + + private: + interface::MCU &_mcu; +}; + +} // namespace leka diff --git a/libs/SerialNumberKit/include/SerialNumberKit.h b/libs/SerialNumberKit/include/SerialNumberKit.h index 3c39f065d0..1a579a14c2 100644 --- a/libs/SerialNumberKit/include/SerialNumberKit.h +++ b/libs/SerialNumberKit/include/SerialNumberKit.h @@ -6,22 +6,28 @@ #include #include +#include + +#include "SerialNumberFormater.h" namespace leka { class SerialNumberKit { - static constexpr size_t SN_SIZE = 33; - static constexpr size_t SHORT_SN_SIZE = 16 + 1; - public: - explicit SerialNumberKit(interface::MCU &mcu) : _mcu(mcu) {} + explicit SerialNumberKit(interface::MCU &mcu) : _formater(mcu) + { + // do nothing + } - auto getSerialNumber() -> std::array &; - auto getShortSerialNumber() -> std::array &; + auto getSerialNumber() -> std::span; + auto getShortSerialNumber() -> std::span; private: - interface::MCU &_mcu; + static constexpr std::size_t SN_SIZE = 31 + 1; // SN + `\0` for const char * use + static constexpr std::size_t SHORT_SN_SIZE = 15 + 1; // ShortSN + `\0` for const char * use + + SerialNumberFormater _formater; std::array serial_number {}; std::array short_serial_number {}; diff --git a/libs/SerialNumberKit/source/SerialNumberFormater.cpp b/libs/SerialNumberKit/source/SerialNumberFormater.cpp new file mode 100644 index 0000000000..5a80d06645 --- /dev/null +++ b/libs/SerialNumberKit/source/SerialNumberFormater.cpp @@ -0,0 +1,44 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include "SerialNumberFormater.h" + +using namespace leka; + +auto SerialNumberFormater::setPrefix(std::span partial_serial_number) const -> std::size_t +{ + auto prefix = std::array {'L', 'K', '-'}; + std::copy(prefix.begin(), prefix.end(), partial_serial_number.begin()); + + return std::size(prefix); +} + +auto SerialNumberFormater::setDateOfTest(std::span partial_serial_number, uint32_t offset) const -> std::size_t +{ + auto date_of_test = std::array {}; + date_of_test.fill('X'); + + std::copy(date_of_test.begin(), date_of_test.end(), partial_serial_number.begin() + offset); + + return std::size(date_of_test); +} + +void SerialNumberFormater::setMCUID(std::span partial_serial_number, uint32_t offset, uint8_t number_of_digits) +{ + constexpr auto MAX_MCU_ID_DIGITS = 24; + constexpr auto MAX_MCU_ID_LENGTH = MAX_MCU_ID_DIGITS + sizeof('\0'); + std::array mcu_id_array {}; + + auto mcu_id = _mcu.getID(); + snprintf(mcu_id_array.begin(), std::size(mcu_id_array), "%08lX%08lX%08lX", mcu_id.front, mcu_id.middle, + mcu_id.back); + + if (number_of_digits > MAX_MCU_ID_DIGITS) { + number_of_digits = MAX_MCU_ID_DIGITS; + } + std::memcpy(partial_serial_number.data() + offset, &mcu_id_array, number_of_digits); +} diff --git a/libs/SerialNumberKit/source/SerialNumberKit.cpp b/libs/SerialNumberKit/source/SerialNumberKit.cpp index 723d784713..0901179b5d 100644 --- a/libs/SerialNumberKit/source/SerialNumberKit.cpp +++ b/libs/SerialNumberKit/source/SerialNumberKit.cpp @@ -3,38 +3,27 @@ // SPDX-License-Identifier: Apache-2.0 #include "SerialNumberKit.h" -#include -#include - -#include "CastUtils.h" using namespace leka; -auto SerialNumberKit::getSerialNumber() -> std::array & +auto SerialNumberKit::getSerialNumber() -> std::span { - auto prefix = utils::cast::from_c_string_to_uint8_t_array("LK-22xx-"); - std::copy(prefix.begin(), prefix.end(), serial_number.begin()); - - std::array mcu_id_array {}; - auto mcu_id = _mcu.getID(); - snprintf(mcu_id_array.begin(), std::size(mcu_id_array), "%08lX%08lX%08lX", mcu_id.front, mcu_id.middle, - mcu_id.back); + auto prefix_size = _formater.setPrefix(serial_number); + auto date_of_test_size = _formater.setDateOfTest(serial_number, prefix_size); + auto number_of_digits = static_cast(SN_SIZE - (prefix_size + date_of_test_size + sizeof('\0'))); - std::memcpy(serial_number.begin() + std::size(prefix) - 1, &mcu_id_array, std::size(mcu_id_array)); + _formater.setMCUID(serial_number, prefix_size + date_of_test_size, number_of_digits); return serial_number; } -auto SerialNumberKit::getShortSerialNumber() -> std::array & +auto SerialNumberKit::getShortSerialNumber() -> std::span { - auto prefix = utils::cast::from_c_string_to_uint8_t_array("LK-22xx-"); - std::copy(prefix.begin(), prefix.end(), short_serial_number.begin()); - - std::array mcu_id_array {}; - auto mcu_id = _mcu.getID(); - snprintf(mcu_id_array.begin(), std::size(mcu_id_array), "%08lX", mcu_id.front); + auto prefix_size = _formater.setPrefix(short_serial_number); + auto date_of_test_size = _formater.setDateOfTest(short_serial_number, prefix_size); + auto number_of_digits = static_cast(SHORT_SN_SIZE - (prefix_size + date_of_test_size + sizeof('\0'))); - std::memcpy(short_serial_number.begin() + std::size(prefix) - 1, &mcu_id_array, std::size(mcu_id_array)); + _formater.setMCUID(short_serial_number, prefix_size + date_of_test_size, number_of_digits); return short_serial_number; } diff --git a/libs/SerialNumberKit/tests/SerialNumberFormater_test.cpp b/libs/SerialNumberKit/tests/SerialNumberFormater_test.cpp new file mode 100644 index 0000000000..f56891295e --- /dev/null +++ b/libs/SerialNumberKit/tests/SerialNumberFormater_test.cpp @@ -0,0 +1,97 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include "SerialNumberFormater.h" +#include "gtest/gtest.h" +#include "mocks/leka/MCU.h" + +using namespace leka; + +using ::testing::Return; + +class SerialNumberFormaterTest : public ::testing::Test +{ + protected: + SerialNumberFormaterTest() = default; + + // void SetUp() override {} + // void TearDown() override {} + + mock::MCU mock_mcu; + SerialNumberFormater serial_number_formater {mock_mcu}; + + MCUID mcu_id_returned {0x2A2B2C2D, 0x3A3B3C3D, 0x4A4B4C4D}; +}; + +TEST_F(SerialNumberFormaterTest, initialization) +{ + EXPECT_NE(&serial_number_formater, nullptr); +} + +TEST_F(SerialNumberFormaterTest, setPrefix) +{ + auto expected_serial_number = std::array {"LK-"}; + auto actual_serial_number = std::array {}; + + auto actual_prefix_number_size = serial_number_formater.setPrefix(actual_serial_number); + EXPECT_EQ(actual_serial_number, expected_serial_number); + EXPECT_EQ(actual_prefix_number_size, std::size("LK-") - sizeof('\0')); +} + +TEST_F(SerialNumberFormaterTest, setDateOfTest) +{ + auto expected_serial_number = std::array {"LK-XXXX"}; + auto actual_serial_number = std::array {"LK-"}; + + auto prefix_size = std::size("LK-") - sizeof('\0'); + + auto actual_prefix_number_size = serial_number_formater.setDateOfTest(actual_serial_number, prefix_size); + EXPECT_EQ(actual_serial_number, expected_serial_number); + EXPECT_EQ(actual_prefix_number_size, std::size("XXXX") - sizeof('\0')); +} + +TEST_F(SerialNumberFormaterTest, setMCUID) +{ + auto expected_serial_number = std::array {}; + auto actual_serial_number = std::array {}; + + auto prefix_plus_date_of_test_size = std::size("LK-XXXX") - sizeof('\0'); + auto number_of_digits = 0; + + EXPECT_CALL(mock_mcu, getID).WillRepeatedly(Return(mcu_id_returned)); + + actual_serial_number = std::array {"LK-XXXX"}; + number_of_digits = 10; + expected_serial_number = std::array {"LK-XXXX2A2B2C2D3A"}; + serial_number_formater.setMCUID(actual_serial_number, prefix_plus_date_of_test_size, number_of_digits); + EXPECT_EQ(actual_serial_number, expected_serial_number); + + actual_serial_number = std::array {"LK-XXXX"}; + number_of_digits = 19; + expected_serial_number = std::array {"LK-XXXX2A2B2C2D3A3B3C3D4A4"}; + serial_number_formater.setMCUID(actual_serial_number, prefix_plus_date_of_test_size, number_of_digits); + EXPECT_EQ(actual_serial_number, expected_serial_number); + + actual_serial_number = std::array {"LK-XXXX"}; + number_of_digits = 24; + expected_serial_number = std::array {"LK-XXXX2A2B2C2D3A3B3C3D4A4B4C4D"}; + serial_number_formater.setMCUID(actual_serial_number, prefix_plus_date_of_test_size, number_of_digits); + EXPECT_EQ(actual_serial_number, expected_serial_number); +} + +TEST_F(SerialNumberFormaterTest, setMCUIDExceedingNumberOfDigits) +{ + auto expected_serial_number = std::array {"LK-XXXX2A2B2C2D3A3B3C3D4A4B4C4D"}; + auto actual_serial_number = std::array {"LK-XXXX"}; + + auto prefix_plus_date_of_test_size = std::size("LK-XXXX") - sizeof('\0'); + + EXPECT_CALL(mock_mcu, getID).WillRepeatedly(Return(mcu_id_returned)); + + serial_number_formater.setMCUID(actual_serial_number, prefix_plus_date_of_test_size, 32); + EXPECT_EQ(actual_serial_number, expected_serial_number); +} diff --git a/libs/SerialNumberKit/tests/SerialNumberKit_test.cpp b/libs/SerialNumberKit/tests/SerialNumberKit_test.cpp index 4fff347b72..76338226ba 100644 --- a/libs/SerialNumberKit/tests/SerialNumberKit_test.cpp +++ b/libs/SerialNumberKit/tests/SerialNumberKit_test.cpp @@ -3,6 +3,8 @@ // SPDX-License-Identifier: Apache-2.0 #include "SerialNumberKit.h" +#include +#include #include "CastUtils.h" #include "gtest/gtest.h" @@ -22,6 +24,8 @@ class SerialNumberKitTest : public ::testing::Test mock::MCU mock_mcu; SerialNumberKit serial_number_kit {mock_mcu}; + + MCUID mcu_id_returned {0x2A2B2C2D, 0x3A3B3C3D, 0x4A4B4C4D}; }; TEST_F(SerialNumberKitTest, initialization) @@ -31,22 +35,22 @@ TEST_F(SerialNumberKitTest, initialization) TEST_F(SerialNumberKitTest, getSerialNumber) { - auto mcu_id_returned = MCUID {0x2A2B2C2D, 0x3A3B3C3D, 0x4A4B4C4D}; - auto expected_serial_number = std::array {"LK-22xx-2A2B2C2D3A3B3C3D4A4B4C4D"}; + auto expected_serial_number = std::array {"LK-XXXX2A2B2C2D3A3B3C3D4A4B4C4D"}; EXPECT_CALL(mock_mcu, getID).WillOnce(Return(mcu_id_returned)); auto actual_serial_number = serial_number_kit.getSerialNumber(); - EXPECT_EQ(actual_serial_number, expected_serial_number); + EXPECT_TRUE(std::equal(std::begin(expected_serial_number), std::end(expected_serial_number), + std::begin(actual_serial_number))); } TEST_F(SerialNumberKitTest, getShortSerialNumber) { - auto mcu_id_returned = MCUID {0x2A2B2C2D, 0x3A3B3C3D, 0x4A4B4C4D}; - auto expected_serial_number = std::array {"LK-22xx-2A2B2C2D"}; + auto expected_serial_number = std::array {"LK-XXXX2A2B2C2D"}; EXPECT_CALL(mock_mcu, getID).WillOnce(Return(mcu_id_returned)); auto actual_serial_number = serial_number_kit.getShortSerialNumber(); - EXPECT_EQ(actual_serial_number, expected_serial_number); + EXPECT_TRUE(std::equal(std::begin(expected_serial_number), std::end(expected_serial_number), + std::begin(actual_serial_number))); } diff --git a/spikes/lk_serial_number/main.cpp b/spikes/lk_serial_number/main.cpp index 20c874ee97..1f0aa42ce0 100644 --- a/spikes/lk_serial_number/main.cpp +++ b/spikes/lk_serial_number/main.cpp @@ -37,6 +37,9 @@ auto main() -> int auto serial_number = serialnumberkit.getSerialNumber(); log_info("S/N: %s", serial_number.data()); + auto short_serial_number = serialnumberkit.getShortSerialNumber(); + log_info("S/N (short): %s", short_serial_number.data()); + 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, From e1562ff4aea08c0d3a55248d2c72d99c689afee0 Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Fri, 16 Sep 2022 18:55:38 +0200 Subject: [PATCH 087/130] :adhesive_bandage: (serialnumber): Set date_of_test in Serial Number --- app/os/main.cpp | 2 +- libs/RobotKit/tests/RobotController_test.h | 2 +- libs/SerialNumberKit/CMakeLists.txt | 4 +- .../include/SerialNumberFormater.h | 7 ++- .../SerialNumberKit/include/SerialNumberKit.h | 8 ++- .../source/SerialNumberFormater.cpp | 6 +++ .../tests/SerialNumberFormater_test.cpp | 50 ++++++++++++++----- .../tests/SerialNumberKit_test.cpp | 6 +-- spikes/lk_serial_number/main.cpp | 19 ++++++- 9 files changed, 82 insertions(+), 22 deletions(-) diff --git a/app/os/main.cpp b/app/os/main.cpp index db615bc568..e135aafc0d 100644 --- a/app/os/main.cpp +++ b/app/os/main.cpp @@ -348,7 +348,7 @@ namespace robot { auto sleep_timeout = CoreTimeout {}; auto mcu = CoreMCU {}; - auto serialnumberkit = SerialNumberKit {mcu}; + auto serialnumberkit = SerialNumberKit {mcu, SerialNumberKit::DEFAULT_CONFIG}; } // namespace internal diff --git a/libs/RobotKit/tests/RobotController_test.h b/libs/RobotKit/tests/RobotController_test.h index f92f5d14cd..8abeb151c9 100644 --- a/libs/RobotKit/tests/RobotController_test.h +++ b/libs/RobotKit/tests/RobotController_test.h @@ -70,7 +70,7 @@ class RobotControllerTest : public testing::Test mock::Battery battery {}; mock::MCU mock_mcu {}; - SerialNumberKit serialnumberkit {mock_mcu}; + SerialNumberKit serialnumberkit {mock_mcu, SerialNumberKit::DEFAULT_CONFIG}; mock::FirmwareUpdate firmware_update {}; diff --git a/libs/SerialNumberKit/CMakeLists.txt b/libs/SerialNumberKit/CMakeLists.txt index e73502a637..f9c11c2f1d 100644 --- a/libs/SerialNumberKit/CMakeLists.txt +++ b/libs/SerialNumberKit/CMakeLists.txt @@ -15,7 +15,9 @@ target_sources(SerialNumberKit source/SerialNumberFormater.cpp ) -target_link_libraries(SerialNumberKit) +target_link_libraries(SerialNumberKit + FileManagerKit +) if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") leka_unit_tests_sources( diff --git a/libs/SerialNumberKit/include/SerialNumberFormater.h b/libs/SerialNumberKit/include/SerialNumberFormater.h index 4aad43f99d..be6e1e68ea 100644 --- a/libs/SerialNumberKit/include/SerialNumberFormater.h +++ b/libs/SerialNumberKit/include/SerialNumberFormater.h @@ -12,7 +12,11 @@ namespace leka { class SerialNumberFormater { public: - explicit SerialNumberFormater(interface::MCU &mcu) : _mcu(mcu) {} + struct Config { + const char *date_of_test_path; + }; + + explicit SerialNumberFormater(interface::MCU &mcu, Config config) : _mcu(mcu), _config(config) {} [[nodiscard]] auto setPrefix(std::span serial_number) const -> std::size_t; [[nodiscard]] auto setDateOfTest(std::span serial_number, uint32_t offset) const -> std::size_t; @@ -20,6 +24,7 @@ class SerialNumberFormater private: interface::MCU &_mcu; + const Config _config; }; } // namespace leka diff --git a/libs/SerialNumberKit/include/SerialNumberKit.h b/libs/SerialNumberKit/include/SerialNumberKit.h index 1a579a14c2..27ba185f88 100644 --- a/libs/SerialNumberKit/include/SerialNumberKit.h +++ b/libs/SerialNumberKit/include/SerialNumberKit.h @@ -15,7 +15,13 @@ namespace leka { class SerialNumberKit { public: - explicit SerialNumberKit(interface::MCU &mcu) : _formater(mcu) + struct Config { + const char *date_of_test_path; + }; + static constexpr auto DEFAULT_CONFIG = Config {.date_of_test_path = "fs/sys/date_of_test"}; + + explicit SerialNumberKit(interface::MCU &mcu, Config config) + : _formater(mcu, SerialNumberFormater::Config {.date_of_test_path = config.date_of_test_path}) { // do nothing } diff --git a/libs/SerialNumberKit/source/SerialNumberFormater.cpp b/libs/SerialNumberKit/source/SerialNumberFormater.cpp index 5a80d06645..8e936f063f 100644 --- a/libs/SerialNumberKit/source/SerialNumberFormater.cpp +++ b/libs/SerialNumberKit/source/SerialNumberFormater.cpp @@ -5,6 +5,7 @@ #include #include +#include "FileManagerKit.h" #include "SerialNumberFormater.h" using namespace leka; @@ -22,6 +23,11 @@ auto SerialNumberFormater::setDateOfTest(std::span partial_serial_numbe auto date_of_test = std::array {}; date_of_test.fill('X'); + if (auto file = FileManagerKit::File {_config.date_of_test_path}; file.is_open()) { + file.read(date_of_test); + file.close(); + } + std::copy(date_of_test.begin(), date_of_test.end(), partial_serial_number.begin() + offset); return std::size(date_of_test); diff --git a/libs/SerialNumberKit/tests/SerialNumberFormater_test.cpp b/libs/SerialNumberKit/tests/SerialNumberFormater_test.cpp index f56891295e..73a5da2f25 100644 --- a/libs/SerialNumberKit/tests/SerialNumberFormater_test.cpp +++ b/libs/SerialNumberKit/tests/SerialNumberFormater_test.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 #include +#include #include #include "SerialNumberFormater.h" @@ -18,11 +19,20 @@ class SerialNumberFormaterTest : public ::testing::Test protected: SerialNumberFormaterTest() = default; - // void SetUp() override {} - // void TearDown() override {} + void SetUp() override + { + std::ofstream dot_stream {config.date_of_test_path, std::ios::binary}; + dot_stream << current_date_of_test_str; + dot_stream.close(); + } + + void TearDown() override { std::filesystem::remove(config.date_of_test_path); } + + std::string current_date_of_test_str = "220619\n"; mock::MCU mock_mcu; - SerialNumberFormater serial_number_formater {mock_mcu}; + SerialNumberFormater::Config config {.date_of_test_path = "/tmp/date_of_test"}; + SerialNumberFormater serial_number_formater {mock_mcu, config}; MCUID mcu_id_returned {0x2A2B2C2D, 0x3A3B3C3D, 0x4A4B4C4D}; }; @@ -44,6 +54,20 @@ TEST_F(SerialNumberFormaterTest, setPrefix) TEST_F(SerialNumberFormaterTest, setDateOfTest) { + auto expected_serial_number = std::array {"LK-2206"}; + auto actual_serial_number = std::array {"LK-"}; + + auto prefix_size = std::size("LK-") - sizeof('\0'); + + auto actual_prefix_number_size = serial_number_formater.setDateOfTest(actual_serial_number, prefix_size); + EXPECT_EQ(actual_serial_number, expected_serial_number); + EXPECT_EQ(actual_prefix_number_size, std::size("2206") - sizeof('\0')); +} + +TEST_F(SerialNumberFormaterTest, setDateOfTestFileNotFound) +{ + std::filesystem::remove(config.date_of_test_path); + auto expected_serial_number = std::array {"LK-XXXX"}; auto actual_serial_number = std::array {"LK-"}; @@ -59,36 +83,36 @@ TEST_F(SerialNumberFormaterTest, setMCUID) auto expected_serial_number = std::array {}; auto actual_serial_number = std::array {}; - auto prefix_plus_date_of_test_size = std::size("LK-XXXX") - sizeof('\0'); + auto prefix_plus_date_of_test_size = std::size("LK-2206") - sizeof('\0'); auto number_of_digits = 0; EXPECT_CALL(mock_mcu, getID).WillRepeatedly(Return(mcu_id_returned)); - actual_serial_number = std::array {"LK-XXXX"}; + actual_serial_number = std::array {"LK-2206"}; number_of_digits = 10; - expected_serial_number = std::array {"LK-XXXX2A2B2C2D3A"}; + expected_serial_number = std::array {"LK-22062A2B2C2D3A"}; serial_number_formater.setMCUID(actual_serial_number, prefix_plus_date_of_test_size, number_of_digits); EXPECT_EQ(actual_serial_number, expected_serial_number); - actual_serial_number = std::array {"LK-XXXX"}; + actual_serial_number = std::array {"LK-2206"}; number_of_digits = 19; - expected_serial_number = std::array {"LK-XXXX2A2B2C2D3A3B3C3D4A4"}; + expected_serial_number = std::array {"LK-22062A2B2C2D3A3B3C3D4A4"}; serial_number_formater.setMCUID(actual_serial_number, prefix_plus_date_of_test_size, number_of_digits); EXPECT_EQ(actual_serial_number, expected_serial_number); - actual_serial_number = std::array {"LK-XXXX"}; + actual_serial_number = std::array {"LK-2206"}; number_of_digits = 24; - expected_serial_number = std::array {"LK-XXXX2A2B2C2D3A3B3C3D4A4B4C4D"}; + expected_serial_number = std::array {"LK-22062A2B2C2D3A3B3C3D4A4B4C4D"}; serial_number_formater.setMCUID(actual_serial_number, prefix_plus_date_of_test_size, number_of_digits); EXPECT_EQ(actual_serial_number, expected_serial_number); } TEST_F(SerialNumberFormaterTest, setMCUIDExceedingNumberOfDigits) { - auto expected_serial_number = std::array {"LK-XXXX2A2B2C2D3A3B3C3D4A4B4C4D"}; - auto actual_serial_number = std::array {"LK-XXXX"}; + auto expected_serial_number = std::array {"LK-22062A2B2C2D3A3B3C3D4A4B4C4D"}; + auto actual_serial_number = std::array {"LK-2206"}; - auto prefix_plus_date_of_test_size = std::size("LK-XXXX") - sizeof('\0'); + auto prefix_plus_date_of_test_size = std::size("LK-2206") - sizeof('\0'); EXPECT_CALL(mock_mcu, getID).WillRepeatedly(Return(mcu_id_returned)); diff --git a/libs/SerialNumberKit/tests/SerialNumberKit_test.cpp b/libs/SerialNumberKit/tests/SerialNumberKit_test.cpp index 76338226ba..4c232a5d70 100644 --- a/libs/SerialNumberKit/tests/SerialNumberKit_test.cpp +++ b/libs/SerialNumberKit/tests/SerialNumberKit_test.cpp @@ -23,7 +23,7 @@ class SerialNumberKitTest : public ::testing::Test // void TearDown() override {} mock::MCU mock_mcu; - SerialNumberKit serial_number_kit {mock_mcu}; + SerialNumberKit serial_number_kit {mock_mcu, SerialNumberKit::DEFAULT_CONFIG}; MCUID mcu_id_returned {0x2A2B2C2D, 0x3A3B3C3D, 0x4A4B4C4D}; }; @@ -35,7 +35,7 @@ TEST_F(SerialNumberKitTest, initialization) TEST_F(SerialNumberKitTest, getSerialNumber) { - auto expected_serial_number = std::array {"LK-XXXX2A2B2C2D3A3B3C3D4A4B4C4D"}; + auto expected_serial_number = std::array {"LK-22062A2B2C2D3A3B3C3D4A4B4C4D"}; EXPECT_CALL(mock_mcu, getID).WillOnce(Return(mcu_id_returned)); @@ -46,7 +46,7 @@ TEST_F(SerialNumberKitTest, getSerialNumber) TEST_F(SerialNumberKitTest, getShortSerialNumber) { - auto expected_serial_number = std::array {"LK-XXXX2A2B2C2D"}; + auto expected_serial_number = std::array {"LK-22062A2B2C2D"}; EXPECT_CALL(mock_mcu, getID).WillOnce(Return(mcu_id_returned)); diff --git a/spikes/lk_serial_number/main.cpp b/spikes/lk_serial_number/main.cpp index 1f0aa42ce0..0d69f9b522 100644 --- a/spikes/lk_serial_number/main.cpp +++ b/spikes/lk_serial_number/main.cpp @@ -5,15 +5,30 @@ #include "rtos/ThisThread.h" #include "CoreMCU.h" +#include "FATFileSystem.h" #include "HelloWorld.h" #include "LogKit.h" +#include "SDBlockDevice.h" #include "SerialNumberKit.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 mcu = CoreMCU {}; -auto serialnumberkit = SerialNumberKit {mcu}; +auto serialnumberkit = SerialNumberKit {mcu, SerialNumberKit::DEFAULT_CONFIG}; + +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 { @@ -23,6 +38,8 @@ auto main() -> int auto start = rtos::Kernel::Clock::now(); + initializeSD(); + auto hello = HelloWorld(); rtos::ThisThread::sleep_for(1s); From 7d9f5db1af20883c2a128d6d9a1169b9a07b3797 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Tue, 20 Sep 2022 18:09:31 +0200 Subject: [PATCH 088/130] :bug: (ActivityKit): Invert minus & plus images In fs/ the + and - images are not labeled with the right ID, they've swaped The fix handles that special use case while we wait for a better fix --- .../include/activities/DisplayTags.h | 2 ++ .../source/activities/DisplayTags.cpp | 25 +++++++++++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/libs/ActivityKit/include/activities/DisplayTags.h b/libs/ActivityKit/include/activities/DisplayTags.h index 28c22bd0b9..2461478476 100644 --- a/libs/ActivityKit/include/activities/DisplayTags.h +++ b/libs/ActivityKit/include/activities/DisplayTags.h @@ -21,6 +21,8 @@ class DisplayTags : public interface::Activity void stop() final; private: + void processCard(const MagicCard &card); + RFIDKit &_rfidkit; interface::VideoKit &_videokit; std::array _path_buffer = {}; diff --git a/libs/ActivityKit/source/activities/DisplayTags.cpp b/libs/ActivityKit/source/activities/DisplayTags.cpp index 55201dad60..15e45fe13e 100644 --- a/libs/ActivityKit/source/activities/DisplayTags.cpp +++ b/libs/ActivityKit/source/activities/DisplayTags.cpp @@ -14,14 +14,7 @@ void DisplayTags::start() _backup_callback = _rfidkit.getCallback(); - auto on_tag_detected_callback = [this](const MagicCard &card) { - snprintf(_path_buffer.data(), _path_buffer.size(), "fs/home/img/id/%.4x.jpg", card.getId()); - _videokit.displayImage(_path_buffer.data()); - - _backup_callback(card); - }; - - _rfidkit.onTagActivated(on_tag_detected_callback); + _rfidkit.onTagActivated([this](const MagicCard &card) { processCard(card); }); } void DisplayTags::stop() @@ -29,6 +22,22 @@ void DisplayTags::stop() _rfidkit.onTagActivated(_backup_callback); } +void DisplayTags::processCard(const MagicCard &card) +{ + // TODO (@HPezz) Remove special case when fix is available + // ! Temporary fix while we wait for a change in fs + if (card == MagicCard::math_arithmetic_addition_sign_plus) { + _videokit.displayImage("fs/home/img/id/003B.jpg"); + } else if (card == MagicCard::math_arithmetic_substraction_sign_minus) { + _videokit.displayImage("fs/home/img/id/003C.jpg"); + } else { + snprintf(_path_buffer.data(), _path_buffer.size(), "fs/home/img/id/%.4x.jpg", card.getId()); + _videokit.displayImage(_path_buffer.data()); + + _backup_callback(card); + } +}; + } // namespace leka::activity // LCOV_EXCL_STOP From 85b4340997695b096dbf26c88b29e13b9d815f6f Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Wed, 14 Sep 2022 16:24:44 +0200 Subject: [PATCH 089/130] :hammer: (version): Move firmware version from build_firmware script to Makefile - Add regex to check format of version - Add script to check version is compliant with semver and mcuboot --- Makefile | 23 ++++--- config/os_version | 1 + tools/check_version.py | 108 +++++++++++++++++++++++++++++++ tools/firmware/build_firmware.sh | 4 +- 4 files changed, 125 insertions(+), 11 deletions(-) create mode 100644 config/os_version create mode 100755 tools/check_version.py diff --git a/Makefile b/Makefile index c5a649eeba..37f76ca834 100644 --- a/Makefile +++ b/Makefile @@ -15,15 +15,16 @@ MCUBOOT_DIR := $(ROOT_DIR)/extern/mcuboot # MARK: - Arguments # -PORT ?= /dev/tty.usbmodem14303 -MBED_GIT_URL ?= $(shell cat $(ROOT_DIR)/config/mbed_git_url) -MBED_BRANCH ?= $(shell cat $(ROOT_DIR)/config/mbed_version) -MBED_VERSION ?= $(shell cat $(ROOT_DIR)/config/mbed_version) -MCUBOOT_GIT_URL ?= $(shell cat $(ROOT_DIR)/config/mcuboot_git_url) -MCUBOOT_VERSION ?= $(shell cat $(ROOT_DIR)/config/mcuboot_version) -BAUDRATE ?= 115200 -BUILD_TYPE ?= Release -TARGET_BOARD ?= LEKA_V1_2_DEV +PORT ?= /dev/tty.usbmodem14303 +MBED_GIT_URL ?= $(shell cat $(ROOT_DIR)/config/mbed_git_url) +MBED_BRANCH ?= $(shell cat $(ROOT_DIR)/config/mbed_version) +MBED_VERSION ?= $(shell cat $(ROOT_DIR)/config/mbed_version) +MCUBOOT_GIT_URL ?= $(shell cat $(ROOT_DIR)/config/mcuboot_git_url) +MCUBOOT_VERSION ?= $(shell cat $(ROOT_DIR)/config/mcuboot_version) +BAUDRATE ?= 115200 +BUILD_TYPE ?= Release +TARGET_BOARD ?= LEKA_V1_2_DEV +FIRMWARE_VERSION ?= $(shell cat $(ROOT_DIR)/config/os_version) # # MARK: - Options @@ -97,6 +98,10 @@ tests_functional: @echo "🏗️ Building functional tests ⚗️" cmake --build $(TARGET_BUILD_DIR) -t tests_functional +firmware: + python3 tools/check_version.py ./config/os_version + ./tools/firmware/build_firmware.sh -r -v $(FIRMWARE_VERSION) + # # MARK: - Config targets # diff --git a/config/os_version b/config/os_version new file mode 100644 index 0000000000..9084fa2f71 --- /dev/null +++ b/config/os_version @@ -0,0 +1 @@ +1.1.0 diff --git a/tools/check_version.py b/tools/check_version.py new file mode 100755 index 0000000000..4ca7556cd2 --- /dev/null +++ b/tools/check_version.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 + +import sys +from typing import Dict +from ctypes import c_uint8, c_uint16 +import argparse +import re + + +# +# MARK: - Standards +# + + +def version_is_semver_compliant(version: str): + # Source: https://semver.org/ + semver_regex = "^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$" + + matches = re.match(semver_regex, version) + return matches is not (None) + + +def version_is_mcuboot_compliant(version: str): + version_splited = version.split(sep=".") + + if len(version_splited) != 3: + return False + + try: + if (int(version_splited[0]) > c_uint8(-1).value): + return False + + if (int(version_splited[1]) > c_uint8(-1).value): + return False + + if (int(version_splited[2]) > c_uint16(-1).value): + return False + + except BaseException as error: + print(f"{error}") + return False + + return True + + +# +# MARK: - argparse +# + + +parser = argparse.ArgumentParser( + description="Check os version compliance with standards used in the project" +) + +parser.add_argument( + "file_version_path", type=str, help="path of file containing version to check" +) + +args = parser.parse_args() + + +# +# MARK: - Functions +# + + +def getDataFromFile(file_path: str) -> str: + data: str = "" + try: + f = open(file_path, "r") + data = f.read() + f.close() + except BaseException as error: + print(f"{error}") + sys.exit(1) + + return data + + +# +# MARK: - Main +# + + +def main(): + standards_applied: Dict[str, function[[str], bool]] = {} + standards_applied["Semantic Versioning 2.0.0"] = version_is_semver_compliant + standards_applied["MCUboot"] = version_is_mcuboot_compliant + + data_containing_version = getDataFromFile(args.file_version_path) + + all_checks_did_pass: bool = True + + for standard_name, version_is_compliant in standards_applied.items(): + if version_is_compliant(data_containing_version): + print(f"✅ Version is compliant with {standard_name}.") + else: + print(f"❌ Version is not compliant with {standard_name}.") + all_checks_did_pass = False + + if not all_checks_did_pass: + return 1 + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/firmware/build_firmware.sh b/tools/firmware/build_firmware.sh index 96e0beddec..b64060353f 100755 --- a/tools/firmware/build_firmware.sh +++ b/tools/firmware/build_firmware.sh @@ -5,17 +5,17 @@ mkdir -p _release # Variables RECOMPILE_BOOTLOADER="false" -while getopts r flag +while getopts rv: flag do case "${flag}" in r) RECOMPILE_BOOTLOADER="true";; + v) APPLICATION_VERSION=$OPTARG;; esac done BOOTLOADER_HEX="_tmp/bootloader.hex" APPLICATION_HEX_SOURCE="_build/LEKA_V1_2_DEV/app/os/LekaOS.hex" -APPLICATION_VERSION="1.1.0+0" APPLICATION_SIGNED_HEX="_tmp/application-signed.hex" FIRMWARE_HEX="_release/firmware.hex" From f316175fb5329ca84576bd45e9b45ef43bcc0104 Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Wed, 14 Sep 2022 16:41:11 +0200 Subject: [PATCH 090/130] :construction_worker: (firmware): Use make firmware command --- .github/workflows/ci-create_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-create_release.yml b/.github/workflows/ci-create_release.yml index 36e94ce4cd..9b00a84d26 100644 --- a/.github/workflows/ci-create_release.yml +++ b/.github/workflows/ci-create_release.yml @@ -41,7 +41,7 @@ jobs: - name: Build firmware, os, bootloader run: | - ./tools/firmware/build_firmware.sh -r + make firmware # # Mark: - Upload From 3b0b61b4699f196fe331d26a50930ff3c97edd4f Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Thu, 15 Sep 2022 12:30:44 +0200 Subject: [PATCH 091/130] :wrench: (serialnumber): Update date_of_test with epoch format --- fs/sys/date_of_test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/sys/date_of_test b/fs/sys/date_of_test index 4ad5df7db8..b137dba7b5 100755 --- a/fs/sys/date_of_test +++ b/fs/sys/date_of_test @@ -1 +1 @@ -220619 +1663086252 From 753ff7c7688ef3c04c738084ce6a98d28b975da6 Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Fri, 16 Sep 2022 19:11:33 +0200 Subject: [PATCH 092/130] :bricks: (date_of_test): Handle epoch format for date_of_test --- .../include/SerialNumberFormater.h | 3 ++ .../source/SerialNumberFormater.cpp | 31 +++++++++++++- .../tests/SerialNumberFormater_test.cpp | 42 +++++++++++++------ .../tests/SerialNumberKit_test.cpp | 4 +- 4 files changed, 65 insertions(+), 15 deletions(-) diff --git a/libs/SerialNumberKit/include/SerialNumberFormater.h b/libs/SerialNumberKit/include/SerialNumberFormater.h index be6e1e68ea..7eb6c4ccc1 100644 --- a/libs/SerialNumberKit/include/SerialNumberFormater.h +++ b/libs/SerialNumberKit/include/SerialNumberFormater.h @@ -23,6 +23,9 @@ class SerialNumberFormater void setMCUID(std::span serial_number, uint32_t offset, uint8_t number_of_digits); private: + void setDateOfTestFromYYMMDD(std::span content, std::span date_of_test) const; + void setDateOfTestFromEpoch(std::span content, std::span date_of_test) const; + interface::MCU &_mcu; const Config _config; }; diff --git a/libs/SerialNumberKit/source/SerialNumberFormater.cpp b/libs/SerialNumberKit/source/SerialNumberFormater.cpp index 8e936f063f..3c5ba01c7e 100644 --- a/libs/SerialNumberKit/source/SerialNumberFormater.cpp +++ b/libs/SerialNumberKit/source/SerialNumberFormater.cpp @@ -24,7 +24,19 @@ auto SerialNumberFormater::setDateOfTest(std::span partial_serial_numbe date_of_test.fill('X'); if (auto file = FileManagerKit::File {_config.date_of_test_path}; file.is_open()) { - file.read(date_of_test); + auto file_content = std::array {}; + + file.read(file_content); + + // ? epoch is 10 characters long, so the file size is at least 8 bytes + if (auto format_is_epoch = file.size() > 8; format_is_epoch) { + setDateOfTestFromEpoch(file_content, date_of_test); + } + // ? some robot still use the format YYMMDD + if (auto format_is_YYMMDD = file.size() <= 8; format_is_YYMMDD) { + setDateOfTestFromYYMMDD(file_content, date_of_test); + } + file.close(); } @@ -33,6 +45,23 @@ auto SerialNumberFormater::setDateOfTest(std::span partial_serial_numbe return std::size(date_of_test); } +void SerialNumberFormater::setDateOfTestFromYYMMDD(std::span content, std::span date_of_test) const +{ + std::copy(content.begin(), content.begin() + std::size(date_of_test), date_of_test.begin()); +} + +void SerialNumberFormater::setDateOfTestFromEpoch(std::span content, std::span date_of_test) const +{ + std::time_t epoch = std::atoll(content.data()); + std::array epoch_c_array {}; + + struct tm local_time; + localtime_r(&epoch, &local_time); + std::strftime(epoch_c_array.data(), std::size(epoch_c_array), "%y%m", &local_time); + + std::copy(epoch_c_array.begin(), epoch_c_array.begin() + std::size(date_of_test), date_of_test.begin()); +} + void SerialNumberFormater::setMCUID(std::span partial_serial_number, uint32_t offset, uint8_t number_of_digits) { constexpr auto MAX_MCU_ID_DIGITS = 24; diff --git a/libs/SerialNumberKit/tests/SerialNumberFormater_test.cpp b/libs/SerialNumberKit/tests/SerialNumberFormater_test.cpp index 73a5da2f25..2d0c843a22 100644 --- a/libs/SerialNumberKit/tests/SerialNumberFormater_test.cpp +++ b/libs/SerialNumberKit/tests/SerialNumberFormater_test.cpp @@ -22,13 +22,13 @@ class SerialNumberFormaterTest : public ::testing::Test void SetUp() override { std::ofstream dot_stream {config.date_of_test_path, std::ios::binary}; - dot_stream << current_date_of_test_str; + dot_stream << current_date_of_test_epoch_str; dot_stream.close(); } void TearDown() override { std::filesystem::remove(config.date_of_test_path); } - std::string current_date_of_test_str = "220619\n"; + std::string current_date_of_test_epoch_str = "1663086252\n"; // Tuesday, September 13, 2022 4:24:12 PM mock::MCU mock_mcu; SerialNumberFormater::Config config {.date_of_test_path = "/tmp/date_of_test"}; @@ -54,6 +54,24 @@ TEST_F(SerialNumberFormaterTest, setPrefix) TEST_F(SerialNumberFormaterTest, setDateOfTest) { + auto expected_serial_number = std::array {"LK-2209"}; + auto actual_serial_number = std::array {"LK-"}; + + auto prefix_size = std::size("LK-") - sizeof('\0'); + + auto actual_prefix_number_size = serial_number_formater.setDateOfTest(actual_serial_number, prefix_size); + EXPECT_EQ(actual_serial_number, expected_serial_number); + EXPECT_EQ(actual_prefix_number_size, std::size("2209") - sizeof('\0')); +} + +TEST_F(SerialNumberFormaterTest, setDateOfTestYYMMDD) +{ + std::string current_date_of_test_YYMMDD_str = "220619\n"; + + std::ofstream dot_stream {config.date_of_test_path, std::ios::binary}; + dot_stream << current_date_of_test_YYMMDD_str; + dot_stream.close(); + auto expected_serial_number = std::array {"LK-2206"}; auto actual_serial_number = std::array {"LK-"}; @@ -83,36 +101,36 @@ TEST_F(SerialNumberFormaterTest, setMCUID) auto expected_serial_number = std::array {}; auto actual_serial_number = std::array {}; - auto prefix_plus_date_of_test_size = std::size("LK-2206") - sizeof('\0'); + auto prefix_plus_date_of_test_size = std::size("LK-2209") - sizeof('\0'); auto number_of_digits = 0; EXPECT_CALL(mock_mcu, getID).WillRepeatedly(Return(mcu_id_returned)); - actual_serial_number = std::array {"LK-2206"}; + actual_serial_number = std::array {"LK-2209"}; number_of_digits = 10; - expected_serial_number = std::array {"LK-22062A2B2C2D3A"}; + expected_serial_number = std::array {"LK-22092A2B2C2D3A"}; serial_number_formater.setMCUID(actual_serial_number, prefix_plus_date_of_test_size, number_of_digits); EXPECT_EQ(actual_serial_number, expected_serial_number); - actual_serial_number = std::array {"LK-2206"}; + actual_serial_number = std::array {"LK-2209"}; number_of_digits = 19; - expected_serial_number = std::array {"LK-22062A2B2C2D3A3B3C3D4A4"}; + expected_serial_number = std::array {"LK-22092A2B2C2D3A3B3C3D4A4"}; serial_number_formater.setMCUID(actual_serial_number, prefix_plus_date_of_test_size, number_of_digits); EXPECT_EQ(actual_serial_number, expected_serial_number); - actual_serial_number = std::array {"LK-2206"}; + actual_serial_number = std::array {"LK-2209"}; number_of_digits = 24; - expected_serial_number = std::array {"LK-22062A2B2C2D3A3B3C3D4A4B4C4D"}; + expected_serial_number = std::array {"LK-22092A2B2C2D3A3B3C3D4A4B4C4D"}; serial_number_formater.setMCUID(actual_serial_number, prefix_plus_date_of_test_size, number_of_digits); EXPECT_EQ(actual_serial_number, expected_serial_number); } TEST_F(SerialNumberFormaterTest, setMCUIDExceedingNumberOfDigits) { - auto expected_serial_number = std::array {"LK-22062A2B2C2D3A3B3C3D4A4B4C4D"}; - auto actual_serial_number = std::array {"LK-2206"}; + auto expected_serial_number = std::array {"LK-22092A2B2C2D3A3B3C3D4A4B4C4D"}; + auto actual_serial_number = std::array {"LK-2209"}; - auto prefix_plus_date_of_test_size = std::size("LK-2206") - sizeof('\0'); + auto prefix_plus_date_of_test_size = std::size("LK-2209") - sizeof('\0'); EXPECT_CALL(mock_mcu, getID).WillRepeatedly(Return(mcu_id_returned)); diff --git a/libs/SerialNumberKit/tests/SerialNumberKit_test.cpp b/libs/SerialNumberKit/tests/SerialNumberKit_test.cpp index 4c232a5d70..79cadd84fc 100644 --- a/libs/SerialNumberKit/tests/SerialNumberKit_test.cpp +++ b/libs/SerialNumberKit/tests/SerialNumberKit_test.cpp @@ -35,7 +35,7 @@ TEST_F(SerialNumberKitTest, initialization) TEST_F(SerialNumberKitTest, getSerialNumber) { - auto expected_serial_number = std::array {"LK-22062A2B2C2D3A3B3C3D4A4B4C4D"}; + auto expected_serial_number = std::array {"LK-22092A2B2C2D3A3B3C3D4A4B4C4D"}; EXPECT_CALL(mock_mcu, getID).WillOnce(Return(mcu_id_returned)); @@ -46,7 +46,7 @@ TEST_F(SerialNumberKitTest, getSerialNumber) TEST_F(SerialNumberKitTest, getShortSerialNumber) { - auto expected_serial_number = std::array {"LK-22062A2B2C2D"}; + auto expected_serial_number = std::array {"LK-22092A2B2C2D"}; EXPECT_CALL(mock_mcu, getID).WillOnce(Return(mcu_id_returned)); From f4264af835aeceb1031ccc17b92cc75fcf2c5856 Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Fri, 16 Sep 2022 21:12:15 +0200 Subject: [PATCH 093/130] :sparkles: (file): Get SHA256 of a file using mbedtls --- include/interface/platform/File.h | 2 ++ libs/FileManagerKit/CMakeLists.txt | 1 + libs/FileManagerKit/include/FileManagerKit.h | 2 ++ libs/FileManagerKit/source/File.cpp | 33 +++++++++++++++++ .../FileManagerKit/tests/File_sha256_test.cpp | 35 +++++++++++++++++++ spikes/lk_file_manager_kit/main.cpp | 18 ++++++++++ tests/unit/headers/CMakeLists.txt | 4 +++ tests/unit/mocks/mocks/leka/File.h | 3 ++ tests/unit/stubs/CMakeLists.txt | 4 +++ 9 files changed, 102 insertions(+) create mode 100644 libs/FileManagerKit/tests/File_sha256_test.cpp diff --git a/include/interface/platform/File.h b/include/interface/platform/File.h index 7e9df4d02b..7fd5710ec1 100644 --- a/include/interface/platform/File.h +++ b/include/interface/platform/File.h @@ -55,6 +55,8 @@ struct File { virtual void clearerr() = 0; [[nodiscard]] virtual auto is_open() const -> bool = 0; + + virtual auto getSHA256() -> std::array = 0; }; } // namespace leka::interface diff --git a/libs/FileManagerKit/CMakeLists.txt b/libs/FileManagerKit/CMakeLists.txt index bd4202a2e5..9208d515c4 100644 --- a/libs/FileManagerKit/CMakeLists.txt +++ b/libs/FileManagerKit/CMakeLists.txt @@ -25,6 +25,7 @@ target_link_libraries(FileManagerKit if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") leka_unit_tests_sources( tests/File_test.cpp + tests/File_sha256_test.cpp tests/FileReception_test.cpp tests/FileManagerKit_test.cpp ) diff --git a/libs/FileManagerKit/include/FileManagerKit.h b/libs/FileManagerKit/include/FileManagerKit.h index cb41a810af..318131c3b1 100644 --- a/libs/FileManagerKit/include/FileManagerKit.h +++ b/libs/FileManagerKit/include/FileManagerKit.h @@ -53,6 +53,8 @@ struct File : public interface::File, public mbed::NonCopyable { [[nodiscard]] auto is_open() const -> bool final; + auto getSHA256() -> std::array final; + private: std::unique_ptr _file {nullptr, &fclose}; }; diff --git a/libs/FileManagerKit/source/File.cpp b/libs/FileManagerKit/source/File.cpp index e402dc3a80..2145059e5b 100644 --- a/libs/FileManagerKit/source/File.cpp +++ b/libs/FileManagerKit/source/File.cpp @@ -2,6 +2,8 @@ // Copyright 2021 APF France handicap // SPDX-License-Identifier: Apache-2.0 +#include "mbedtls/platform.h" +#include "mbedtls/sha256.h" #include #include #include @@ -189,3 +191,34 @@ void File::clearerr() std::clearerr(_file.get()); } + +auto File::getSHA256() -> std::array +{ + std::array sha256 {}; + std::array buffer = {}; + + sha256.fill('\0'); + + if (!is_open()) { + return sha256; + } + + mbedtls_platform_setup(nullptr); + mbedtls_sha256_context context; + + mbedtls_sha256_init(&context); + mbedtls_sha256_starts(&context, 0); + + for (auto i = 0; i < size(); i += std::size(buffer)) { + seek(i); + auto bytes_read = read(buffer.data(), std::size(buffer)); + mbedtls_sha256_update(&context, buffer.data(), bytes_read); + } + + mbedtls_sha256_finish(&context, sha256.data()); + + mbedtls_sha256_free(&context); + mbedtls_platform_teardown(nullptr); + + return sha256; +} diff --git a/libs/FileManagerKit/tests/File_sha256_test.cpp b/libs/FileManagerKit/tests/File_sha256_test.cpp new file mode 100644 index 0000000000..a0784e7c41 --- /dev/null +++ b/libs/FileManagerKit/tests/File_sha256_test.cpp @@ -0,0 +1,35 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include "FileManagerKit.h" +#include "gtest/gtest.h" + +using namespace leka; + +TEST(FileSHA256Test, getSHA256) +{ + auto file = FileManagerKit::File {"fs/usr/os/LekaOS-1.0.0.bin", "r"}; + + // ? SHA256 got from $shasum -a 256 fs/usr/os/LekaOS-1.0.0.bin + auto expected_sha256 = std::to_array({ + 0x02, 0x4d, 0x59, 0x65, 0x4e, 0xca, 0x26, 0xf4, 0xfa, 0x21, 0xb7, 0x29, 0x26, 0x81, 0xf6, 0x9f, + 0xdb, 0xd2, 0x3f, 0x3a, 0x82, 0xb7, 0xbc, 0x8f, 0x1f, 0x66, 0xbf, 0xf9, 0x64, 0x84, 0x2c, 0xa8, + }); + auto actual_sha256 = file.getSHA256(); + + EXPECT_EQ(expected_sha256, actual_sha256); +} + +TEST(FileSHA256Test, getSHA256FileNotAccessible) +{ + auto file = FileManagerKit::File {"/tmp/unexisting_directory/XXXXXX", "r"}; + + std::array expected_sha256 {}; + expected_sha256.fill('\0'); + auto actual_sha256 = file.getSHA256(); + + EXPECT_EQ(expected_sha256, actual_sha256); +} diff --git a/spikes/lk_file_manager_kit/main.cpp b/spikes/lk_file_manager_kit/main.cpp index 2b13f2097f..012ff436f4 100644 --- a/spikes/lk_file_manager_kit/main.cpp +++ b/spikes/lk_file_manager_kit/main.cpp @@ -25,6 +25,8 @@ const std::filesystem::path sub_test_dir = test_dir / "sub/"; const std::filesystem::path test_filename = test_dir / "test_spike_lk_filesystem_kit"; const std::filesystem::path sub_test_filename = sub_test_dir / "test_spike_lk_filesystem_kit"; +const auto file_for_sha256_path = std::filesystem::path {"fs/usr/os/LekaOS-1.0.0.bin"}; + SDBlockDevice sd_blockdevice(SD_SPI_MOSI, SD_SPI_MISO, SD_SPI_SCK); FATFileSystem fatfs("fs"); @@ -38,6 +40,18 @@ void initializeSD() fatfs.mount(&sd_blockdevice); } +void printSHA256(std::span array) +{ + for (size_t i = 0; i < std::size(array); i++) { + if (array[i] < 0xF) { + printf("0%x", array[i]); + } else { + printf("%x", array[i]); + } + } + printf("\n"); +} + auto main() -> int { logger::init(); @@ -56,6 +70,10 @@ auto main() -> int rtos::ThisThread::sleep_for(1s); + file.open(file_for_sha256_path); + auto sha256 = file.getSHA256(); + printSHA256(sha256); + 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, diff --git a/tests/unit/headers/CMakeLists.txt b/tests/unit/headers/CMakeLists.txt index dd78e831fb..5534998d69 100644 --- a/tests/unit/headers/CMakeLists.txt +++ b/tests/unit/headers/CMakeLists.txt @@ -51,6 +51,10 @@ target_include_directories(mbed-os ${EXTERN_MBED_OS_DIR}/connectivity/include ${EXTERN_MBED_OS_DIR}/connectivity/include/connectivity + ${EXTERN_MBED_OS_DIR}/connectivity/mbedtls + ${EXTERN_MBED_OS_DIR}/connectivity/mbedtls/include + ${EXTERN_MBED_OS_DIR}/connectivity/mbedtls/include/mbedtls + ${EXTERN_MBED_OS_DIR}/drivers ${EXTERN_MBED_OS_DIR}/drivers/include ${EXTERN_MBED_OS_DIR}/drivers/include/drivers diff --git a/tests/unit/mocks/mocks/leka/File.h b/tests/unit/mocks/mocks/leka/File.h index aa4fe77c54..89b2138a46 100644 --- a/tests/unit/mocks/mocks/leka/File.h +++ b/tests/unit/mocks/mocks/leka/File.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include "filesystem" @@ -56,6 +57,8 @@ class File : public interface::File MOCK_METHOD(void, clearerr, (), (override)); MOCK_METHOD(bool, is_open, (), (const override)); + + MOCK_METHOD((std::array), getSHA256, (), (override)); }; } // namespace leka::mock diff --git a/tests/unit/stubs/CMakeLists.txt b/tests/unit/stubs/CMakeLists.txt index 5851b38944..01859ccd08 100644 --- a/tests/unit/stubs/CMakeLists.txt +++ b/tests/unit/stubs/CMakeLists.txt @@ -58,4 +58,8 @@ target_sources(stubs ${EXTERN_MBED_OS_DIR}/connectivity/FEATURE_BLE/source/SecurityManager.cpp ${EXTERN_MBED_OS_DIR}/connectivity/FEATURE_BLE/tests/UNITTESTS/doubles/fakes/BLE.cpp ${EXTERN_MBED_OS_DIR}/connectivity/FEATURE_BLE/tests/UNITTESTS/doubles/fakes/source/GattServerImpl_mock.cpp + + ${EXTERN_MBED_OS_DIR}/connectivity/mbedtls/source/platform.c + ${EXTERN_MBED_OS_DIR}/connectivity/mbedtls/source/sha256.c + ${EXTERN_MBED_OS_DIR}/connectivity/mbedtls/source/platform_util.c ) From af671b8a6e271351db5fbfc5916d37b713d2e4a5 Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Wed, 7 Sep 2022 14:28:22 +0200 Subject: [PATCH 094/130] :bug: (tools): Fix args namespace case --- tools/check_fs_content.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/check_fs_content.py b/tools/check_fs_content.py index 5ca9bbd07e..bab38bb1b6 100755 --- a/tools/check_fs_content.py +++ b/tools/check_fs_content.py @@ -77,7 +77,7 @@ def wait_for_status(): status = '' no_response_counter = 0 - while(no_response_counter <= MAX_GET_LINE_RETRIES): + while (no_response_counter <= MAX_GET_LINE_RETRIES): sleep(.005) status = read_status() @@ -159,7 +159,7 @@ def print_report(broken, empty, missing, reboot): # Relative path from LekaOS root ROOT_PATH = args.dir if not args.test else 'fs/usr/test' -LOOP_SLEEP_DELAY = args.SLEEP_DELAY if not args.FAST else 0.2 +LOOP_SLEEP_DELAY = args.loop_delay if not args.fast else 0.2 FILES_BROKEN = list() FILES_EMPTY = list() From 3a066ef47b3127e9abf8beccb7f31dea52567c7a Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Tue, 6 Sep 2022 11:47:06 +0200 Subject: [PATCH 095/130] :sparkles: (ActivityKit): Add new activity - NumberRecognition --- libs/ActivityKit/CMakeLists.txt | 2 + .../include/activities/NumberRecognition.h | 51 +++++++++++++++++ .../source/activities/NumberRecognition.cpp | 56 +++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 libs/ActivityKit/include/activities/NumberRecognition.h create mode 100644 libs/ActivityKit/source/activities/NumberRecognition.cpp diff --git a/libs/ActivityKit/CMakeLists.txt b/libs/ActivityKit/CMakeLists.txt index 3f9b199581..9ca4a95c56 100644 --- a/libs/ActivityKit/CMakeLists.txt +++ b/libs/ActivityKit/CMakeLists.txt @@ -15,12 +15,14 @@ target_sources(ActivityKit PRIVATE source/ActivityKit.cpp source/activities/DisplayTags.cpp + source/activities/NumberRecognition.cpp ) target_link_libraries(ActivityKit RFIDKit CoreLED VideoKit + ReinforcerKit ) if(${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") diff --git a/libs/ActivityKit/include/activities/NumberRecognition.h b/libs/ActivityKit/include/activities/NumberRecognition.h new file mode 100644 index 0000000000..ac40f57128 --- /dev/null +++ b/libs/ActivityKit/include/activities/NumberRecognition.h @@ -0,0 +1,51 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +// LCOV_EXCL_START + +#include + +#include "RFIDKit.h" +#include "ReinforcerKit.h" +#include "include/Number.h" +#include "interface/Activity.h" +#include "interface/libs/VideoKit.h" + +namespace leka::activity { + +class NumberRecognition : public interface::Activity +{ + public: + explicit NumberRecognition(RFIDKit &rfidkit, interface::VideoKit &videokit, ReinforcerKit &reinforcerkit) + : _rfidkit(rfidkit), _videokit(videokit), _reinforcerkit(reinforcerkit) {}; + + void start() final; + void stop() final; + + private: + void processCard(const MagicCard &card); + void launchNextRound(); + + RFIDKit &_rfidkit; + interface::VideoKit &_videokit; + ReinforcerKit &_reinforcerkit; + + static constexpr uint8_t kRoundsNumber = 10; + static constexpr uint8_t kSizeOfNumberTable = 11 * 2; + + uint8_t _current_round = 0; + std::optional _current_number {}; + std::function _backup_callback {}; + std::array _numbers = { + Number::number_0, Number::number_1, Number::number_2, Number::number_3, Number::number_4, Number::number_5, + Number::number_6, Number::number_7, Number::number_8, Number::number_9, Number::number_10, Number::number_0, + Number::number_1, Number::number_2, Number::number_3, Number::number_4, Number::number_5, Number::number_6, + Number::number_7, Number::number_8, Number::number_9, Number::number_10}; +}; + +} // namespace leka::activity + +// LCOV_EXCL_STOP diff --git a/libs/ActivityKit/source/activities/NumberRecognition.cpp b/libs/ActivityKit/source/activities/NumberRecognition.cpp new file mode 100644 index 0000000000..66405fe1f1 --- /dev/null +++ b/libs/ActivityKit/source/activities/NumberRecognition.cpp @@ -0,0 +1,56 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +// LCOV_EXCL_START + +#include "NumberRecognition.h" +#include + +namespace leka::activity { + +void NumberRecognition::start() +{ + _current_round = 0; + _current_number = {}; + + _backup_callback = _rfidkit.getCallback(); + std::shuffle(_numbers.begin(), _numbers.end(), std::mt19937(static_cast(time(nullptr)))); + launchNextRound(); + + _rfidkit.onTagActivated([this](const MagicCard &card) { processCard(card); }); +} + +void NumberRecognition::stop() +{ + _rfidkit.onTagActivated(_backup_callback); +} + +void NumberRecognition::processCard(const MagicCard &card) +{ + if (card == _current_number->card) { + _reinforcerkit.playDefault(); + ++_current_round; + + if (_current_round == kRoundsNumber) { + _backup_callback(MagicCard::dice_roll); + return; + } + + launchNextRound(); + } else { + _backup_callback(card); + } +} + +void NumberRecognition::launchNextRound() +{ + _current_number = _numbers.at(_current_round); + + auto full_path = "/fs/home/img/id/" + std::string(_current_number->id) + ".jpg"; + _videokit.fillWhiteBackgroundAndDisplayImage(full_path); +} + +} // namespace leka::activity + +// LCOV_EXCL_STOP From b8f8722b01e2930f02236ca1771b2a3c3b6cfdb0 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 7 Sep 2022 11:43:57 +0200 Subject: [PATCH 096/130] :sparkles: (os): Add activity - NumberRecognition --- app/os/main.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/os/main.cpp b/app/os/main.cpp index e135aafc0d..137804bdb2 100644 --- a/app/os/main.cpp +++ b/app/os/main.cpp @@ -42,6 +42,7 @@ #include "FirmwareKit.h" #include "HelloWorld.h" #include "LogKit.h" +#include "NumberRecognition.h" #include "QSPIFBlockDevice.h" #include "RFIDKit.h" #include "ReinforcerKit.h" @@ -330,12 +331,15 @@ namespace activities { namespace internal { - auto display_tag = leka::activity::DisplayTags(rfidkit, display::videokit); + auto display_tag = leka::activity::DisplayTags(rfidkit, display::videokit); + auto number_recognition = leka::activity::NumberRecognition(rfidkit, display::videokit, reinforcerkit); - } + } // namespace internal inline const std::unordered_map activities { - {MagicCard::number_10, &internal::display_tag}}; + {MagicCard::number_10, &internal::display_tag}, + {MagicCard::number_1, &internal::number_recognition}, + }; } // namespace activities From 7d37648f2ed4b4ad4594b71fcc663cfd01229ca0 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 7 Sep 2022 14:18:58 +0200 Subject: [PATCH 097/130] :sparkles: (os): Add activity - PictoColorRecognition --- libs/ActivityKit/CMakeLists.txt | 1 + .../activities/PictoColorRecognition.h | 49 ++++++++++++++++ .../activities/PictoColorRecognition.cpp | 56 +++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 libs/ActivityKit/include/activities/PictoColorRecognition.h create mode 100644 libs/ActivityKit/source/activities/PictoColorRecognition.cpp diff --git a/libs/ActivityKit/CMakeLists.txt b/libs/ActivityKit/CMakeLists.txt index 9ca4a95c56..83761471be 100644 --- a/libs/ActivityKit/CMakeLists.txt +++ b/libs/ActivityKit/CMakeLists.txt @@ -16,6 +16,7 @@ target_sources(ActivityKit source/ActivityKit.cpp source/activities/DisplayTags.cpp source/activities/NumberRecognition.cpp + source/activities/PictoColorRecognition.cpp ) target_link_libraries(ActivityKit diff --git a/libs/ActivityKit/include/activities/PictoColorRecognition.h b/libs/ActivityKit/include/activities/PictoColorRecognition.h new file mode 100644 index 0000000000..57ee42e382 --- /dev/null +++ b/libs/ActivityKit/include/activities/PictoColorRecognition.h @@ -0,0 +1,49 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +// LCOV_EXCL_START + +#include + +#include "RFIDKit.h" +#include "ReinforcerKit.h" +#include "include/Color.h" +#include "interface/Activity.h" +#include "interface/libs/VideoKit.h" + +namespace leka::activity { + +class PictoColorRecognition : public interface::Activity +{ + public: + explicit PictoColorRecognition(RFIDKit &rfidkit, interface::VideoKit &videokit, ReinforcerKit &reinforcerkit) + : _rfidkit(rfidkit), _videokit(videokit), _reinforcerkit(reinforcerkit) {}; + + void start() final; + void stop() final; + + private: + void processCard(const MagicCard &card); + void launchNextRound(); + + RFIDKit &_rfidkit; + interface::VideoKit &_videokit; + ReinforcerKit &_reinforcerkit; + + static constexpr uint8_t kRoundsNumber = 10; + static constexpr uint8_t kSizeOfColorTable = 4 * 3; + + uint8_t _current_round = 0; + std::optional _current_color {}; + std::function _backup_callback {}; + std::array _colors = {Color::blue, Color::blue, Color::blue, Color::green, + Color::green, Color::green, Color::yellow, Color::yellow, + Color::yellow, Color::red, Color::red, Color::red}; +}; + +} // namespace leka::activity + +// LCOV_EXCL_STOP diff --git a/libs/ActivityKit/source/activities/PictoColorRecognition.cpp b/libs/ActivityKit/source/activities/PictoColorRecognition.cpp new file mode 100644 index 0000000000..c60f934a77 --- /dev/null +++ b/libs/ActivityKit/source/activities/PictoColorRecognition.cpp @@ -0,0 +1,56 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +// LCOV_EXCL_START + +#include "PictoColorRecognition.h" +#include + +namespace leka::activity { + +void PictoColorRecognition::start() +{ + _current_round = 0; + _current_color = {}; + + _backup_callback = _rfidkit.getCallback(); + std::shuffle(_colors.begin(), _colors.end(), std::mt19937(static_cast(time(nullptr)))); + launchNextRound(); + + _rfidkit.onTagActivated([this](const MagicCard &card) { processCard(card); }); +} + +void PictoColorRecognition::stop() +{ + _rfidkit.onTagActivated(_backup_callback); +} + +void PictoColorRecognition::processCard(const MagicCard &card) +{ + if (card == _current_color->card) { + _reinforcerkit.playDefault(); + ++_current_round; + + if (_current_round == kRoundsNumber) { + _backup_callback(MagicCard::dice_roll); + return; + } + + launchNextRound(); + } else { + _backup_callback(card); + } +} + +void PictoColorRecognition::launchNextRound() +{ + _current_color = _colors.at(_current_round); + + auto full_path = "/fs/home/img/id/" + std::string(_current_color->id) + ".jpg"; + _videokit.fillWhiteBackgroundAndDisplayImage(full_path); +} + +} // namespace leka::activity + +// LCOV_EXCL_STOP From 6d77e7cd69fe711964792d67f45249539921945f Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 7 Sep 2022 14:19:42 +0200 Subject: [PATCH 098/130] :sparkles: (os): Add activity - PictoColorRecognition --- app/os/main.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/os/main.cpp b/app/os/main.cpp index 137804bdb2..8799a3f613 100644 --- a/app/os/main.cpp +++ b/app/os/main.cpp @@ -43,6 +43,7 @@ #include "HelloWorld.h" #include "LogKit.h" #include "NumberRecognition.h" +#include "PictoColorRecognition.h" #include "QSPIFBlockDevice.h" #include "RFIDKit.h" #include "ReinforcerKit.h" @@ -331,14 +332,16 @@ namespace activities { namespace internal { - auto display_tag = leka::activity::DisplayTags(rfidkit, display::videokit); - auto number_recognition = leka::activity::NumberRecognition(rfidkit, display::videokit, reinforcerkit); + auto display_tag = leka::activity::DisplayTags(rfidkit, display::videokit); + auto number_recognition = leka::activity::NumberRecognition(rfidkit, display::videokit, reinforcerkit); + auto picto_color_recognition = leka::activity::PictoColorRecognition(rfidkit, display::videokit, reinforcerkit); } // namespace internal inline const std::unordered_map activities { {MagicCard::number_10, &internal::display_tag}, {MagicCard::number_1, &internal::number_recognition}, + {MagicCard::number_2, &internal::picto_color_recognition}, }; } // namespace activities From e9e4710e52eb96daf22f629895c4428843a7ffe6 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 7 Sep 2022 15:19:22 +0200 Subject: [PATCH 099/130] :sparkles: (ActivityKit): Add new activity - LedColorRecognition --- libs/ActivityKit/CMakeLists.txt | 1 + .../include/activities/LedColorRecognition.h | 52 ++++++++++++++++ .../source/activities/LedColorRecognition.cpp | 60 +++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 libs/ActivityKit/include/activities/LedColorRecognition.h create mode 100644 libs/ActivityKit/source/activities/LedColorRecognition.cpp diff --git a/libs/ActivityKit/CMakeLists.txt b/libs/ActivityKit/CMakeLists.txt index 83761471be..13c65d53dd 100644 --- a/libs/ActivityKit/CMakeLists.txt +++ b/libs/ActivityKit/CMakeLists.txt @@ -17,6 +17,7 @@ target_sources(ActivityKit source/activities/DisplayTags.cpp source/activities/NumberRecognition.cpp source/activities/PictoColorRecognition.cpp + source/activities/LedColorRecognition.cpp ) target_link_libraries(ActivityKit diff --git a/libs/ActivityKit/include/activities/LedColorRecognition.h b/libs/ActivityKit/include/activities/LedColorRecognition.h new file mode 100644 index 0000000000..354255c09c --- /dev/null +++ b/libs/ActivityKit/include/activities/LedColorRecognition.h @@ -0,0 +1,52 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +// LCOV_EXCL_START + +#include +#include + +#include "RFIDKit.h" +#include "ReinforcerKit.h" +#include "include/Color.h" +#include "interface/Activity.h" +#include "interface/libs/VideoKit.h" + +namespace leka::activity { + +class LedColorRecognition : public interface::Activity +{ + public: + explicit LedColorRecognition(RFIDKit &rfidkit, interface::VideoKit &videokit, ReinforcerKit &reinforcerkit, + interface::LED &led) + : _rfidkit(rfidkit), _videokit(videokit), _reinforcerkit(reinforcerkit), _led(led) {}; + + void start() final; + void stop() final; + + private: + void processCard(const MagicCard &card); + void launchNextRound(); + + RFIDKit &_rfidkit; + interface::VideoKit &_videokit; + ReinforcerKit &_reinforcerkit; + interface::LED &_led; + + static constexpr uint8_t kRoundsNumber = 10; + static constexpr uint8_t kSizeOfColorsTable = 6 * 2; + + uint8_t _current_round = 0; + std::optional _current_color {}; + std::function _backup_callback {}; + std::array _colors = {Color::purple, Color::purple, Color::blue, Color::blue, + Color::green, Color::green, Color::yellow, Color::yellow, + Color::orange, Color::orange, Color::red, Color::red}; +}; + +} // namespace leka::activity + +// LCOV_EXCL_STOP diff --git a/libs/ActivityKit/source/activities/LedColorRecognition.cpp b/libs/ActivityKit/source/activities/LedColorRecognition.cpp new file mode 100644 index 0000000000..cade674eae --- /dev/null +++ b/libs/ActivityKit/source/activities/LedColorRecognition.cpp @@ -0,0 +1,60 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +// LCOV_EXCL_START + +#include "LedColorRecognition.h" + +namespace leka::activity { + +void LedColorRecognition::start() +{ + _current_round = 0; + _current_color = {}; + + _videokit.displayImage("fs/home/img/system/robot-face-smiling-slightly.jpg"); + + _backup_callback = _rfidkit.getCallback(); + std::shuffle(_colors.begin(), _colors.end(), std::mt19937(static_cast(time(nullptr)))); + launchNextRound(); + + _rfidkit.onTagActivated([this](const MagicCard &card) { processCard(card); }); +} + +void LedColorRecognition::stop() +{ + _rfidkit.onTagActivated(_backup_callback); +} + +void LedColorRecognition::processCard(const MagicCard &card) +{ + using namespace std::chrono; + + if (card == _current_color->card) { + _reinforcerkit.playDefault(); + rtos::ThisThread::sleep_for(400ms); + ++_current_round; + + if (_current_round == kRoundsNumber) { + _backup_callback(MagicCard::dice_roll); + return; + } + + launchNextRound(); + } else { + _backup_callback(card); + } +}; + +void LedColorRecognition::launchNextRound() +{ + _current_color = _colors.at(_current_round); + + _led.setColor(_current_color->color); + _led.show(); +} + +} // namespace leka::activity + +// LCOV_EXCL_STOP From bd8f17caeac7fa6def9e401fa77e61459d70935a Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 7 Sep 2022 15:19:52 +0200 Subject: [PATCH 100/130] :sparkles: (os): Add activity - LedColorRecognition --- app/os/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/os/main.cpp b/app/os/main.cpp index 8799a3f613..f7ffbd7e40 100644 --- a/app/os/main.cpp +++ b/app/os/main.cpp @@ -41,6 +41,7 @@ #include "FATFileSystem.h" #include "FirmwareKit.h" #include "HelloWorld.h" +#include "LedColorRecognition.h" #include "LogKit.h" #include "NumberRecognition.h" #include "PictoColorRecognition.h" @@ -335,6 +336,8 @@ namespace activities { auto display_tag = leka::activity::DisplayTags(rfidkit, display::videokit); auto number_recognition = leka::activity::NumberRecognition(rfidkit, display::videokit, reinforcerkit); auto picto_color_recognition = leka::activity::PictoColorRecognition(rfidkit, display::videokit, reinforcerkit); + auto led_color_recognition = + leka::activity::LedColorRecognition(rfidkit, display::videokit, reinforcerkit, leds::belt); } // namespace internal @@ -342,6 +345,7 @@ namespace activities { {MagicCard::number_10, &internal::display_tag}, {MagicCard::number_1, &internal::number_recognition}, {MagicCard::number_2, &internal::picto_color_recognition}, + {MagicCard::number_3, &internal::led_color_recognition}, }; } // namespace activities From 731609d5b55bb56282539bc85451da086f3d6ddc Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Thu, 18 Aug 2022 16:50:28 +0200 Subject: [PATCH 101/130] :zap: (MathUtils): Make map constexpr --- libs/Utils/include/MathUtils.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/Utils/include/MathUtils.h b/libs/Utils/include/MathUtils.h index a9764d5210..8861f20aee 100644 --- a/libs/Utils/include/MathUtils.h +++ b/libs/Utils/include/MathUtils.h @@ -10,7 +10,8 @@ namespace leka::utils::math { template -auto map(IntputType x, IntputType in_min, IntputType in_max, OutputType out_min, OutputType out_max) -> OutputType +constexpr auto map(IntputType x, IntputType in_min, IntputType in_max, OutputType out_min, OutputType out_max) + -> OutputType { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } From 3541487ed56f17725981a3bcc00830a44f2735f8 Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Thu, 18 Aug 2022 16:52:12 +0200 Subject: [PATCH 102/130] :fire: (MathUtils): Remove random8 test for values > uint8_t This test was not useful and generating warning messages. --- libs/Utils/tests/MathUtils_test_random8.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/libs/Utils/tests/MathUtils_test_random8.cpp b/libs/Utils/tests/MathUtils_test_random8.cpp index e06d53b9a4..dd1b17bcc4 100644 --- a/libs/Utils/tests/MathUtils_test_random8.cpp +++ b/libs/Utils/tests/MathUtils_test_random8.cpp @@ -22,10 +22,3 @@ TEST(MathUtilsTest, RandomBetween0And255) EXPECT_GE(random, 0); EXPECT_LE(random, 255); } - -TEST(MathUtilsTest, RandomBetweenOutsideOfRange) -{ - auto random = random8(300, 500); - EXPECT_GE(random, 0); - EXPECT_LE(random, 255); -} From e166409cdfe33e67b739e52eb1853515a37c9e2b Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Tue, 20 Sep 2022 19:38:28 +0200 Subject: [PATCH 103/130] :construction_worker: (runners): Update runners to Ubuntu 22.04 LTS --- .github/workflows/ci-build_targets.yml | 2 +- .github/workflows/ci-code_analysis-clang_tidy.yml | 2 +- .github/workflows/ci-code_analysis-compare_base_head.yml | 6 +++--- .github/workflows/ci-code_analysis-sanitizers.yml | 2 +- .github/workflows/ci-code_analysis-sonarcloud.yml | 2 +- .github/workflows/ci-create_release.yml | 2 +- .github/workflows/ci-linters.yml | 2 +- .github/workflows/ci-unit_tests.yml | 2 +- .github/workflows/tools-cleanup_artifacts.yml | 2 +- .github/workflows/tools-projects_status_labeler.yml | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci-build_targets.yml b/.github/workflows/ci-build_targets.yml index c47d06e29d..dc5ad72187 100644 --- a/.github/workflows/ci-build_targets.yml +++ b/.github/workflows/ci-build_targets.yml @@ -20,7 +20,7 @@ jobs: build_target: name: target - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: fail-fast: false diff --git a/.github/workflows/ci-code_analysis-clang_tidy.yml b/.github/workflows/ci-code_analysis-clang_tidy.yml index c3d062444a..a84a7fb4cb 100644 --- a/.github/workflows/ci-code_analysis-clang_tidy.yml +++ b/.github/workflows/ci-code_analysis-clang_tidy.yml @@ -15,7 +15,7 @@ on: jobs: run: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: # diff --git a/.github/workflows/ci-code_analysis-compare_base_head.yml b/.github/workflows/ci-code_analysis-compare_base_head.yml index 46e97ce3b2..f90bd287be 100644 --- a/.github/workflows/ci-code_analysis-compare_base_head.yml +++ b/.github/workflows/ci-code_analysis-compare_base_head.yml @@ -15,7 +15,7 @@ jobs: build_base_ref: name: build base_ref w/ debug - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: fail-fast: false @@ -75,7 +75,7 @@ jobs: build_head_ref: name: build head_ref w/ debug - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: fail-fast: false @@ -134,7 +134,7 @@ jobs: compare_base_head_files: name: Compare base/head files - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: [build_base_ref, build_head_ref] strategy: diff --git a/.github/workflows/ci-code_analysis-sanitizers.yml b/.github/workflows/ci-code_analysis-sanitizers.yml index 39407aa44a..b697589e1b 100644 --- a/.github/workflows/ci-code_analysis-sanitizers.yml +++ b/.github/workflows/ci-code_analysis-sanitizers.yml @@ -16,7 +16,7 @@ on: jobs: run_sanitizers: name: Build & run - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: fail-fast: false diff --git a/.github/workflows/ci-code_analysis-sonarcloud.yml b/.github/workflows/ci-code_analysis-sonarcloud.yml index ea867e85db..d978b9a8e3 100644 --- a/.github/workflows/ci-code_analysis-sonarcloud.yml +++ b/.github/workflows/ci-code_analysis-sonarcloud.yml @@ -15,7 +15,7 @@ on: jobs: run: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: # diff --git a/.github/workflows/ci-create_release.yml b/.github/workflows/ci-create_release.yml index 9b00a84d26..9fc31aa9e9 100644 --- a/.github/workflows/ci-create_release.yml +++ b/.github/workflows/ci-create_release.yml @@ -16,7 +16,7 @@ on: jobs: bootloader_os_firmware: name: Bootloader + OS = Firmware - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: # diff --git a/.github/workflows/ci-linters.yml b/.github/workflows/ci-linters.yml index 3d90ab252b..4f3bc8e2ae 100644 --- a/.github/workflows/ci-linters.yml +++ b/.github/workflows/ci-linters.yml @@ -7,7 +7,7 @@ on: push jobs: clang_format: name: clang-format - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout diff --git a/.github/workflows/ci-unit_tests.yml b/.github/workflows/ci-unit_tests.yml index 6948bb916a..fbe84ba83f 100644 --- a/.github/workflows/ci-unit_tests.yml +++ b/.github/workflows/ci-unit_tests.yml @@ -16,7 +16,7 @@ on: jobs: run_unit_tests: name: Build & run - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: fail-fast: false diff --git a/.github/workflows/tools-cleanup_artifacts.yml b/.github/workflows/tools-cleanup_artifacts.yml index e68cf9c0f8..647414bc6f 100644 --- a/.github/workflows/tools-cleanup_artifacts.yml +++ b/.github/workflows/tools-cleanup_artifacts.yml @@ -6,7 +6,7 @@ on: jobs: delete-artifacts: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: kolpav/purge-artifacts-action@v1 with: diff --git a/.github/workflows/tools-projects_status_labeler.yml b/.github/workflows/tools-projects_status_labeler.yml index b087af1309..b6f48a5c74 100644 --- a/.github/workflows/tools-projects_status_labeler.yml +++ b/.github/workflows/tools-projects_status_labeler.yml @@ -8,7 +8,7 @@ on: jobs: triage: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Auto card labeler uses: technote-space/auto-card-labeler@v2 From 4680a4a1fc200fa17903c9882471f15ba49da3c4 Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Tue, 20 Sep 2022 19:39:57 +0200 Subject: [PATCH 104/130] :construction_worker: (linters): Remove clang-format installation step It is now available in the runner https://github.com/actions/runner-images/blob/main/images/linux/Ubuntu2204-Readme.md#language-and-runtime --- .github/workflows/ci-linters.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/ci-linters.yml b/.github/workflows/ci-linters.yml index 4f3bc8e2ae..a722b2a1c7 100644 --- a/.github/workflows/ci-linters.yml +++ b/.github/workflows/ci-linters.yml @@ -15,14 +15,6 @@ jobs: with: fetch-depth: 2 - - name: Install clang-format 14 - run: | - wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add - - sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-14 main" - sudo apt update -y - sudo apt install -y --no-install-recommends clang-format-14 - clang-format-14 --version - - name: Run clang-format run: | python3 tools/run-clang-format.py --clang-format-executable=clang-format-14 -r --extension=h,hpp,c,cpp --color=always --style=file ./app ./drivers ./libs ./spikes ./tests From 1c68b13b31040a6c91bc94b00355a7cb12ffb823 Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Tue, 20 Sep 2022 19:46:19 +0200 Subject: [PATCH 105/130] :construction_worker: (setup): Replace apt-get with apt-fast --- .github/actions/setup/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 3091cc5942..6d14f913e1 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -93,8 +93,8 @@ runs: - name: Install tools shell: bash run: | - sudo apt-get update - sudo apt-get install -y --no-install-recommends ninja-build ccache lcov gcovr + sudo apt-fast update + sudo apt-fast install -y --no-install-recommends ninja-build ccache lcov gcovr - name: Cache ccache id: global_cache-ccache From 078866815a9f7ed5b07e47cd3e3f188952ccc62e Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Tue, 20 Sep 2022 19:50:56 +0200 Subject: [PATCH 106/130] :construction_worker: (linters): Use clang-tidy 14 + remove installation step --- .github/workflows/ci-code_analysis-clang_tidy.yml | 12 ------------ tools/run-clang-tidy.rb | 2 ++ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci-code_analysis-clang_tidy.yml b/.github/workflows/ci-code_analysis-clang_tidy.yml index a84a7fb4cb..a884f6d937 100644 --- a/.github/workflows/ci-code_analysis-clang_tidy.yml +++ b/.github/workflows/ci-code_analysis-clang_tidy.yml @@ -28,18 +28,6 @@ jobs: - name: Setup CI uses: ./.github/actions/setup - # - # Mark: - Install clang-tidy - # - - - name: Install clang-tidy 13 - run: | - wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add - - sudo apt-add-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-13 main" - sudo apt update -y - sudo apt install -y --no-install-recommends clang-tidy-13 - clang-tidy-13 --version - # # Mark: - Config # diff --git a/tools/run-clang-tidy.rb b/tools/run-clang-tidy.rb index 0494c6878b..0d99b77d91 100644 --- a/tools/run-clang-tidy.rb +++ b/tools/run-clang-tidy.rb @@ -62,6 +62,8 @@ def log_debug(message) if system("which clang-tidy > /dev/null") $CLANG_TIDY_EXEC = "clang-tidy" +elsif system("which clang-tidy-14 > /dev/null") + $CLANG_TIDY_EXEC = "clang-tidy-14" elsif system("which clang-tidy-13 > /dev/null") $CLANG_TIDY_EXEC = "clang-tidy-13" elsif system("which /opt/homebrew/opt/llvm/bin/clang-tidy > /dev/null") From c0053945396e0b79318312b9d378a9568948c03b Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 7 Sep 2022 16:13:22 +0200 Subject: [PATCH 107/130] :recycle: (MagicCard): Make getId() constexpr --- libs/RFIDKit/include/MagicCard.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/RFIDKit/include/MagicCard.h b/libs/RFIDKit/include/MagicCard.h index 42a0ad112b..857e9dd1c0 100644 --- a/libs/RFIDKit/include/MagicCard.h +++ b/libs/RFIDKit/include/MagicCard.h @@ -20,7 +20,7 @@ struct MagicCard { explicit constexpr MagicCard(const rfid::Tag &tag) : _tag(tag) {} - [[nodiscard]] auto getId() const -> uint16_t + [[nodiscard]] constexpr auto getId() const -> uint16_t { auto high = _tag.data.at(id_high_byte_index); auto low = _tag.data.at(id_low_byte_index); From 0598a68d4072710f56317620e087efcd15f83a95 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 7 Sep 2022 16:14:54 +0200 Subject: [PATCH 108/130] :sparkles: (ActivityKit): Add new activity - ChooseReinforcer --- libs/ActivityKit/CMakeLists.txt | 1 + .../include/activities/ChooseReinforcer.h | 37 ++++++++++++ .../source/activities/ChooseReinforcer.cpp | 56 +++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 libs/ActivityKit/include/activities/ChooseReinforcer.h create mode 100644 libs/ActivityKit/source/activities/ChooseReinforcer.cpp diff --git a/libs/ActivityKit/CMakeLists.txt b/libs/ActivityKit/CMakeLists.txt index 13c65d53dd..a957e57e13 100644 --- a/libs/ActivityKit/CMakeLists.txt +++ b/libs/ActivityKit/CMakeLists.txt @@ -18,6 +18,7 @@ target_sources(ActivityKit source/activities/NumberRecognition.cpp source/activities/PictoColorRecognition.cpp source/activities/LedColorRecognition.cpp + source/activities/ChooseReinforcer.cpp ) target_link_libraries(ActivityKit diff --git a/libs/ActivityKit/include/activities/ChooseReinforcer.h b/libs/ActivityKit/include/activities/ChooseReinforcer.h new file mode 100644 index 0000000000..585dbea22d --- /dev/null +++ b/libs/ActivityKit/include/activities/ChooseReinforcer.h @@ -0,0 +1,37 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +// LCOV_EXCL_START + +#include "RFIDKit.h" +#include "ReinforcerKit.h" +#include "interface/Activity.h" +#include "interface/libs/VideoKit.h" + +namespace leka::activity { + +class ChooseReinforcer : public interface::Activity +{ + public: + explicit ChooseReinforcer(RFIDKit &rfidkit, interface::VideoKit &videokit, ReinforcerKit &reinforcerkit) + : _rfidkit(rfidkit), _videokit(videokit), _reinforcerkit(reinforcerkit) {}; + + void start() final; + void stop() final; + + private: + void processCard(const MagicCard &card); + + RFIDKit &_rfidkit; + interface::VideoKit &_videokit; + ReinforcerKit &_reinforcerkit; + + std::function _backup_callback {}; +}; + +} // namespace leka::activity + +// LCOV_EXCL_STOP diff --git a/libs/ActivityKit/source/activities/ChooseReinforcer.cpp b/libs/ActivityKit/source/activities/ChooseReinforcer.cpp new file mode 100644 index 0000000000..909d3ca4e2 --- /dev/null +++ b/libs/ActivityKit/source/activities/ChooseReinforcer.cpp @@ -0,0 +1,56 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +// LCOV_EXCL_START + +#include "ChooseReinforcer.h" + +namespace leka::activity { + +void ChooseReinforcer::start() +{ + _videokit.displayImage("fs/home/img/system/robot-face-smiling-slightly.jpg"); + + _backup_callback = _rfidkit.getCallback(); + + _rfidkit.onTagActivated([this](const MagicCard &card) { processCard(card); }); +} + +void ChooseReinforcer::processCard(const MagicCard &card) +{ + switch (card.getId()) { + case (MagicCard::reinforcer_1_blink_green.getId()): + _reinforcerkit.setDefaultReinforcer(ReinforcerKit::Reinforcer::BlinkGreen); + _reinforcerkit.playDefault(); + break; + case (MagicCard::reinforcer_2_spin_blink.getId()): + _reinforcerkit.setDefaultReinforcer(ReinforcerKit::Reinforcer::SpinBlink); + _reinforcerkit.playDefault(); + break; + case (MagicCard::reinforcer_3_fire.getId()): + _reinforcerkit.setDefaultReinforcer(ReinforcerKit::Reinforcer::Fire); + _reinforcerkit.playDefault(); + break; + case (MagicCard::reinforcer_4_sprinkles.getId()): + _reinforcerkit.setDefaultReinforcer(ReinforcerKit::Reinforcer::Sprinkles); + _reinforcerkit.playDefault(); + break; + case (MagicCard::reinforcer_5_rainbow.getId()): + _reinforcerkit.setDefaultReinforcer(ReinforcerKit::Reinforcer::Rainbow); + _reinforcerkit.playDefault(); + break; + default: + _backup_callback(card); + break; + } +} + +void ChooseReinforcer::stop() +{ + _rfidkit.onTagActivated(_backup_callback); +} + +} // namespace leka::activity + +// LCOV_EXCL_STOP From 182f2a1152de5bb7210ad96b9d3777f3505ac61b Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 7 Sep 2022 16:15:19 +0200 Subject: [PATCH 109/130] :sparkles: (os): Add activity - ChooseReinforcer --- app/os/main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/os/main.cpp b/app/os/main.cpp index f7ffbd7e40..851a3de379 100644 --- a/app/os/main.cpp +++ b/app/os/main.cpp @@ -10,6 +10,7 @@ #include "rtos/Thread.h" #include "ActivityKit.h" +#include "ChooseReinforcer.h" #include "CoreBattery.h" #include "CoreBufferedSerial.h" #include "CoreDMA2D.hpp" @@ -334,6 +335,7 @@ namespace activities { namespace internal { auto display_tag = leka::activity::DisplayTags(rfidkit, display::videokit); + auto choose_reinforcer = leka::activity::ChooseReinforcer(rfidkit, display::videokit, reinforcerkit); auto number_recognition = leka::activity::NumberRecognition(rfidkit, display::videokit, reinforcerkit); auto picto_color_recognition = leka::activity::PictoColorRecognition(rfidkit, display::videokit, reinforcerkit); auto led_color_recognition = @@ -343,6 +345,7 @@ namespace activities { inline const std::unordered_map activities { {MagicCard::number_10, &internal::display_tag}, + {MagicCard::number_0, &internal::choose_reinforcer}, {MagicCard::number_1, &internal::number_recognition}, {MagicCard::number_2, &internal::picto_color_recognition}, {MagicCard::number_3, &internal::led_color_recognition}, From 3e3183224ad929492088ac84387708d3a1939245 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 7 Sep 2022 16:47:58 +0200 Subject: [PATCH 110/130] :sparkles: (ActivityKit): Add new activity - EmotionRecognition --- libs/ActivityKit/CMakeLists.txt | 1 + .../include/activities/EmotionRecognition.h | 49 ++++++++++++++++ .../source/activities/EmotionRecognition.cpp | 56 +++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 libs/ActivityKit/include/activities/EmotionRecognition.h create mode 100644 libs/ActivityKit/source/activities/EmotionRecognition.cpp diff --git a/libs/ActivityKit/CMakeLists.txt b/libs/ActivityKit/CMakeLists.txt index a957e57e13..6cac331367 100644 --- a/libs/ActivityKit/CMakeLists.txt +++ b/libs/ActivityKit/CMakeLists.txt @@ -19,6 +19,7 @@ target_sources(ActivityKit source/activities/PictoColorRecognition.cpp source/activities/LedColorRecognition.cpp source/activities/ChooseReinforcer.cpp + source/activities/EmotionRecognition.cpp ) target_link_libraries(ActivityKit diff --git a/libs/ActivityKit/include/activities/EmotionRecognition.h b/libs/ActivityKit/include/activities/EmotionRecognition.h new file mode 100644 index 0000000000..2c4d32319c --- /dev/null +++ b/libs/ActivityKit/include/activities/EmotionRecognition.h @@ -0,0 +1,49 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +// LCOV_EXCL_START + +#include + +#include "RFIDKit.h" +#include "ReinforcerKit.h" +#include "include/Emotion.h" +#include "interface/Activity.h" +#include "interface/libs/VideoKit.h" + +namespace leka::activity { + +class EmotionRecognition : public interface::Activity +{ + public: + explicit EmotionRecognition(RFIDKit &rfidkit, interface::VideoKit &videokit, ReinforcerKit &reinforcerkit) + : _rfidkit(rfidkit), _videokit(videokit), _reinforcerkit(reinforcerkit) {}; + + void start() final; + void stop() final; + + private: + void processCard(const MagicCard &card); + void launchNextRound(); + + RFIDKit &_rfidkit; + interface::VideoKit &_videokit; + ReinforcerKit &_reinforcerkit; + + static constexpr uint8_t kRoundsNumber = 10; + static constexpr uint8_t kSizeOfEmotionsTable = 5 * 2; + + uint8_t _current_round = 0; + std::optional _current_emotion {}; + std::function _backup_callback {}; + std::array _emotions = { + Emotion::anger, Emotion::anger, Emotion::fear, Emotion::fear, Emotion::joy, + Emotion::joy, Emotion::sadness, Emotion::sadness, Emotion::disgust, Emotion::disgust}; +}; + +} // namespace leka::activity + +// LCOV_EXCL_STOP diff --git a/libs/ActivityKit/source/activities/EmotionRecognition.cpp b/libs/ActivityKit/source/activities/EmotionRecognition.cpp new file mode 100644 index 0000000000..a3a5e91122 --- /dev/null +++ b/libs/ActivityKit/source/activities/EmotionRecognition.cpp @@ -0,0 +1,56 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +// LCOV_EXCL_START + +#include "EmotionRecognition.h" +#include + +namespace leka::activity { + +void EmotionRecognition::start() +{ + _current_round = 0; + _current_emotion = {}; + + _backup_callback = _rfidkit.getCallback(); + std::shuffle(_emotions.begin(), _emotions.end(), std::mt19937(static_cast(time(nullptr)))); + launchNextRound(); + + _rfidkit.onTagActivated([this](const MagicCard &card) { processCard(card); }); +} + +void EmotionRecognition::stop() +{ + _rfidkit.onTagActivated(_backup_callback); +} + +void EmotionRecognition::processCard(const MagicCard &card) +{ + if (card == std::get<0>(_current_emotion->cards) || card == std::get<1>(_current_emotion->cards)) { + _reinforcerkit.playDefault(); + ++_current_round; + + if (_current_round == kRoundsNumber) { + _backup_callback(MagicCard::dice_roll); + return; + } + + launchNextRound(); + } else { + _backup_callback(card); + } +} + +void EmotionRecognition::launchNextRound() +{ + _current_emotion = _emotions.at(_current_round); + + auto full_path = "/fs/home/img/id/" + std::string(_current_emotion->id) + ".jpg"; + _videokit.displayImage(full_path); +} + +} // namespace leka::activity + +// LCOV_EXCL_STOP From c9072b15b0778dc4ea1b12b3d56f1f326bb7772a Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 7 Sep 2022 16:48:29 +0200 Subject: [PATCH 111/130] :sparkles: (os): Add activity - EmotionRecognition --- app/os/main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/os/main.cpp b/app/os/main.cpp index 851a3de379..210d0242ce 100644 --- a/app/os/main.cpp +++ b/app/os/main.cpp @@ -38,6 +38,7 @@ #include "CoreTimeout.h" #include "CoreVideo.hpp" #include "DisplayTags.h" +#include "EmotionRecognition.h" #include "EventLoopKit.h" #include "FATFileSystem.h" #include "FirmwareKit.h" @@ -340,6 +341,7 @@ namespace activities { auto picto_color_recognition = leka::activity::PictoColorRecognition(rfidkit, display::videokit, reinforcerkit); auto led_color_recognition = leka::activity::LedColorRecognition(rfidkit, display::videokit, reinforcerkit, leds::belt); + auto emotion_recognition = leka::activity::EmotionRecognition(rfidkit, display::videokit, reinforcerkit); } // namespace internal @@ -349,6 +351,7 @@ namespace activities { {MagicCard::number_1, &internal::number_recognition}, {MagicCard::number_2, &internal::picto_color_recognition}, {MagicCard::number_3, &internal::led_color_recognition}, + {MagicCard::number_4, &internal::emotion_recognition}, }; } // namespace activities From 52ee4e3ec121d89627cd3ab4c2ecb2d45baf015f Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 7 Sep 2022 17:42:58 +0200 Subject: [PATCH 112/130] :sparkles: (ActivityKit): Add new activity - FoodRecognition --- libs/ActivityKit/CMakeLists.txt | 1 + .../include/activities/FoodRecognition.h | 51 +++++++++++++++++ .../source/activities/FoodRecognition.cpp | 56 +++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 libs/ActivityKit/include/activities/FoodRecognition.h create mode 100644 libs/ActivityKit/source/activities/FoodRecognition.cpp diff --git a/libs/ActivityKit/CMakeLists.txt b/libs/ActivityKit/CMakeLists.txt index 6cac331367..a66389b97b 100644 --- a/libs/ActivityKit/CMakeLists.txt +++ b/libs/ActivityKit/CMakeLists.txt @@ -20,6 +20,7 @@ target_sources(ActivityKit source/activities/LedColorRecognition.cpp source/activities/ChooseReinforcer.cpp source/activities/EmotionRecognition.cpp + source/activities/FoodRecognition.cpp ) target_link_libraries(ActivityKit diff --git a/libs/ActivityKit/include/activities/FoodRecognition.h b/libs/ActivityKit/include/activities/FoodRecognition.h new file mode 100644 index 0000000000..42c87f117e --- /dev/null +++ b/libs/ActivityKit/include/activities/FoodRecognition.h @@ -0,0 +1,51 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +// LCOV_EXCL_START + +#include + +#include "RFIDKit.h" +#include "ReinforcerKit.h" +#include "include/Food.h" +#include "interface/Activity.h" +#include "interface/libs/VideoKit.h" + +namespace leka::activity { + +class FoodRecognition : public interface::Activity +{ + public: + explicit FoodRecognition(RFIDKit &rfidkit, interface::VideoKit &videokit, ReinforcerKit &reinforcerkit) + : _rfidkit(rfidkit), _videokit(videokit), _reinforcerkit(reinforcerkit) {}; + + void start() final; + void stop() final; + + private: + void processCard(const MagicCard &card); + void launchNextRound(); + + RFIDKit &_rfidkit; + interface::VideoKit &_videokit; + ReinforcerKit &_reinforcerkit; + + static constexpr uint8_t kScoreToWin = 10; + static constexpr uint8_t kSizeOfFoodsTable = 9 * 2; + + uint8_t _current_round = 0; + std::optional _current_food {}; + std::function _backup_callback {}; + std::array _foods = { + Food::apple, Food::apple, Food::banana, Food::banana, Food::grapes, Food::grapes, + Food::mushroom, Food::mushroom, Food::salad, Food::salad, Food::carrot, Food::carrot, + Food::cherry, Food::cherry, Food::strawberry, Food::strawberry, Food::potato, Food::potato, + }; +}; + +} // namespace leka::activity + +// LCOV_EXCL_STOP diff --git a/libs/ActivityKit/source/activities/FoodRecognition.cpp b/libs/ActivityKit/source/activities/FoodRecognition.cpp new file mode 100644 index 0000000000..223a5b7f86 --- /dev/null +++ b/libs/ActivityKit/source/activities/FoodRecognition.cpp @@ -0,0 +1,56 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +// LCOV_EXCL_START + +#include "FoodRecognition.h" +#include + +namespace leka::activity { + +void FoodRecognition::start() +{ + _current_round = 0; + _current_food = {}; + + _backup_callback = _rfidkit.getCallback(); + std::shuffle(_foods.begin(), _foods.end(), std::mt19937(static_cast(time(nullptr)))); + launchNextRound(); + + _rfidkit.onTagActivated([this](const MagicCard &card) { processCard(card); }); +} + +void FoodRecognition::stop() +{ + _rfidkit.onTagActivated(_backup_callback); +} + +void FoodRecognition::processCard(const MagicCard &card) +{ + if (card == _current_food->card) { + _reinforcerkit.playDefault(); + ++_current_round; + + if (_current_round == kScoreToWin) { + _backup_callback(MagicCard::dice_roll); + return; + } + + launchNextRound(); + } else { + _backup_callback(card); + } +} + +void FoodRecognition::launchNextRound() +{ + _current_food = _foods.at(_current_round); + + auto full_path = "/fs/home/img/id/" + std::string(_current_food->id) + ".jpg"; + _videokit.fillWhiteBackgroundAndDisplayImage(full_path); +} + +} // namespace leka::activity + +// LCOV_EXCL_STOP From 807186e62f453897655ddca1fa3f91b2659d5f86 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Wed, 7 Sep 2022 17:43:26 +0200 Subject: [PATCH 113/130] :sparkles: (os): Add activity - FoodRecognition --- app/os/main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/os/main.cpp b/app/os/main.cpp index 210d0242ce..280451a96b 100644 --- a/app/os/main.cpp +++ b/app/os/main.cpp @@ -42,6 +42,7 @@ #include "EventLoopKit.h" #include "FATFileSystem.h" #include "FirmwareKit.h" +#include "FoodRecognition.h" #include "HelloWorld.h" #include "LedColorRecognition.h" #include "LogKit.h" @@ -342,6 +343,7 @@ namespace activities { auto led_color_recognition = leka::activity::LedColorRecognition(rfidkit, display::videokit, reinforcerkit, leds::belt); auto emotion_recognition = leka::activity::EmotionRecognition(rfidkit, display::videokit, reinforcerkit); + auto food_recognition = leka::activity::FoodRecognition(rfidkit, display::videokit, reinforcerkit); } // namespace internal @@ -352,6 +354,7 @@ namespace activities { {MagicCard::number_2, &internal::picto_color_recognition}, {MagicCard::number_3, &internal::led_color_recognition}, {MagicCard::number_4, &internal::emotion_recognition}, + {MagicCard::number_5, &internal::food_recognition}, }; } // namespace activities From 4fd0ce8c5f534b76c3e593b231650e464a964fb6 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Thu, 8 Sep 2022 11:14:09 +0200 Subject: [PATCH 114/130] :sparkles: (ActivityKit): Add new activity - LedNumberCounting --- libs/ActivityKit/CMakeLists.txt | 1 + .../include/activities/LedNumberCounting.h | 55 ++++++++++++++++ .../source/activities/LedNumberCounting.cpp | 64 +++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 libs/ActivityKit/include/activities/LedNumberCounting.h create mode 100644 libs/ActivityKit/source/activities/LedNumberCounting.cpp diff --git a/libs/ActivityKit/CMakeLists.txt b/libs/ActivityKit/CMakeLists.txt index a66389b97b..6c3b917d3a 100644 --- a/libs/ActivityKit/CMakeLists.txt +++ b/libs/ActivityKit/CMakeLists.txt @@ -21,6 +21,7 @@ target_sources(ActivityKit source/activities/ChooseReinforcer.cpp source/activities/EmotionRecognition.cpp source/activities/FoodRecognition.cpp + source/activities/LedNumberCounting.cpp ) target_link_libraries(ActivityKit diff --git a/libs/ActivityKit/include/activities/LedNumberCounting.h b/libs/ActivityKit/include/activities/LedNumberCounting.h new file mode 100644 index 0000000000..8078e54dbb --- /dev/null +++ b/libs/ActivityKit/include/activities/LedNumberCounting.h @@ -0,0 +1,55 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +// LCOV_EXCL_START + +#include "MathUtils.h" +#include "RFIDKit.h" +#include "ReinforcerKit.h" +#include "interface/Activity.h" +#include "interface/libs/VideoKit.h" + +namespace leka::activity { + +class LedNumberCounting : public interface::Activity +{ + public: + explicit LedNumberCounting(RFIDKit &rfidkit, interface::VideoKit &videokit, ReinforcerKit &reinforcerkit, + interface::LED &led) + : _rfidkit(rfidkit), _videokit(videokit), _reinforcerkit(reinforcerkit), _led(led) {}; + + void start() final; + void stop() final; + + private: + void processCard(const MagicCard &card); + void launchNextRound(); + + RFIDKit &_rfidkit; + interface::VideoKit &_videokit; + ReinforcerKit &_reinforcerkit; + interface::LED &_led; + + static constexpr uint8_t kRoundsNumber = 10; + static constexpr uint8_t kSizeOfColorsTable = 6; + static constexpr uint8_t kSizeOfLedNumberTable = 12; + static constexpr uint8_t kSizeOfLedIndexTable = 7; + + static constexpr std::array colors_table = {RGB::orange, RGB::pure_green, RGB::cyan, + RGB::yellow, RGB::magenta, RGB::pure_red}; + + uint8_t _current_round = 0; + uint8_t _current_leds_number = 0; + MagicCard _expected_tag_number = MagicCard::none; + + std::function _backup_callback {}; + std::array _led_numbers = {1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6}; + std::array _led_indexes = {0, 3, 6, 9, 12, 15, 19}; +}; + +} // namespace leka::activity + +// LCOV_EXCL_STOP diff --git a/libs/ActivityKit/source/activities/LedNumberCounting.cpp b/libs/ActivityKit/source/activities/LedNumberCounting.cpp new file mode 100644 index 0000000000..55a8125d45 --- /dev/null +++ b/libs/ActivityKit/source/activities/LedNumberCounting.cpp @@ -0,0 +1,64 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +// LCOV_EXCL_START + +#include "LedNumberCounting.h" +#include + +namespace leka::activity { + +void LedNumberCounting::start() +{ + _current_round = 0; + _current_leds_number = 0; + _expected_tag_number = MagicCard::none; + + _videokit.displayImage("fs/home/img/system/robot-face-smiling-slightly.jpg"); + + _backup_callback = _rfidkit.getCallback(); + std::shuffle(_led_indexes.begin(), _led_indexes.end(), std::mt19937(static_cast(time(nullptr)))); + std::shuffle(_led_numbers.begin(), _led_numbers.end(), std::mt19937(static_cast(time(nullptr)))); + launchNextRound(); + + _rfidkit.onTagActivated([this](const MagicCard &card) { processCard(card); }); +} + +void LedNumberCounting::stop() +{ + _rfidkit.onTagActivated(_backup_callback); +} + +void LedNumberCounting::processCard(const MagicCard &card) +{ + if (card == _expected_tag_number) { + _reinforcerkit.playDefault(); + ++_current_round; + + if (_current_round == kRoundsNumber) { + _backup_callback(MagicCard::dice_roll); + return; + } + + std::shuffle(_led_indexes.begin(), _led_indexes.end(), std::mt19937(static_cast(time(nullptr)))); + launchNextRound(); + } else { + _backup_callback(card); + } +} + +void LedNumberCounting::launchNextRound() +{ + _current_leds_number = _led_numbers.at(_current_round); + _expected_tag_number = MagicCard(MagicCard::number_0.getId() + _current_leds_number); + + for (auto i = 0; i < _current_leds_number; ++i) { + _led.setColorAtIndex(_led_indexes.at(i), colors_table.at(i)); + } + _led.show(); +} + +} // namespace leka::activity + +// LCOV_EXCL_STOP From 1d8b9de7531310a5ae2fad7a8ca5e32c2c6a9367 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Thu, 8 Sep 2022 11:14:38 +0200 Subject: [PATCH 115/130] :sparkles: (os): Add activity - LedNumberCounting --- app/os/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/os/main.cpp b/app/os/main.cpp index 280451a96b..7152564c75 100644 --- a/app/os/main.cpp +++ b/app/os/main.cpp @@ -45,6 +45,7 @@ #include "FoodRecognition.h" #include "HelloWorld.h" #include "LedColorRecognition.h" +#include "LedNumberCounting.h" #include "LogKit.h" #include "NumberRecognition.h" #include "PictoColorRecognition.h" @@ -344,6 +345,8 @@ namespace activities { leka::activity::LedColorRecognition(rfidkit, display::videokit, reinforcerkit, leds::belt); auto emotion_recognition = leka::activity::EmotionRecognition(rfidkit, display::videokit, reinforcerkit); auto food_recognition = leka::activity::FoodRecognition(rfidkit, display::videokit, reinforcerkit); + auto led_number_counting = + leka::activity::LedNumberCounting(rfidkit, display::videokit, reinforcerkit, leds::belt); } // namespace internal @@ -355,6 +358,7 @@ namespace activities { {MagicCard::number_3, &internal::led_color_recognition}, {MagicCard::number_4, &internal::emotion_recognition}, {MagicCard::number_5, &internal::food_recognition}, + {MagicCard::number_6, &internal::led_number_counting}, }; } // namespace activities From bbfc56a2b848f77c7b72f6a3d3d22b26f1e9e4f2 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Thu, 8 Sep 2022 15:19:35 +0200 Subject: [PATCH 116/130] :sparkles: (ActivityKit): Add new activity - FlashNumberCounting --- libs/ActivityKit/CMakeLists.txt | 1 + .../include/activities/FlashNumberCounting.h | 52 ++++++++++++++ .../source/activities/FlashNumberCounting.cpp | 70 +++++++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 libs/ActivityKit/include/activities/FlashNumberCounting.h create mode 100644 libs/ActivityKit/source/activities/FlashNumberCounting.cpp diff --git a/libs/ActivityKit/CMakeLists.txt b/libs/ActivityKit/CMakeLists.txt index 6c3b917d3a..3fba1b7086 100644 --- a/libs/ActivityKit/CMakeLists.txt +++ b/libs/ActivityKit/CMakeLists.txt @@ -22,6 +22,7 @@ target_sources(ActivityKit source/activities/EmotionRecognition.cpp source/activities/FoodRecognition.cpp source/activities/LedNumberCounting.cpp + source/activities/FlashNumberCounting.cpp ) target_link_libraries(ActivityKit diff --git a/libs/ActivityKit/include/activities/FlashNumberCounting.h b/libs/ActivityKit/include/activities/FlashNumberCounting.h new file mode 100644 index 0000000000..01c4de129f --- /dev/null +++ b/libs/ActivityKit/include/activities/FlashNumberCounting.h @@ -0,0 +1,52 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +// LCOV_EXCL_START + +#include "RFIDKit.h" +#include "ReinforcerKit.h" +#include "interface/Activity.h" +#include "interface/libs/VideoKit.h" + +namespace leka::activity { + +class FlashNumberCounting : public interface::Activity +{ + public: + explicit FlashNumberCounting(RFIDKit &rfidkit, interface::VideoKit &videokit, ReinforcerKit &reinforcerkit, + interface::LED &led) + : _rfidkit(rfidkit), _videokit(videokit), _reinforcerkit(reinforcerkit), _led(led) {}; + + void start() final; + void stop() final; + + private: + void processCard(const MagicCard &card); + void launchNextRound(); + + RFIDKit &_rfidkit; + interface::VideoKit &_videokit; + ReinforcerKit &_reinforcerkit; + interface::LED &_led; + + static constexpr uint8_t kRoundsNumber = 10; + static constexpr uint8_t kSizeOfColorsTable = 5 * 2; + static constexpr uint8_t kSizeOfFlashNumberTable = 12; + + static constexpr std::array _colors = { + RGB::pure_blue, RGB::pure_green, RGB::cyan, RGB::yellow, RGB::magenta, + RGB::pure_blue, RGB::pure_green, RGB::cyan, RGB::yellow, RGB::magenta}; + + uint8_t _current_round = 0; + uint8_t _current_flashes_number = 0; + MagicCard _expected_tag_number = MagicCard::none; + std::function _backup_callback {}; + std::array _flash_numbers = {1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6}; +}; + +} // namespace leka::activity + +// LCOV_EXCL_STOP diff --git a/libs/ActivityKit/source/activities/FlashNumberCounting.cpp b/libs/ActivityKit/source/activities/FlashNumberCounting.cpp new file mode 100644 index 0000000000..ed7036a2eb --- /dev/null +++ b/libs/ActivityKit/source/activities/FlashNumberCounting.cpp @@ -0,0 +1,70 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +// LCOV_EXCL_START + +#include "FlashNumberCounting.h" +#include + +namespace leka::activity { +using namespace std::chrono; + +void FlashNumberCounting::start() +{ + _current_round = 0; + _current_flashes_number = 0; + _expected_tag_number = MagicCard::none; + + _videokit.displayImage("fs/home/img/system/robot-face-smiling-slightly.jpg"); + + _backup_callback = _rfidkit.getCallback(); + std::shuffle(_flash_numbers.begin(), _flash_numbers.end(), std::mt19937(static_cast(time(nullptr)))); + rtos::ThisThread::sleep_for(2s); + + launchNextRound(); + + _rfidkit.onTagActivated([this](const MagicCard &card) { processCard(card); }); +} + +void FlashNumberCounting::stop() +{ + _rfidkit.onTagActivated(_backup_callback); +} + +void FlashNumberCounting::processCard(const MagicCard &card) +{ + if (card == _expected_tag_number) { + _reinforcerkit.playDefault(); + rtos::ThisThread::sleep_for(1s); + + ++_current_round; + + if (_current_round == kRoundsNumber) { + _backup_callback(MagicCard::dice_roll); + return; + } + launchNextRound(); + } else { + _backup_callback(card); + } +} + +void FlashNumberCounting::launchNextRound() +{ + _current_flashes_number = _flash_numbers.at(_current_round); + _expected_tag_number = MagicCard(MagicCard::number_0.getId() + _current_flashes_number); + + for (auto i = 0; i < _current_flashes_number; ++i) { + _led.setColor(_colors.at(_current_round)); + _led.show(); + rtos::ThisThread::sleep_for(400ms); + _led.setColor(RGB::black); + _led.show(); + rtos::ThisThread::sleep_for(500ms); + } +} + +} // namespace leka::activity + +// LCOV_EXCL_STOP From 29a9e5ab5fe89549fd57f3bf9780654ee42862b7 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Thu, 8 Sep 2022 15:19:58 +0200 Subject: [PATCH 117/130] :sparkles: (os): Add activity - FlashNumberCounting --- app/os/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/os/main.cpp b/app/os/main.cpp index 7152564c75..0007999c76 100644 --- a/app/os/main.cpp +++ b/app/os/main.cpp @@ -42,6 +42,7 @@ #include "EventLoopKit.h" #include "FATFileSystem.h" #include "FirmwareKit.h" +#include "FlashNumberCounting.h" #include "FoodRecognition.h" #include "HelloWorld.h" #include "LedColorRecognition.h" @@ -347,6 +348,8 @@ namespace activities { auto food_recognition = leka::activity::FoodRecognition(rfidkit, display::videokit, reinforcerkit); auto led_number_counting = leka::activity::LedNumberCounting(rfidkit, display::videokit, reinforcerkit, leds::belt); + auto flash_number_counting = + leka::activity::FlashNumberCounting(rfidkit, display::videokit, reinforcerkit, leds::belt); } // namespace internal @@ -359,6 +362,7 @@ namespace activities { {MagicCard::number_4, &internal::emotion_recognition}, {MagicCard::number_5, &internal::food_recognition}, {MagicCard::number_6, &internal::led_number_counting}, + {MagicCard::number_7, &internal::flash_number_counting}, }; } // namespace activities From 20eab69ca6a0c5446eadc9f331516adfa566acd1 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Fri, 9 Sep 2022 15:10:00 +0200 Subject: [PATCH 118/130] :sparkles: (ActivityKit): Add new activity - SuperSimon --- libs/ActivityKit/CMakeLists.txt | 1 + .../include/activities/SuperSimon.h | 52 ++++++++++++ .../source/activities/SuperSimon.cpp | 85 +++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 libs/ActivityKit/include/activities/SuperSimon.h create mode 100644 libs/ActivityKit/source/activities/SuperSimon.cpp diff --git a/libs/ActivityKit/CMakeLists.txt b/libs/ActivityKit/CMakeLists.txt index 3fba1b7086..88cf40f4c3 100644 --- a/libs/ActivityKit/CMakeLists.txt +++ b/libs/ActivityKit/CMakeLists.txt @@ -23,6 +23,7 @@ target_sources(ActivityKit source/activities/FoodRecognition.cpp source/activities/LedNumberCounting.cpp source/activities/FlashNumberCounting.cpp + source/activities/SuperSimon.cpp ) target_link_libraries(ActivityKit diff --git a/libs/ActivityKit/include/activities/SuperSimon.h b/libs/ActivityKit/include/activities/SuperSimon.h new file mode 100644 index 0000000000..95d163b5a4 --- /dev/null +++ b/libs/ActivityKit/include/activities/SuperSimon.h @@ -0,0 +1,52 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +// LCOV_EXCL_START + +#include "Color.h" +#include "Number.h" +#include "RFIDKit.h" +#include "ReinforcerKit.h" +#include "interface/Activity.h" +#include "interface/libs/VideoKit.h" + +namespace leka::activity { + +class SuperSimon : public interface::Activity +{ + public: + explicit SuperSimon(RFIDKit &rfidkit, interface::VideoKit &videokit, ReinforcerKit &reinforcerkit, + interface::LED &led) + : _rfidkit(rfidkit), _videokit(videokit), _reinforcerkit(reinforcerkit), _led(led) {}; + + void start() final; + void stop() final; + + private: + void processCard(const MagicCard &card); + void launchNextRound(); + + RFIDKit &_rfidkit; + interface::VideoKit &_videokit; + ReinforcerKit &_reinforcerkit; + interface::LED &_led; + + static constexpr uint8_t kNumberOfColors = 6 * 2; + static constexpr uint8_t kRoundsNumber = 5; + + uint8_t _expected_color_index = 0; + uint8_t _current_round = 0; + uint8_t _last_round = 5; + std::function _backup_callback {}; + std::array, kRoundsNumber> _expected_colors = {}; + std::array _colors = {Color::blue, Color::blue, Color::green, Color::green, + Color::red, Color::red, Color::purple, Color::purple, + Color::orange, Color::orange, Color::yellow, Color::yellow}; +}; + +} // namespace leka::activity + +// LCOV_EXCL_STOP diff --git a/libs/ActivityKit/source/activities/SuperSimon.cpp b/libs/ActivityKit/source/activities/SuperSimon.cpp new file mode 100644 index 0000000000..84152791fd --- /dev/null +++ b/libs/ActivityKit/source/activities/SuperSimon.cpp @@ -0,0 +1,85 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +// LCOV_EXCL_START + +#include + +#include "SuperSimon.h" + +namespace leka::activity { +using namespace std::chrono; + +void SuperSimon::start() +{ + _current_round = 0; + _expected_color_index = 0; + + _videokit.displayImage("fs/home/img/system/robot-face-smiling-slightly.jpg"); + + _backup_callback = _rfidkit.getCallback(); + std::shuffle(_colors.begin(), _colors.end(), std::mt19937(static_cast(time(nullptr)))); + rtos::ThisThread::sleep_for(2s); + + launchNextRound(); + + _rfidkit.onTagActivated([this](const MagicCard &card) { processCard(card); }); +} + +void SuperSimon::stop() +{ + _rfidkit.onTagActivated(_backup_callback); +} + +void SuperSimon::processCard(const MagicCard &card) +{ + if (card == _expected_colors.at(_expected_color_index)->card) { + _led.setColor(_expected_colors.at(_expected_color_index)->color); + _led.show(); + rtos::ThisThread::sleep_for(400ms); + + _led.setColor(RGB::black); + _led.show(); + rtos::ThisThread::sleep_for(500ms); + + if (_expected_color_index == _current_round) { + _reinforcerkit.playDefault(); + rtos::ThisThread::sleep_for(1s); + _expected_color_index = 0; + + if (_current_round >= _last_round) { + _backup_callback(MagicCard::dice_roll); + return; + } + + ++_current_round; + launchNextRound(); + return; + } + + ++_expected_color_index; + + } else { + _backup_callback(card); + } +}; + +void SuperSimon::launchNextRound() +{ + for (auto i = 0; i <= _current_round; ++i) { + _expected_colors.at(i) = _colors.at(i); + + _led.setColor(_expected_colors.at(i)->color); + _led.show(); + rtos::ThisThread::sleep_for(400ms); + + _led.setColor(RGB::black); + _led.show(); + rtos::ThisThread::sleep_for(500ms); + } +} + +} // namespace leka::activity + +// LCOV_EXCL_STOP From 23f3098a5082a6e8882ef7e4162e07ba924eab7c Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Fri, 9 Sep 2022 15:10:30 +0200 Subject: [PATCH 119/130] :sparkles: (os): Add activity - SuperSimon --- app/os/main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/os/main.cpp b/app/os/main.cpp index 0007999c76..2406cb7c47 100644 --- a/app/os/main.cpp +++ b/app/os/main.cpp @@ -57,6 +57,7 @@ #include "SDBlockDevice.h" #include "SerialNumberKit.h" #include "SlicingBlockDevice.h" +#include "SuperSimon.h" #include "VideoKit.h" #include "bootutil/bootutil.h" #include "commands/LedFullCommand.h" @@ -350,6 +351,7 @@ namespace activities { leka::activity::LedNumberCounting(rfidkit, display::videokit, reinforcerkit, leds::belt); auto flash_number_counting = leka::activity::FlashNumberCounting(rfidkit, display::videokit, reinforcerkit, leds::belt); + auto super_simon = leka::activity::SuperSimon(rfidkit, display::videokit, reinforcerkit, leds::belt); } // namespace internal @@ -363,6 +365,7 @@ namespace activities { {MagicCard::number_5, &internal::food_recognition}, {MagicCard::number_6, &internal::led_number_counting}, {MagicCard::number_7, &internal::flash_number_counting}, + {MagicCard::number_8, &internal::super_simon}, }; } // namespace activities From cd301e8ef12719f7c919ed0e9ecdeff2be209d8b Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Fri, 9 Sep 2022 15:37:48 +0200 Subject: [PATCH 120/130] :sparkles: (ActivityKit): Add new activity - ShapeRecognition --- libs/ActivityKit/CMakeLists.txt | 1 + .../include/activities/ShapeRecognition.h | 48 ++++++++++++++++ .../source/activities/ShapeRecognition.cpp | 56 +++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 libs/ActivityKit/include/activities/ShapeRecognition.h create mode 100644 libs/ActivityKit/source/activities/ShapeRecognition.cpp diff --git a/libs/ActivityKit/CMakeLists.txt b/libs/ActivityKit/CMakeLists.txt index 88cf40f4c3..23f087b962 100644 --- a/libs/ActivityKit/CMakeLists.txt +++ b/libs/ActivityKit/CMakeLists.txt @@ -24,6 +24,7 @@ target_sources(ActivityKit source/activities/LedNumberCounting.cpp source/activities/FlashNumberCounting.cpp source/activities/SuperSimon.cpp + source/activities/ShapeRecognition.cpp ) target_link_libraries(ActivityKit diff --git a/libs/ActivityKit/include/activities/ShapeRecognition.h b/libs/ActivityKit/include/activities/ShapeRecognition.h new file mode 100644 index 0000000000..15362036be --- /dev/null +++ b/libs/ActivityKit/include/activities/ShapeRecognition.h @@ -0,0 +1,48 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +// LCOV_EXCL_START + +#include + +#include "RFIDKit.h" +#include "ReinforcerKit.h" +#include "include/Shape.h" +#include "interface/Activity.h" +#include "interface/libs/VideoKit.h" + +namespace leka::activity { + +class ShapeRecognition : public interface::Activity +{ + public: + explicit ShapeRecognition(RFIDKit &rfidkit, interface::VideoKit &videokit, ReinforcerKit &reinforcerkit) + : _rfidkit(rfidkit), _videokit(videokit), _reinforcerkit(reinforcerkit) {}; + + void start() final; + void stop() final; + + private: + void processCard(const MagicCard &card); + void launchNextRound(); + + RFIDKit &_rfidkit; + interface::VideoKit &_videokit; + ReinforcerKit &_reinforcerkit; + + static constexpr uint8_t kRoundsNumber = 8; + static constexpr uint8_t kNumberOfShapes = 4 * 2; + + uint8_t _score = 0; + std::optional _current_shape {}; + std::function _backup_callback {}; + std::array _shapes = {Shape::square, Shape::square, Shape::circle, Shape::circle, + Shape::triangle, Shape::triangle, Shape::star, Shape::star}; +}; + +} // namespace leka::activity + +// LCOV_EXCL_STOP diff --git a/libs/ActivityKit/source/activities/ShapeRecognition.cpp b/libs/ActivityKit/source/activities/ShapeRecognition.cpp new file mode 100644 index 0000000000..6e292a5f61 --- /dev/null +++ b/libs/ActivityKit/source/activities/ShapeRecognition.cpp @@ -0,0 +1,56 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +// LCOV_EXCL_START + +#include "ShapeRecognition.h" +#include + +namespace leka::activity { + +void ShapeRecognition::start() +{ + _score = 0; + _current_shape = {}; + + _backup_callback = _rfidkit.getCallback(); + std::shuffle(_shapes.begin(), _shapes.end(), std::mt19937(static_cast(time(nullptr)))); + launchNextRound(); + + _rfidkit.onTagActivated([this](const MagicCard &card) { processCard(card); }); +} + +void ShapeRecognition::stop() +{ + _rfidkit.onTagActivated(_backup_callback); +} + +void ShapeRecognition::processCard(const MagicCard &card) +{ + if (card == _current_shape->card) { + _reinforcerkit.playDefault(); + ++_score; + + if (_score == kRoundsNumber) { + _backup_callback(MagicCard::dice_roll); + return; + } + + launchNextRound(); + } else { + _backup_callback(card); + } +}; + +void ShapeRecognition::launchNextRound() +{ + _current_shape = _shapes.at(_score); + + auto full_path = "/fs/home/img/id/" + std::string(_current_shape->id) + ".jpg"; + _videokit.fillWhiteBackgroundAndDisplayImage(full_path); +} + +} // namespace leka::activity + +// LCOV_EXCL_STOP From 99298e6e64cf244fede64db7e35ca4be08ffc7b7 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Fri, 9 Sep 2022 15:38:31 +0200 Subject: [PATCH 121/130] :sparkles: (os): Add activity - ShapeRecognition --- app/os/main.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/os/main.cpp b/app/os/main.cpp index 2406cb7c47..e49ee89e48 100644 --- a/app/os/main.cpp +++ b/app/os/main.cpp @@ -56,6 +56,7 @@ #include "RobotController.h" #include "SDBlockDevice.h" #include "SerialNumberKit.h" +#include "ShapeRecognition.h" #include "SlicingBlockDevice.h" #include "SuperSimon.h" #include "VideoKit.h" @@ -351,7 +352,8 @@ namespace activities { leka::activity::LedNumberCounting(rfidkit, display::videokit, reinforcerkit, leds::belt); auto flash_number_counting = leka::activity::FlashNumberCounting(rfidkit, display::videokit, reinforcerkit, leds::belt); - auto super_simon = leka::activity::SuperSimon(rfidkit, display::videokit, reinforcerkit, leds::belt); + auto super_simon = leka::activity::SuperSimon(rfidkit, display::videokit, reinforcerkit, leds::belt); + auto shape_recognition = leka::activity::ShapeRecognition(rfidkit, display::videokit, reinforcerkit); } // namespace internal @@ -366,6 +368,7 @@ namespace activities { {MagicCard::number_6, &internal::led_number_counting}, {MagicCard::number_7, &internal::flash_number_counting}, {MagicCard::number_8, &internal::super_simon}, + {MagicCard::number_9, &internal::shape_recognition}, }; } // namespace activities From 5d3744fe0da9dc5a8140d61f3717ee272a858faf Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Wed, 7 Sep 2022 16:01:44 +0200 Subject: [PATCH 122/130] :arrow_up: (boost): Bump boost::sml to v1.1.6 --- include/boost/sml.hpp | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/include/boost/sml.hpp b/include/boost/sml.hpp index 659666e8ee..e3821d5b11 100644 --- a/include/boost/sml.hpp +++ b/include/boost/sml.hpp @@ -9,12 +9,12 @@ #if (__cplusplus < 201305L && _MSC_VER < 1900) #error "[Boost::ext].SML requires C++14 support (Clang-3.4+, GCC-5.1+, MSVC-2015+)" #else -#define BOOST_SML_VERSION 1'1'5 +#define BOOST_SML_VERSION 1'1'6 #define BOOST_SML_NAMESPACE_BEGIN \ namespace boost { \ inline namespace ext { \ namespace sml { \ - inline namespace v1_1_5 { + inline namespace v1_1_6 { #define BOOST_SML_NAMESPACE_END \ } \ } \ @@ -1424,10 +1424,24 @@ struct sm_impl : aux::conditional_t::value, aux: } template , events_ids_t>::value && - !aux::is_base_of, events_ids_t>::value)> + !aux::is_base_of, events_ids_t>::value && + !aux::is_same, initial>::value)> bool process_internal_events(const TEvent &, TDeps &, TSubs &, Ts &&...) { return false; } + template , events_ids_t>::value && + !aux::is_base_of, events_ids_t>::value && + aux::is_same, initial>::value)> + bool process_internal_events(const TEvent &event, TDeps &deps, TSubs &subs) { + policies::log_process_event(aux::type{}, deps, event); +#if BOOST_SML_DISABLE_EXCEPTIONS + return process_event_impl, mappings>>(event, deps, subs, states_t{}, + aux::make_index_sequence{}); +#else + return process_event_except_imp, mappings>>(event, deps, subs, has_exceptions{}); +#endif + } template , events_ids_t>::value && !aux::is_base_of, events_ids_t>::value)> @@ -1706,7 +1720,7 @@ class sm { aux::get>(sub_sms_).start(deps_, sub_sms_); } template 1) && aux::is_unique_t::value)> - explicit sm(TDeps &&...deps) : deps_{aux::init{}, aux::pool{deps...}}, sub_sms_{aux::pool{deps...}} { + explicit sm(TDeps &&... deps) : deps_{aux::init{}, aux::pool{deps...}}, sub_sms_{aux::pool{deps...}} { aux::get>(sub_sms_).start(deps_, sub_sms_); } sm(aux::init, deps_t &deps) : deps_{deps}, sub_sms_{deps} { aux::get>(sub_sms_).start(deps_, sub_sms_); } @@ -2748,4 +2762,4 @@ BOOST_SML_NAMESPACE_END #undef __has_builtin__make_integer_seq #endif #endif -#endif \ No newline at end of file +#endif From 0dde9e2889f36bc102549a8f74a8c2da326d4354 Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Thu, 22 Sep 2022 12:45:17 +0200 Subject: [PATCH 123/130] :bug: (ActivityKit): Add missing #include to SuperSimon --- libs/ActivityKit/include/activities/SuperSimon.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/ActivityKit/include/activities/SuperSimon.h b/libs/ActivityKit/include/activities/SuperSimon.h index 95d163b5a4..dc8fa10176 100644 --- a/libs/ActivityKit/include/activities/SuperSimon.h +++ b/libs/ActivityKit/include/activities/SuperSimon.h @@ -6,6 +6,8 @@ // LCOV_EXCL_START +#include + #include "Color.h" #include "Number.h" #include "RFIDKit.h" From 655c5641fdf1a33df8541ca078ab109dcebd79b6 Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Thu, 22 Sep 2022 15:03:26 +0200 Subject: [PATCH 124/130] :construction_worker: (setup): Remove actions/setup-python it was causing CI to crash, see: https://github.com/actions/setup-python/issues/442 With ubuntu-22.04 the action is not needed anymore as the default Python version is 3.10.4 --- .github/actions/setup/action.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 6d14f913e1..e593909261 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -109,11 +109,6 @@ runs: # Mark: - Python/pip # - - name: Setup Python - uses: actions/setup-python@v2 - with: - python-version: "3.x" - - name: Cache pip dependencies id: global_cache-pip_dependencies uses: actions/cache@v2 @@ -121,9 +116,9 @@ runs: path: | ${{ env.RUNNER_HOME }}/.cache/pip ${{ env.pythonLocation }} - key: ${{ runner.os }}-global_cache-pip_dependencies-${{ env.pythonLocation }}-${{ hashFiles('config/mbed_version') }} + key: ${{ runner.os }}-global_cache-pip_dependencies-${{ hashFiles('config/mbed_version') }} restore-keys: | - ${{ runner.os }}-global_cache-pip_dependencies-${{ env.pythonLocation }}- + ${{ runner.os }}-global_cache-pip_dependencies- - name: Install pip packages shell: bash From d08d22ce2a437fdf31941ad478d4072f7a503cdf Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Fri, 16 Sep 2022 22:35:34 +0200 Subject: [PATCH 125/130] :sparkles: (BLEKit): Interface - Add onDataRequested method + update all services implementing the interface --- libs/BLEKit/include/BLEServiceBattery.h | 5 ++ libs/BLEKit/include/BLEServiceCommands.h | 5 ++ .../include/BLEServiceDeviceInformation.h | 5 ++ libs/BLEKit/include/BLEServiceFileReception.h | 5 ++ libs/BLEKit/include/BLEServiceMonitoring.h | 5 ++ libs/BLEKit/include/BLEServiceUpdate.h | 5 ++ .../include/CoreGattServerEventHandler.h | 1 + libs/BLEKit/include/internal/BLEService.h | 8 ++- .../source/CoreGattServerEventHandler.cpp | 15 +++++ libs/BLEKit/tests/BLEServiceBattery_test.cpp | 10 +++ .../BLEServiceDeviceInformation_test.cpp | 10 +++ .../tests/BLEServiceFileReception_test.cpp | 8 +++ .../tests/BLEServiceMonitoring_test.cpp | 8 +++ libs/BLEKit/tests/BLEServiceUpdate_test.cpp | 8 +++ .../tests/CoreGattServerEventHandler_test.cpp | 63 +++++++++++++++++++ tests/unit/mocks/mocks/leka/BLEService.h | 1 + 16 files changed, 159 insertions(+), 3 deletions(-) diff --git a/libs/BLEKit/include/BLEServiceBattery.h b/libs/BLEKit/include/BLEServiceBattery.h index 916d9568a8..171c212fa1 100644 --- a/libs/BLEKit/include/BLEServiceBattery.h +++ b/libs/BLEKit/include/BLEServiceBattery.h @@ -31,6 +31,11 @@ class BLEServiceBattery : public interface::BLEService // do nothing } + void onDataRequested(const data_requested_handle_t ¶ms) final + { + // do nothing + } + private: data_to_send_handle_t send_data_function; diff --git a/libs/BLEKit/include/BLEServiceCommands.h b/libs/BLEKit/include/BLEServiceCommands.h index 6896b3aa48..90ccff49bf 100644 --- a/libs/BLEKit/include/BLEServiceCommands.h +++ b/libs/BLEKit/include/BLEServiceCommands.h @@ -30,6 +30,11 @@ class BLEServiceCommands : public interface::BLEService _on_commands_received = callback; } + void onDataRequested(const data_requested_handle_t ¶ms) final + { + // do nothing + } + private: std::array _buffer {}; WriteOnlyArrayGattCharacteristic tx_characteristic { diff --git a/libs/BLEKit/include/BLEServiceDeviceInformation.h b/libs/BLEKit/include/BLEServiceDeviceInformation.h index b25b4654bb..a8d17d0534 100644 --- a/libs/BLEKit/include/BLEServiceDeviceInformation.h +++ b/libs/BLEKit/include/BLEServiceDeviceInformation.h @@ -40,6 +40,11 @@ class BLEServiceDeviceInformation : public interface::BLEService // do nothing } + void onDataRequested(const data_requested_handle_t ¶ms) final + { + // do nothing + } + private: inline static auto _manufacturer = utils::cast::from_c_string_to_uint8_t_array("APF France handicap"); diff --git a/libs/BLEKit/include/BLEServiceFileReception.h b/libs/BLEKit/include/BLEServiceFileReception.h index 7769dab4a3..dabeb3bd86 100644 --- a/libs/BLEKit/include/BLEServiceFileReception.h +++ b/libs/BLEKit/include/BLEServiceFileReception.h @@ -48,6 +48,11 @@ class BLEServiceFileReception : public interface::BLEService _on_file_data_callback = callback; } + void onDataRequested(const data_requested_handle_t ¶ms) final + { + // do nothing + } + private: std::array file_path {}; WriteOnlyArrayGattCharacteristic file_path_characteristic { diff --git a/libs/BLEKit/include/BLEServiceMonitoring.h b/libs/BLEKit/include/BLEServiceMonitoring.h index 282aecb975..64c59186c2 100644 --- a/libs/BLEKit/include/BLEServiceMonitoring.h +++ b/libs/BLEKit/include/BLEServiceMonitoring.h @@ -47,6 +47,11 @@ class BLEServiceMonitoring : public interface::BLEService void onSoftReboot(const std::function &callback) { _on_soft_reboot = callback; } + void onDataRequested(const data_requested_handle_t ¶ms) final + { + // do nothing + } + private: uint8_t charging_status {0x00}; ReadOnlyGattCharacteristic _charging_status_characteristic { diff --git a/libs/BLEKit/include/BLEServiceUpdate.h b/libs/BLEKit/include/BLEServiceUpdate.h index 0c2ceac260..b65ec39373 100644 --- a/libs/BLEKit/include/BLEServiceUpdate.h +++ b/libs/BLEKit/include/BLEServiceUpdate.h @@ -49,6 +49,11 @@ class BLEServiceUpdate : public interface::BLEService _on_factory_reset_notification_callback = callback; } + void onDataRequested(const data_requested_handle_t ¶ms) final + { + // do nothing + } + private: bool must_apply_update {false}; WriteOnlyGattCharacteristic apply_update_characteristic { diff --git a/libs/BLEKit/include/CoreGattServerEventHandler.h b/libs/BLEKit/include/CoreGattServerEventHandler.h index bba0e59bb2..1bce7ed54f 100644 --- a/libs/BLEKit/include/CoreGattServerEventHandler.h +++ b/libs/BLEKit/include/CoreGattServerEventHandler.h @@ -21,6 +21,7 @@ class CoreGattServerEventHandler : public ble::GattServer::EventHandler void setServices(std::span const &services); void onDataWritten(const GattWriteCallbackParams ¶ms) override; + void onDataRead(const GattReadCallbackParams ¶ms) override; private: std::span _services {}; diff --git a/libs/BLEKit/include/internal/BLEService.h b/libs/BLEKit/include/internal/BLEService.h index 0b1a64633f..4bda15dff9 100644 --- a/libs/BLEKit/include/internal/BLEService.h +++ b/libs/BLEKit/include/internal/BLEService.h @@ -22,10 +22,12 @@ class BLEService : public GattService virtual ~BLEService() = default; - using data_received_handle_t = GattWriteCallbackParams; - using data_to_send_handle_t = std::tuple>; + using data_received_handle_t = GattWriteCallbackParams; + using data_requested_handle_t = GattReadCallbackParams; + using data_to_send_handle_t = std::tuple>; - virtual void onDataReceived(const data_received_handle_t &handle) = 0; + virtual void onDataReceived(const data_received_handle_t &handle) = 0; + virtual void onDataRequested(const data_requested_handle_t &handle) = 0; void onDataReadyToSend(const std::function &callback) { diff --git a/libs/BLEKit/source/CoreGattServerEventHandler.cpp b/libs/BLEKit/source/CoreGattServerEventHandler.cpp index 4a37266f2b..c854f2e00e 100644 --- a/libs/BLEKit/source/CoreGattServerEventHandler.cpp +++ b/libs/BLEKit/source/CoreGattServerEventHandler.cpp @@ -26,3 +26,18 @@ void CoreGattServerEventHandler::onDataWritten(const GattWriteCallbackParams &pa std::for_each(_services.begin(), _services.end(), on_data_received); } + +void CoreGattServerEventHandler::onDataRead(const GattReadCallbackParams ¶ms) +{ + auto on_data_requested = [¶ms](interface::BLEService *service) { + auto characteristics_count = service->getCharacteristicCount(); + + for (uint8_t index = 0; index < characteristics_count; index++) { + if (params.handle == service->getCharacteristic(index)->getValueHandle()) { + service->onDataRequested(params); + } + } + }; + + std::for_each(_services.begin(), _services.end(), on_data_requested); +} diff --git a/libs/BLEKit/tests/BLEServiceBattery_test.cpp b/libs/BLEKit/tests/BLEServiceBattery_test.cpp index 4edf5e6063..6dcde97e58 100644 --- a/libs/BLEKit/tests/BLEServiceBattery_test.cpp +++ b/libs/BLEKit/tests/BLEServiceBattery_test.cpp @@ -58,3 +58,13 @@ TEST(BLEServiceBatteryTest, onDataReceived) // nothing expected } + +TEST(BLEServiceBatteryTest, onDataRequested) +{ + auto service_battery = BLEServiceBattery {}; + + auto dummy_params = BLEServiceBattery::data_requested_handle_t {}; + service_battery.onDataRequested(dummy_params); + + // nothing expected +} diff --git a/libs/BLEKit/tests/BLEServiceDeviceInformation_test.cpp b/libs/BLEKit/tests/BLEServiceDeviceInformation_test.cpp index c4c7eba1ea..ae4d295a99 100644 --- a/libs/BLEKit/tests/BLEServiceDeviceInformation_test.cpp +++ b/libs/BLEKit/tests/BLEServiceDeviceInformation_test.cpp @@ -71,3 +71,13 @@ TEST(BLEServiceDeviceInformationTest, setOSVersion) EXPECT_EQ(actual_os_version.at(i), expected_os_version[i]); } } + +TEST(BLEServiceDeviceInformationTest, onDataRequested) +{ + auto service_device_information = BLEServiceDeviceInformation {}; + + auto dummy_params = BLEServiceDeviceInformation::data_requested_handle_t {}; + service_device_information.onDataRequested(dummy_params); + + // nothing expected +} diff --git a/libs/BLEKit/tests/BLEServiceFileReception_test.cpp b/libs/BLEKit/tests/BLEServiceFileReception_test.cpp index be0ec4b90f..02adef9556 100644 --- a/libs/BLEKit/tests/BLEServiceFileReception_test.cpp +++ b/libs/BLEKit/tests/BLEServiceFileReception_test.cpp @@ -40,6 +40,7 @@ class BLEServiceFileReceptionTest : public testing::Test std::array default_expected_file_path {}; BLEServiceFileReception::data_received_handle_t data_received_handle {}; + BLEServiceFileReception::data_requested_handle_t data_requested_handle {}; void onDataReceivedProcess(std::span data) { @@ -134,3 +135,10 @@ TEST_F(BLEServiceFileReceptionTest, onFileDataReceivedCallbackNotSameHandle) onDataReceivedProcess(sent_data); } + +TEST_F(BLEServiceFileReceptionTest, onDataRequested) +{ + service_file_reception.onDataRequested(data_requested_handle); + + // nothing expected +} diff --git a/libs/BLEKit/tests/BLEServiceMonitoring_test.cpp b/libs/BLEKit/tests/BLEServiceMonitoring_test.cpp index cafdd66544..bdc03452e2 100644 --- a/libs/BLEKit/tests/BLEServiceMonitoring_test.cpp +++ b/libs/BLEKit/tests/BLEServiceMonitoring_test.cpp @@ -20,6 +20,7 @@ class BLEServiceMonitoringTest : public testing::Test BLEServiceMonitoring service_monitoring {}; BLEServiceMonitoring::data_received_handle_t data_received_handle {}; + BLEServiceMonitoring::data_requested_handle_t data_requested_handle {}; bool default_is_screensaver_enable {true}; @@ -134,3 +135,10 @@ TEST_F(BLEServiceMonitoringTest, onSoftRebootNotSameHandle) onDataReceivedProcess(&sent_value_data); } + +TEST_F(BLEServiceMonitoringTest, onDataRequested) +{ + service_monitoring.onDataRequested(data_requested_handle); + + // nothing expected +} diff --git a/libs/BLEKit/tests/BLEServiceUpdate_test.cpp b/libs/BLEKit/tests/BLEServiceUpdate_test.cpp index fabe5e992c..503bbea82b 100644 --- a/libs/BLEKit/tests/BLEServiceUpdate_test.cpp +++ b/libs/BLEKit/tests/BLEServiceUpdate_test.cpp @@ -20,6 +20,7 @@ class BLEServiceUpdateTest : public testing::Test BLEServiceUpdate service_update {}; BLEServiceUpdate::data_received_handle_t data_received_handle {}; + BLEServiceUpdate::data_requested_handle_t data_requested_handle {}; bool default_request_update_sent {false}; FirmwareVersion default_version {0x00, 0x00, 0x0000}; @@ -232,3 +233,10 @@ TEST_F(BLEServiceUpdateTest, getVersionRevisionNotSameHandle) EXPECT_EQ(actual_version.revision, expected_version_revision); EXPECT_NE(actual_version.revision, sent_value); } + +TEST_F(BLEServiceUpdateTest, onDataRequested) +{ + service_update.onDataRequested(data_requested_handle); + + // nothing expected +} diff --git a/libs/BLEKit/tests/CoreGattServerEventHandler_test.cpp b/libs/BLEKit/tests/CoreGattServerEventHandler_test.cpp index c54b541761..e663b2ddc6 100644 --- a/libs/BLEKit/tests/CoreGattServerEventHandler_test.cpp +++ b/libs/BLEKit/tests/CoreGattServerEventHandler_test.cpp @@ -101,3 +101,66 @@ TEST_F(CoreGattServerEventHandlerTest, onDataReceivedParamsHandleDifferent) gatt_event_handler.onDataWritten(params); } + +TEST_F(CoreGattServerEventHandlerTest, onDataRequested) +{ + auto characteristic_value = uint8_t {0}; + auto characteristic = GattCharacteristic {0x1234, &characteristic_value}; + auto service_characteristic_table = std::to_array({&characteristic}); + auto mock_service = mock::BLEService(0x01, service_characteristic_table); + + auto services = std::to_array({&mock_service}); + + gatt_event_handler.setServices(services); + + auto expected_params = GattReadCallbackParams {}; + expected_params.handle = characteristic.getValueHandle(); + + EXPECT_CALL(mock_service, onDataRequested(compareParams(expected_params))) + .Times(std::size(service_characteristic_table)); + + gatt_event_handler.onDataRead(expected_params); +} + +TEST_F(CoreGattServerEventHandlerTest, onDataRequestedMultipleServices) +{ + auto characteristic_value = uint8_t {}; + auto characteristic = GattCharacteristic {0x1234, &characteristic_value}; + auto service_1_characteristic_table = std::to_array({&characteristic}); + auto service_2_characteristic_table = std::to_array({&characteristic, &characteristic}); + auto mock_service_1 = mock::BLEService(0x01, service_1_characteristic_table); + auto mock_service_2 = mock::BLEService(0x02, service_2_characteristic_table); + + auto services = std::to_array({&mock_service_1, &mock_service_2}); + + gatt_event_handler.setServices(services); + + auto expected_params = GattReadCallbackParams {}; + expected_params.handle = characteristic.getValueHandle(); + + EXPECT_CALL(mock_service_1, onDataRequested(compareParams(expected_params))) + .Times(std::size(service_1_characteristic_table)); + EXPECT_CALL(mock_service_2, onDataRequested(compareParams(expected_params))) + .Times(std::size(service_2_characteristic_table)); + + gatt_event_handler.onDataRead(expected_params); +} + +TEST_F(CoreGattServerEventHandlerTest, onDataRequestedParamsHandleDifferent) +{ + auto characteristic_value = uint8_t {}; + auto characteristic = GattCharacteristic {0x1234, &characteristic_value}; + auto service_characteristic_table = std::to_array({&characteristic}); + auto mock_service = mock::BLEService(0x01, service_characteristic_table); + + auto services = std::to_array({&mock_service}); + + gatt_event_handler.setServices(services); + + auto params = GattReadCallbackParams {}; + params.handle = characteristic.getValueHandle() + 1; + + EXPECT_CALL(mock_service, onDataRequested).Times(0); + + gatt_event_handler.onDataRead(params); +} diff --git a/tests/unit/mocks/mocks/leka/BLEService.h b/tests/unit/mocks/mocks/leka/BLEService.h index 2822cab135..d28a7fb9f5 100644 --- a/tests/unit/mocks/mocks/leka/BLEService.h +++ b/tests/unit/mocks/mocks/leka/BLEService.h @@ -18,6 +18,7 @@ class BLEService : public interface::BLEService } MOCK_METHOD(void, onDataReceived, (const data_received_handle_t ¶ms), (override)); + MOCK_METHOD(void, onDataRequested, (const data_requested_handle_t ¶ms), (override)); }; } // namespace leka::mock From 1ef437bb56570bff356a511c0b42848cf37684a3 Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Fri, 16 Sep 2022 22:51:58 +0200 Subject: [PATCH 126/130] :sparkles: (RobotKit): Link BLE and FileReception to get SHA256 --- libs/BLEKit/include/BLEServiceFileReception.h | 24 ++++++++-- .../internal/ServicesCharacteristics.h | 1 + .../tests/BLEServiceFileReception_test.cpp | 47 ++++++++++++++++++- libs/RobotKit/include/RobotController.h | 5 ++ 4 files changed, 72 insertions(+), 5 deletions(-) diff --git a/libs/BLEKit/include/BLEServiceFileReception.h b/libs/BLEKit/include/BLEServiceFileReception.h index dabeb3bd86..85999d1799 100644 --- a/libs/BLEKit/include/BLEServiceFileReception.h +++ b/libs/BLEKit/include/BLEServiceFileReception.h @@ -14,6 +14,12 @@ class BLEServiceFileReception : public interface::BLEService public: BLEServiceFileReception() : interface::BLEService(service::receive_file::uuid, _characteristic_table) {}; + void setFileSHA256(std::array sha256) const + { + auto data = std::make_tuple(file_sha256_characteristic.getValueHandle(), sha256); + sendData(data); + } + void onDataReceived(const data_received_handle_t ¶ms) final { if (params.handle == file_path_characteristic.getValueHandle()) { @@ -48,9 +54,16 @@ class BLEServiceFileReception : public interface::BLEService _on_file_data_callback = callback; } + void onFileSHA256Requested(const std::function)> &callback) + { + _on_file_sha256_callback = callback; + } + void onDataRequested(const data_requested_handle_t ¶ms) final { - // do nothing + if (params.handle == file_sha256_characteristic.getValueHandle() && _on_file_sha256_callback != nullptr) { + _on_file_sha256_callback(file_path); + } } private: @@ -62,11 +75,16 @@ class BLEServiceFileReception : public interface::BLEService WriteOnlyArrayGattCharacteristic file_reception_buffer_characteristic { service::receive_file::characteristic::file_reception_buffer, file_reception_buffer.begin()}; + std::array file_sha256 {}; + ReadOnlyArrayGattCharacteristic file_sha256_characteristic { + service::receive_file::characteristic::file_sha256, file_sha256.begin()}; + std::function)> _on_file_data_callback {}; std::function)> _on_file_path_callback {}; + std::function)> _on_file_sha256_callback {}; - std::array _characteristic_table {&file_path_characteristic, - &file_reception_buffer_characteristic}; + std::array _characteristic_table { + &file_path_characteristic, &file_reception_buffer_characteristic, &file_sha256_characteristic}; }; } // namespace leka diff --git a/libs/BLEKit/include/internal/ServicesCharacteristics.h b/libs/BLEKit/include/internal/ServicesCharacteristics.h index 360caf9a24..365d31e55a 100644 --- a/libs/BLEKit/include/internal/ServicesCharacteristics.h +++ b/libs/BLEKit/include/internal/ServicesCharacteristics.h @@ -52,6 +52,7 @@ namespace receive_file { namespace characteristic { inline constexpr uint16_t file_path = 0x7080; inline constexpr uint16_t file_reception_buffer = 0x8283; + inline constexpr uint16_t file_sha256 = 0x7083; } // namespace characteristic } // namespace receive_file diff --git a/libs/BLEKit/tests/BLEServiceFileReception_test.cpp b/libs/BLEKit/tests/BLEServiceFileReception_test.cpp index 02adef9556..385c4b8ffc 100644 --- a/libs/BLEKit/tests/BLEServiceFileReception_test.cpp +++ b/libs/BLEKit/tests/BLEServiceFileReception_test.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "BLEServiceFileReception.h" +#include #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -136,9 +137,51 @@ TEST_F(BLEServiceFileReceptionTest, onFileDataReceivedCallbackNotSameHandle) onDataReceivedProcess(sent_data); } -TEST_F(BLEServiceFileReceptionTest, onDataRequested) +TEST_F(BLEServiceFileReceptionTest, onFileSHA256Requested) { + testing::MockFunction)> mock_callback {}; + service_file_reception.onFileSHA256Requested(mock_callback.AsStdFunction()); + + EXPECT_CALL(mock_callback, Call).Times(1); + + service_file_reception.onDataRequested(data_requested_handle); +} + +TEST_F(BLEServiceFileReceptionTest, onFileSHA256RequestedNotSameHandle) +{ + testing::MockFunction)> mock_callback {}; + service_file_reception.onFileSHA256Requested(mock_callback.AsStdFunction()); + + data_requested_handle.handle = 0xFFFF; + + EXPECT_CALL(mock_callback, Call).Times(0); + + service_file_reception.onDataRequested(data_requested_handle); +} + +TEST_F(BLEServiceFileReceptionTest, onFileSHA256RequestedtUnset) +{ + service_file_reception.onFileSHA256Requested(nullptr); + service_file_reception.onDataRequested(data_requested_handle); +} + +TEST_F(BLEServiceFileReceptionTest, setFileSHA256) +{ + std::array expected_sha256 { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + }; + std::array actual_sha256 {}; + + auto spy_callback = [&actual_sha256](const BLEServiceFileReception::data_to_send_handle_t &handle) { + for (auto i = 0; i < std::size(actual_sha256); i++) { + actual_sha256.at(i) = std::get<1>(handle)[i]; + } + }; + + service_file_reception.onDataReadyToSend(spy_callback); - // nothing expected + service_file_reception.setFileSHA256(expected_sha256); + EXPECT_EQ(actual_sha256, expected_sha256); } diff --git a/libs/RobotKit/include/RobotController.h b/libs/RobotKit/include/RobotController.h index 3e4db51273..5da85d18fb 100644 --- a/libs/RobotKit/include/RobotController.h +++ b/libs/RobotKit/include/RobotController.h @@ -377,6 +377,11 @@ class RobotController : public interface::RobotController [this](std::span path) { file_reception.setFilePath(path.data()); }); _service_file_reception.onFileDataReceived( [this](std::span buffer) { file_reception.onPacketReceived(buffer); }); + _service_file_reception.onFileSHA256Requested([this](std::span path) { + if (FileManagerKit::File file {path.data()}; file.is_open()) { + _service_file_reception.setFileSHA256(file.getSHA256()); + } + }); auto on_update_requested = [this]() { raise(event::update_requested {}); }; _service_update.onUpdateRequested(on_update_requested); From faee30872dbe50a327c59d56497122f468b554cf Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Thu, 22 Sep 2022 15:52:49 +0200 Subject: [PATCH 127/130] :construction_worker: (actions): Update actions/cache to v3 --- .github/actions/setup/action.yml | 6 +++--- .github/workflows/ci-code_analysis-sonarcloud.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index e593909261..dfaee56b48 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -56,7 +56,7 @@ runs: - name: Cache ARM GCC Toolchain id: cache_arm_toolchain - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ env.RUNNER_HOME }}/gcc-arm-none-eabi key: ${{ runner.os }}-global_cache-arm_toolchain-${{ hashFiles('config/toolchain_gcc_arm_none_eabi_url') }} @@ -98,7 +98,7 @@ runs: - name: Cache ccache id: global_cache-ccache - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ env.CCACHE_DIR}} key: ${{ runner.os }}-${{ inputs.ccache_name }}-ccache-${{ env.CACHE_NOW_TIME }} @@ -111,7 +111,7 @@ runs: - name: Cache pip dependencies id: global_cache-pip_dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: | ${{ env.RUNNER_HOME }}/.cache/pip diff --git a/.github/workflows/ci-code_analysis-sonarcloud.yml b/.github/workflows/ci-code_analysis-sonarcloud.yml index d978b9a8e3..4398d1f75e 100644 --- a/.github/workflows/ci-code_analysis-sonarcloud.yml +++ b/.github/workflows/ci-code_analysis-sonarcloud.yml @@ -51,7 +51,7 @@ jobs: - name: Cache sonarcloud cache id: global_cache-sonarcloud - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ env.SONARCLOUD_CACHE_DIR}} key: ${{ runner.os }}-global_cache-sonarcloud-${{ github.event.number }}-${{ env.CACHE_NOW_TIME }} From 4fe79dd02d4577d6dd4977422dc29b4ed45d7626 Mon Sep 17 00:00:00 2001 From: Ladislas de Toldi Date: Thu, 22 Sep 2022 15:54:33 +0200 Subject: [PATCH 128/130] :construction_worker: (sonarcloud): Update sonar-scanner to v4.7.0.2747 --- .github/actions/setup/setup_env.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup/setup_env.sh b/.github/actions/setup/setup_env.sh index 5ac14764d7..8350fc43b4 100755 --- a/.github/actions/setup/setup_env.sh +++ b/.github/actions/setup/setup_env.sh @@ -76,7 +76,7 @@ echo "ARM_TOOLCHAIN_URL=$ARM_TOOLCHAIN_URL" >> $GITHUB_ENV # MARK: - SonarCloud # -SONARCLOUD_CLI_URL="https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.6.2.2472.zip" +SONARCLOUD_CLI_URL="https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.7.0.2747.zip" SONARCLOUD_CLI_FILENAME="sonar-scanner-cli-*.zip" SONARCLOUD_CLI_EXTRACT_DIRECTORY="sonar-scanner-*" From 78cbf1ff8bc60742b043b64e70d107ebf5bea30c Mon Sep 17 00:00:00 2001 From: Yann Locatelli Date: Mon, 26 Sep 2022 11:45:40 +0200 Subject: [PATCH 129/130] :bookmark: (release): Bump to v1.2.0 --- config/os_version | 2 +- libs/RobotKit/include/RobotController.h | 2 +- libs/RobotKit/tests/RobotController_test.h | 2 +- .../tests/RobotController_test_initializeComponents.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config/os_version b/config/os_version index 9084fa2f71..26aaba0e86 100644 --- a/config/os_version +++ b/config/os_version @@ -1 +1 @@ -1.1.0 +1.2.0 diff --git a/libs/RobotKit/include/RobotController.h b/libs/RobotKit/include/RobotController.h index 5da85d18fb..94f06ff0a6 100644 --- a/libs/RobotKit/include/RobotController.h +++ b/libs/RobotKit/include/RobotController.h @@ -245,7 +245,7 @@ class RobotController : public interface::RobotController auto _serial_number = _serialnumberkit.getSerialNumber(); _service_device_information.setSerialNumber(_serial_number); - auto _os_version = _firmware_update.getCurrentVersion(); + auto _os_version = FirmwareVersion {.major = 1, .minor = 2, .revision = 0}; _service_device_information.setOSVersion(_os_version); auto advertising_data = _ble.getAdvertisingData(); diff --git a/libs/RobotKit/tests/RobotController_test.h b/libs/RobotKit/tests/RobotController_test.h index 8abeb151c9..7c154847ac 100644 --- a/libs/RobotKit/tests/RobotController_test.h +++ b/libs/RobotKit/tests/RobotController_test.h @@ -136,7 +136,7 @@ class RobotControllerTest : public testing::Test EXPECT_CALL(mock_mcu, getID).Times(1); EXPECT_CALL(mbed_mock_gatt, write(_, _, _, _)).Times(1); - EXPECT_CALL(firmware_update, getCurrentVersion).Times(1); + // EXPECT_CALL(firmware_update, getCurrentVersion).Times(1); EXPECT_CALL(mbed_mock_gatt, write(_, _, _, _)).Times(1); Sequence set_serial_number_as_ble_device_name; diff --git a/libs/RobotKit/tests/RobotController_test_initializeComponents.cpp b/libs/RobotKit/tests/RobotController_test_initializeComponents.cpp index c94f874492..4e7758bf8f 100644 --- a/libs/RobotKit/tests/RobotController_test_initializeComponents.cpp +++ b/libs/RobotKit/tests/RobotController_test_initializeComponents.cpp @@ -22,7 +22,7 @@ TEST_F(RobotControllerTest, initializeComponents) // TODO: Specify which BLE service and what is expected if necessary EXPECT_CALL(mbed_mock_gatt, write(_, _, _, _)).Times(1); - EXPECT_CALL(firmware_update, getCurrentVersion).Times(1); + // EXPECT_CALL(firmware_update, getCurrentVersion).Times(1); EXPECT_CALL(mbed_mock_gatt, write(_, _, _, _)).Times(1); Sequence set_serial_number_as_ble_device_name; From 512b191ead94e35e660cfb5f32dca9c4792a4aba Mon Sep 17 00:00:00 2001 From: Hugo Pezziardi Date: Mon, 26 Sep 2022 14:49:22 +0200 Subject: [PATCH 130/130] :bug: (supersimon): Move increment before last round check --- libs/ActivityKit/include/activities/SuperSimon.h | 1 - libs/ActivityKit/source/activities/SuperSimon.cpp | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/libs/ActivityKit/include/activities/SuperSimon.h b/libs/ActivityKit/include/activities/SuperSimon.h index dc8fa10176..ce4a3ff117 100644 --- a/libs/ActivityKit/include/activities/SuperSimon.h +++ b/libs/ActivityKit/include/activities/SuperSimon.h @@ -9,7 +9,6 @@ #include #include "Color.h" -#include "Number.h" #include "RFIDKit.h" #include "ReinforcerKit.h" #include "interface/Activity.h" diff --git a/libs/ActivityKit/source/activities/SuperSimon.cpp b/libs/ActivityKit/source/activities/SuperSimon.cpp index 84152791fd..8d468c078d 100644 --- a/libs/ActivityKit/source/activities/SuperSimon.cpp +++ b/libs/ActivityKit/source/activities/SuperSimon.cpp @@ -47,13 +47,13 @@ void SuperSimon::processCard(const MagicCard &card) _reinforcerkit.playDefault(); rtos::ThisThread::sleep_for(1s); _expected_color_index = 0; + ++_current_round; - if (_current_round >= _last_round) { + if (_current_round == _last_round) { _backup_callback(MagicCard::dice_roll); return; } - ++_current_round; launchNextRound(); return; }