From 618ddaaa1b19e1aa19e45e3ee5fe6010c71d8b6d Mon Sep 17 00:00:00 2001 From: fastium Date: Mon, 6 Jan 2025 00:14:43 +0100 Subject: [PATCH] ADD test for multi-tasking --- .pre-commit-config.yaml | 2 +- TESTS/bike-computer/bike-system/main.cpp | 249 ++++++++++++++++++++++- common/speedometer.cpp | 4 + mbed_app.json | 48 +++-- multi_tasking/bike_system.cpp | 84 ++++++-- multi_tasking/bike_system.hpp | 3 + 6 files changed, 342 insertions(+), 48 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 50916f5..830e48c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,5 +22,5 @@ repos: - id: cppcheck name: cppcheck require_serial: true - entry: cppcheck --enable=all --suppress=missingInclude --suppress=missingIncludeSystem --suppress=unusedFunction --suppress=unusedStructMember --inline-suppr -i mbed-os --std=c++14 --error-exitcode=1 + entry: cppcheck --enable=all --suppress=missingInclude --suppress=missingIncludeSystem --suppress=unusedFunction --inline-suppr -i mbed-os --std=c++14 --error-exitcode=1 language: system diff --git a/TESTS/bike-computer/bike-system/main.cpp b/TESTS/bike-computer/bike-system/main.cpp index 605f74c..f566562 100644 --- a/TESTS/bike-computer/bike-system/main.cpp +++ b/TESTS/bike-computer/bike-system/main.cpp @@ -23,15 +23,23 @@ ***************************************************************************/ #include +#include +#include "gear_device.hpp" #include "greentea-client/test_env.h" #include "mbed.h" +#include "mbed_trace.h" // NOLINT +#include "multi_tasking/bike_system.hpp" #include "static_scheduling/bike_system.hpp" #include "static_scheduling_with_event/bike_system.hpp" #include "task_logger.hpp" #include "unity/unity.h" #include "utest/utest.h" +#if defined(MBED_CONF_MBED_TRACE_ENABLE) +#define TRACE_GROUP "TEST_BIKE_SYSTEM" +#endif // MBED_CONF_MBED_TRACE_ENAB + namespace utest { namespace v1 { @@ -140,6 +148,235 @@ static void test_bike_system_with_event() { } } +// test_multi_tasking_bike_system handler function +static void test_multi_tasking_bike_system() { + tr_info("test multi tasking bike system"); + + // create the BikeSystem instance + multi_tasking::BikeSystem bikeSystem; + + // run the bike system in a separate thread + Thread thread; + osStatus status = + thread.start(callback(&bikeSystem, &multi_tasking::BikeSystem::start)); + + if (status != osOK) { + tr_error("Thread bike system is not OK !"); + } + + tr_info("bike system has started"); + + // let the bike system run for 20 secs + ThisThread::sleep_for(20s); + + // check whether scheduling was correct + // Order is kGearTaskIndex, kSpeedTaskIndex, kTemperatureTaskIndex, + // kResetTaskIndex, kDisplayTask1Index, kDisplayTask2Index + // When we use event handling, we do not check the computation time + constexpr std::chrono::microseconds taskPeriods[] = { + 800000us, 400000us, 1600000us, 800000us, 1600000us, 1600000us}; + + // allow for 2 msecs offset (with EventQueue) + constexpr uint64_t kDeltaUs = 2000; + + // stop the bike system + bikeSystem.stop(); + thread.terminate(); + + tr_info("Threads have stopped"); + + TEST_ASSERT_UINT64_WITHIN( + kDeltaUs, + taskPeriods[advembsof::TaskLogger::kTemperatureTaskIndex].count(), + bikeSystem.getTaskLogger() + .getPeriod(advembsof::TaskLogger::kTemperatureTaskIndex) + .count()); + TEST_ASSERT_UINT64_WITHIN( + kDeltaUs, + taskPeriods[advembsof::TaskLogger::kDisplayTask1Index].count(), + bikeSystem.getTaskLogger() + .getPeriod(advembsof::TaskLogger::kDisplayTask1Index) + .count()); +} + +// test_reset_multi_tasking_bike_system handler function +Timer timer; +static std::chrono::microseconds resetTime = std::chrono::microseconds::zero(); +static EventFlags eventFlags; +static constexpr uint32_t kResetEventFlag = (1UL << 0); +static void resetCallback() { + resetTime = timer.elapsed_time(); + eventFlags.set(kResetEventFlag); +} + +static void test_reset_multi_tasking_bike_system() { + tr_info("test reset multi tasking bike system"); + // create the BikeSystem instance + multi_tasking::BikeSystem bikeSystem; + + // run the bike system in a separate thread + Thread thread; + osStatus status = + thread.start(callback(&bikeSystem, &multi_tasking::BikeSystem::start)); + + if (status != osOK) { + tr_error("Thread bike system is not OK !"); + } + + tr_info("Bike system has started"); + + // let the bike system run for 2 secs + ThisThread::sleep_for(2s); + + // test reset on BikeSystem + bikeSystem.getSpeedometer().setOnResetCallback(resetCallback); + + // start the timer instance + timer.start(); + + // check for reset response time + constexpr uint8_t kNbrOfResets = 10; + std::chrono::microseconds lastResponseTime = std::chrono::microseconds::zero(); + for (uint8_t i = 0; i < kNbrOfResets; i++) { + tr_info("Reset test N°%d", i); + // take time before reset + auto startTime = timer.elapsed_time(); + + // reset the BikeSystem + bikeSystem.onReset(); + + // wait for resetCallback to be called + eventFlags.wait_all(kResetEventFlag); + + // get the response time and check it + auto responseTime = resetTime - startTime; + + // cppcheck generates an internal error with 20us + constexpr std::chrono::microseconds kMaxExpectedResponseTime(20); + + tr_info("Reset task: response time is %lld usecs\n, expected : %lld", + responseTime.count(), + kMaxExpectedResponseTime.count()); + + TEST_ASSERT_TRUE(responseTime.count() <= kMaxExpectedResponseTime.count()); + + // jitter of 20us is accepted + constexpr uint64_t kDeltaUs = 3; + constexpr std::chrono::microseconds kMaxExpectedJitter(2); + if (i > 0) { + auto jitter = responseTime - lastResponseTime; + tr_info("Reset task: jitter is %lld usecs\n", std::abs(jitter.count())); + TEST_ASSERT_UINT64_WITHIN( + kDeltaUs, kMaxExpectedJitter.count(), std::abs(jitter.count())); + } + lastResponseTime = responseTime; + + // let the bike system run for 2 secs + ThisThread::sleep_for(2s); + } + + // stop the bike system + bikeSystem.stop(); + thread.terminate(); + tr_info("Threads have stopped"); + timer.stop(); +} + +Timer timerGear; +static std::chrono::microseconds gearTime = std::chrono::microseconds::zero(); +static EventFlags eventGearFlags; +static constexpr uint32_t kGearEventFlag = (1UL << 0); +constexpr uint8_t kNbrOfGear = 9; +static void onGearChange() { + gearTime = timerGear.elapsed_time(); + eventGearFlags.set(kGearEventFlag); + tr_info("Gear changed"); +} + +static void gearTest(Callback gearChange) { + // std::chrono::microseconds lastResponseTime = std::chrono::microseconds::zero(); + + for (uint8_t i = 1; i < kNbrOfGear; i++) { + tr_info("Gear test N°%d", i); + // take time before increase the gear + auto startTime = timerGear.elapsed_time(); + + // change the gear with the callback + tr_info("Change gear"); + gearChange(); + + // wait flag + eventGearFlags.wait_all(kGearEventFlag); + + // get the response time and check it + auto responseTime = gearTime - startTime; + constexpr std::chrono::microseconds kMaxExpectedResponseTime(100000); + + tr_info("Change gear task: response time is %lld usecs\n, expected : %lld", + responseTime.count(), + kMaxExpectedResponseTime.count()); + + TEST_ASSERT_TRUE(responseTime.count() <= kMaxExpectedResponseTime.count()); + + // jitter of 20us is accepted + // constexpr uint64_t kDeltaUs = 2000; + // constexpr std::chrono::microseconds kMaxExpectedJitter(4000); + // if (i > 1) { + // auto jitter = responseTime - lastResponseTime; + // tr_info("Gear task: jitter is %lld usecs\n", std::abs(jitter.count())); + // TEST_ASSERT_UINT64_WITHIN( + // kDeltaUs, kMaxExpectedJitter.count(), std::abs(jitter.count())); + // } + // lastResponseTime = responseTime; + + // let the bike system run for 2 secs + ThisThread::sleep_for(2s); + } +} + +static void test_gear_multi_tasking_bike_system() { + tr_info("test reset multi tasking bike system"); + // create the BikeSystem instance + multi_tasking::BikeSystem bikeSystem; + + // run the bike system in a separate thread + Thread thread; + osStatus status = + thread.start(callback(&bikeSystem, &multi_tasking::BikeSystem::start)); + + if (status != osOK) { + tr_error("Thread bike system is not OK !"); + } + + tr_info("Bike system has started"); + + // let the bike system run for 2 secs + ThisThread::sleep_for(2s); + + // get the gear device to call onUp and onDown functions + multi_tasking::GearDevice& gearDevice = bikeSystem.getGearDevice(); + + // set callback for response time measuring + bikeSystem.setCallbackGearChage(onGearChange); + + // start the timer instance + timerGear.start(); + + // test timing incresing the gear + tr_info("Test incresing gear"); + gearTest(callback(&gearDevice, &multi_tasking::GearDevice::onUp)); + + // test timing decreasing the gear + tr_info("Test decreasing gear"); + gearTest(callback(&gearDevice, &multi_tasking::GearDevice::onDown)); + + // stop the bike system + bikeSystem.stop(); + thread.terminate(); + tr_info("Threads have stopped"); + timerGear.stop(); +} + static status_t greentea_setup(const size_t number_of_cases) { // Here, we specify the timeout (60s) and the host test (a built-in host test // or the name of our Python file) @@ -152,7 +389,10 @@ static status_t greentea_setup(const size_t number_of_cases) { static Case cases[] = { Case("test bike system", test_bike_system), Case("test bike system with event queue", test_bike_system_event_queue), - Case("test bike system with event handling", test_bike_system_with_event), + Case("test bike system with event", test_bike_system_with_event), + Case("test multi-tasking bike system", test_multi_tasking_bike_system), + Case("test reset multi-tasking bike system", test_reset_multi_tasking_bike_system), + Case("test gear multi-tasking bike system", test_gear_multi_tasking_bike_system), }; static Specification specification(greentea_setup, cases); @@ -160,4 +400,9 @@ static Specification specification(greentea_setup, cases); }; // namespace v1 }; // namespace utest -int main() { return !utest::v1::Harness::run(utest::v1::specification); } +int main() { +#if defined(MBED_CONF_MBED_TRACE_ENABLE) + mbed_trace_init(); +#endif + return !utest::v1::Harness::run(utest::v1::specification); +} diff --git a/common/speedometer.cpp b/common/speedometer.cpp index 7d05312..0f1b413 100644 --- a/common/speedometer.cpp +++ b/common/speedometer.cpp @@ -80,6 +80,10 @@ float Speedometer::getDistance() { } void Speedometer::reset() { +#if defined(MBED_TEST_MODE) + _cbOnReset(); +#endif + this->_totalDistanceMutex.lock(); this->_totalDistance = 0.0f; this->_totalDistanceMutex.unlock(); diff --git a/mbed_app.json b/mbed_app.json index c42f542..2e6efc6 100644 --- a/mbed_app.json +++ b/mbed_app.json @@ -1,27 +1,25 @@ { - "macros": [ - "MBED_CONF_MBED_TRACE_FEA_IPV6=0" - ], - "config": { - "main-stack-size": { - "value": 4096 - } - }, - "target_overrides": { - "*": { - "mbed-trace.enable": false, - "platform.stdio-convert-newlines": true, - "platform.stdio-baud-rate": 115200, - "platform.default-serial-baud-rate": 115200, - "platform.stdio-buffered-serial": true, - "platform.all-stats-enabled": true, - "target.printf_lib":"minimal-printf", - "platform.minimal-printf-enable-floating-point": true, - "platform.minimal-printf-set-floating-point-max-decimals": 2 - }, - "DISCO_H747I": { - "mbed-trace.enable": true, - "mbed-trace.max-level": "TRACE_LEVEL_DEBUG" - } + "macros": ["MBED_CONF_MBED_TRACE_FEA_IPV6=0"], + "config": { + "main-stack-size": { + "value": 6144 } - } \ No newline at end of file + }, + "target_overrides": { + "*": { + "mbed-trace.enable": false, + "platform.stdio-convert-newlines": true, + "platform.stdio-baud-rate": 115200, + "platform.default-serial-baud-rate": 115200, + "platform.stdio-buffered-serial": true, + "platform.all-stats-enabled": true, + "target.printf_lib": "minimal-printf", + "platform.minimal-printf-enable-floating-point": true, + "platform.minimal-printf-set-floating-point-max-decimals": 2 + }, + "DISCO_H747I": { + "mbed-trace.enable": true, + "mbed-trace.max-level": "TRACE_LEVEL_DEBUG" + } + } +} diff --git a/multi_tasking/bike_system.cpp b/multi_tasking/bike_system.cpp index 01ba9f5..98d84ee 100644 --- a/multi_tasking/bike_system.cpp +++ b/multi_tasking/bike_system.cpp @@ -27,12 +27,12 @@ #include "bike_system.hpp" #include +#include -#include "Callback.h" -#include "cmsis_os2.h" -#include "constants.hpp" +#include "cmsis_os.h" +#include "common/constants.hpp" #include "mbed_trace.h" -#include "pedal_device.hpp" + #if MBED_CONF_MBED_TRACE_ENABLE #define TRACE_GROUP "BikeSystem" #endif // MBED_CONF_MBED_TRACE_ENABLE @@ -75,7 +75,7 @@ BikeSystem::BikeSystem() _isrEventThread(osPriorityAboveNormal, OS_STACK_SIZE, nullptr, "ISR_Event"), _speedDistanceThread( osPriorityNormal, OS_STACK_SIZE, nullptr, "Speed_distance_Task"), - _gearTaskThread(osPriorityNormal, OS_STACK_SIZE, nullptr, "Gear_Task"), + _gearTaskThread(osPriorityAboveNormal, OS_STACK_SIZE, nullptr, "Gear_Task"), _gearDevice(&_mailGearDevice, _timer), _pedalDevice(&_mailPedalDevice, _timer), _resetDevice(callback(this, &BikeSystem::onReset)), @@ -88,13 +88,22 @@ BikeSystem::BikeSystem() #if defined(MBED_TEST_MODE) const advembsof::TaskLogger& BikeSystem::getTaskLogger() { return _taskLogger; } -bike_computer::Speedometer& Bike_system::getSpeedometer() { +bike_computer::Speedometer& BikeSystem::getSpeedometer() { + // ENTER CRITICAL SECTION _mutexSpeedometer.lock(); bike_computer::Speedometer& speedometer = _speedometer; _mutexSpeedometer.unlock(); + // END CRITICAL SECTION + return speedometer; } +GearDevice& BikeSystem::getGearDevice() { return _gearDevice; } + +void BikeSystem::setCallbackGearChage(Callback cbGearChange) { + _cbGearChange = cbGearChange; +} + #endif // defined(MBED_TEST_MODE) void BikeSystem::init() { @@ -104,7 +113,7 @@ void BikeSystem::init() { // initialize the lcd display disco::ReturnCode rc = _displayDevice.init(); if (rc != disco::ReturnCode::Ok) { - tr_error("Ffalseailed to initialized the lcd display: %ld", static_cast(rc)); + tr_error("Ffalseailed to initialized the lcd display: %d", static_cast(rc)); } // initialize the sensor device @@ -114,7 +123,13 @@ void BikeSystem::init() { } // enable/disable task logging - _taskLogger.enable(false); + bool runTaskLogger = false; + +#if defined(MBED_TEST_MODE) + runTaskLogger = true; +#endif + + _taskLogger.enable(runTaskLogger); } void BikeSystem::start() { @@ -134,18 +149,24 @@ void BikeSystem::start() { osStatus status = _isrEventThread.start(callback(this, &BikeSystem::dispatch_isr_events)); if (status != osOK) { - tr_error("Thread %s started with status %d", _isrEventThread.get_name(), status); + tr_error("Thread %s started with status %ld", + _isrEventThread.get_name(), + static_cast(status)); } status = _speedDistanceThread.start(callback(this, &BikeSystem::loop_speed_distance_task)); if (status != osOK) { - tr_error("Thread %s started with status %d", _isrEventThread.get_name(), status); + tr_error("Thread %s started with status %ld", + _isrEventThread.get_name(), + static_cast(status)); } status = _gearTaskThread.start(callback(this, &BikeSystem::loop_gear_task)); if (status != osOK) { - tr_error("Thread %s started with status %d", _gearTaskThread.get_name(), status); + tr_error("Thread %s started with status %ld", + _gearTaskThread.get_name(), + static_cast(status)); } #if !defined(MBED_TEST_MODE) @@ -159,6 +180,16 @@ void BikeSystem::start() { dispatch_events(); } +void BikeSystem::stop() { + osStatus status = _isrEventThread.terminate(); + status += _speedDistanceThread.terminate(); + status += _gearTaskThread.terminate(); + if (status != 0) { + tr_error("Stop thread error"); + } + tr_info("Bike system has stopped !"); +} + /* Callback from isr */ void BikeSystem::onReset() { @@ -170,20 +201,19 @@ void BikeSystem::onReset() { // ISR thread functions void BikeSystem::resetTask() { -#ifndef(MBED_TEST_MODE) +#if !defined(MBED_TEST_MODE) auto taskStartTime = _timer.elapsed_time(); std::chrono::microseconds responseTime = _timer.elapsed_time() - _resetTime; tr_info("Reset task: response time is %" PRIu64 " usecs", responseTime.count()); #endif - // ENTER CRITICAL SECTION _mutexSpeedometer.lock(); _speedometer.reset(); _mutexSpeedometer.unlock(); // END CRITICAL SECTION -#ifndef(MBED_TEST_MODE) +#if !defined(MBED_TEST_MODE) _taskLogger.logPeriodAndExecutionTime( _timer, advembsof::TaskLogger::kResetTaskIndex, taskStartTime); #endif @@ -208,7 +238,8 @@ void BikeSystem::speedDistanceTask() { std::chrono::microseconds responseTime = _timer.elapsed_time() - currentStep->callTime; - tr_info("Reset task: response time is %" PRIu64 " usecs", responseTime.count()); + tr_info("Speed distance task: response time is %" PRIu64 " usecs", + responseTime.count()); osStatus status = _mailPedalDevice.free(currentStep); if (status != osOK) { @@ -228,6 +259,9 @@ void BikeSystem::speedDistanceTask() { _mutexSpeedometer.unlock(); // END CRITICAL SECTION + ThisThread::sleep_for(std::chrono::duration_cast( + kSpeedDistanceTaskComputationTime - (_timer.elapsed_time() - taskStartTime))); + _taskLogger.logPeriodAndExecutionTime( _timer, advembsof::TaskLogger::kSpeedTaskIndex, taskStartTime); } @@ -239,6 +273,16 @@ void BikeSystem::gearTask() { gearMail_t* currentGear = _mailGearDevice.try_get(); if (currentGear != nullptr) { +#if !defined(MBED_TEST_MODE) + std::chrono::microseconds responseTime = + _timer.elapsed_time() - currentGear->callTime; + tr_info("Gear task: response time is %" PRIu64 " usecs", responseTime.count()); +#endif + +#if defined(MBED_TEST_MODE) + _cbGearChange(); +#endif + // ENTER CRITICAL SECTION _mutexGear.lock(); _currentGear = currentGear->gear; @@ -248,17 +292,17 @@ void BikeSystem::gearTask() { _mutexGearSize.unlock(); // END CRITICAL SECTION - std::chrono::microseconds responseTime = - _timer.elapsed_time() - currentGear->callTime; - tr_info("Reset task: response time is %" PRIu64 " usecs", responseTime.count()); - osStatus status = _mailGearDevice.free(currentGear); if (status != osOK) { tr_error("free current gear in the gear tasks doesn't work !"); } } + + ThisThread::sleep_for(std::chrono::duration_cast( + kGearTaskComputationTime - (_timer.elapsed_time() - taskStartTime))); + _taskLogger.logPeriodAndExecutionTime( - _timer, advembsof::TaskLogger::kSpeedTaskIndex, taskStartTime); + _timer, advembsof::TaskLogger::kGearTaskIndex, taskStartTime); } /* Main thread functions */ diff --git a/multi_tasking/bike_system.hpp b/multi_tasking/bike_system.hpp index 68d2789..377a084 100644 --- a/multi_tasking/bike_system.hpp +++ b/multi_tasking/bike_system.hpp @@ -59,6 +59,9 @@ class BikeSystem { #if defined(MBED_TEST_MODE) const advembsof::TaskLogger& getTaskLogger(); bike_computer::Speedometer& getSpeedometer(); + GearDevice& getGearDevice(); + void setCallbackGearChage(Callback cbGearChange); + Callback _cbGearChange; #endif // defined(MBED_TEST_MODE) private: