From 4c174ce444e0fd839d1e73866817eaba88c397e7 Mon Sep 17 00:00:00 2001 From: Klagarge Date: Sun, 17 Nov 2024 14:20:38 +0100 Subject: [PATCH] ADD bike-system super-loop with while --- TESTS/bike-computer/bike-system/main.cpp | 88 +++++++++ main.cpp | 19 +- static_scheduling/bike_system.cpp | 233 +++++++++++++++++++++++ static_scheduling/bike_system.hpp | 98 ++++++++++ static_scheduling/reset_device.cpp | 2 +- 5 files changed, 434 insertions(+), 6 deletions(-) create mode 100644 TESTS/bike-computer/bike-system/main.cpp create mode 100644 static_scheduling/bike_system.cpp create mode 100644 static_scheduling/bike_system.hpp diff --git a/TESTS/bike-computer/bike-system/main.cpp b/TESTS/bike-computer/bike-system/main.cpp new file mode 100644 index 0000000..f3a57cd --- /dev/null +++ b/TESTS/bike-computer/bike-system/main.cpp @@ -0,0 +1,88 @@ +// Copyright 2022 Haute école d'ingénierie et d'architecture de Fribourg +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/**************************************************************************** + * @file main.cpp + * @author Serge Ayer + * + * @brief Bike computer test suite: scheduling + * + * @date 2023-08-26 + * @version 0.1.0 + ***************************************************************************/ + +#include + +#include "static_scheduling/bike_system.hpp" + +#include "greentea-client/test_env.h" +#include "mbed.h" +#include "task_logger.hpp" +#include "unity/unity.h" +#include "utest/utest.h" + +using namespace utest::v1; + +// test_bike_system handler function +static void test_bike_system() { + // create the BikeSystem instance + static_scheduling::BikeSystem bikeSystem; + + // run the bike system in a separate thread + Thread thread; + thread.start(callback(&bikeSystem, &static_scheduling::BikeSystem::start)); + + // let the bike system run for 20 secs + ThisThread::sleep_for(20s); + + // stop the bike system + bikeSystem.stop(); + + // check whether scheduling was correct + // Order is kGearTaskIndex, kSpeedTaskIndex, kTemperatureTaskIndex, + // kResetTaskIndex, kDisplayTask1Index, kDisplayTask2Index + constexpr std::chrono::microseconds taskComputationTimes[] = { + 100000us, 200000us, 100000us, 100000us, 200000us, 100000us}; + constexpr std::chrono::microseconds taskPeriods[] = { + 800000us, 400000us, 1600000us, 800000us, 1600000us, 1600000us}; + + // allow for 2 msecs offset + uint64_t deltaUs = 2000; + for (uint8_t taskIndex = 0; taskIndex < advembsof::TaskLogger::kNbrOfTasks; + taskIndex++) { + TEST_ASSERT_UINT64_WITHIN( + deltaUs, + taskPeriods[taskIndex].count(), + bikeSystem.getTaskLogger().getPeriod(taskIndex).count()); + TEST_ASSERT_UINT64_WITHIN( + deltaUs, + taskComputationTimes[taskIndex].count(), + bikeSystem.getTaskLogger().getComputationTime(taskIndex).count()); + } +} + +static utest::v1::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) + GREENTEA_SETUP(180, "default_auto"); + + return greentea_test_setup_handler(number_of_cases); +} + +// List of test cases in this file +static Case cases[] = {Case("test bike system", test_bike_system)}; + +static Specification specification(greentea_setup, cases); + +int main() { return !Harness::run(specification); } \ No newline at end of file diff --git a/main.cpp b/main.cpp index 9bb6191..c777a16 100644 --- a/main.cpp +++ b/main.cpp @@ -6,19 +6,28 @@ #if !MBED_TEST_MODE #include "mbed.h" // NOLINT +#include "mbed_trace.h" +#include "static_scheduling/bike_system.hpp" // Blinking rate in milliseconds #define BLINKING_RATE 500ms +#define TRACE_GROUP "MAIN" + int main() { // Initialise the digital pin LED1 as an output - DigitalOut led(LED1); + mbed_trace_init(); + // DigitalOut led(LED1); + // tr_debug("Hello world"); - while (true) { - led = !led; - ThisThread::sleep_for(BLINKING_RATE); - } + // while (true) { + // led = !led; + // ThisThread::sleep_for(BLINKING_RATE); + // tr_debug("blink"); + // } + static_scheduling::BikeSystem bikeSystem; + bikeSystem.start(); } #endif // MBED_TEST_MODE diff --git a/static_scheduling/bike_system.cpp b/static_scheduling/bike_system.cpp new file mode 100644 index 0000000..b62ec22 --- /dev/null +++ b/static_scheduling/bike_system.cpp @@ -0,0 +1,233 @@ +// Copyright 2022 Haute école d'ingénierie et d'architecture de Fribourg +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/**************************************************************************** + * @file bike_system.cpp + * @author Serge Ayer + * @author Rémi Heredero + * @author Yann Sierro + * + * @brief Bike System implementation (static scheduling) + * + * @date 2023-11-15 + * @version 1.1.0 + ***************************************************************************/ + +#include "bike_system.hpp" + +#include + +#include "mbed_trace.h" +#if MBED_CONF_MBED_TRACE_ENABLE +#define TRACE_GROUP "BikeSystem" +#endif // MBED_CONF_MBED_TRACE_ENABLE + +namespace static_scheduling { + +static constexpr std::chrono::milliseconds kGearTaskPeriod = 800ms; +static constexpr std::chrono::milliseconds kGearTaskDelay = 0ms; +static constexpr std::chrono::milliseconds kGearTaskComputationTime = 100ms; +static constexpr std::chrono::milliseconds kSpeedDistanceTaskPeriod = 400ms; +static constexpr std::chrono::milliseconds kSpeedDistanceTaskDelay = 0ms; // 0 or 100ms +static constexpr std::chrono::milliseconds kSpeedDistanceTaskComputationTime = 200ms; +static constexpr std::chrono::milliseconds kDisplayTask1Period = 1600ms; +static constexpr std::chrono::milliseconds kDisplayTask1Delay = 300ms; +static constexpr std::chrono::milliseconds kDisplayTask1ComputationTime = 200ms; +static constexpr std::chrono::milliseconds kResetTaskPeriod = 800ms; +static constexpr std::chrono::milliseconds kResetTaskDelay = 700ms; +static constexpr std::chrono::milliseconds kResetTaskComputationTime = 100ms; +static constexpr std::chrono::milliseconds kTemperatureTaskPeriod = 1600ms; +static constexpr std::chrono::milliseconds kTemperatureTaskDelay = 1100ms; +static constexpr std::chrono::milliseconds kTemperatureTaskComputationTime = 100ms; +static constexpr std::chrono::milliseconds kDisplayTask2Period = 1600ms; +static constexpr std::chrono::milliseconds kDisplayTask2Delay = 1200ms; +static constexpr std::chrono::milliseconds kDisplayTask2ComputationTime = 100ms; + +// TODO: implement the constructor + +BikeSystem::BikeSystem() : + _gearDevice(_timer), + _pedalDevice(_timer), + _resetDevice(_timer), + _speedometer(_timer) +{ + +} + +void BikeSystem::start() { + tr_info("Starting Super-Loop without event handling"); + + init(); + + // TODO: implement the super-loop based for implementing the appropriate schedule + // Done + while (true) { + auto startTime = _timer.elapsed_time(); + + // TODO: implement calls to different tasks based on computed schedule + // Done + gearTask(); // 100ms : 0ms -> 100ms + speedDistanceTask(); // 200ms : 100ms -> 300ms + displayTask1(); // 200ms : 300ms -> 500ms + speedDistanceTask(); // 200ms : 500ms -> 700ms + resetTask(); // 100ms : 700ms -> 800ms + gearTask(); // 100ms : 800ms -> 900ms + speedDistanceTask(); // 200ms : 900ms -> 1100ms + temperatureTask(); // 100ms : 1100ms -> 1200ms + displayTask2(); // 100ms : 1200ms -> 1300ms + speedDistanceTask(); // 200ms : 1300ms -> 1500ms + resetTask(); // 100ms : 1500ms -> 1600ms + + + + // register the time at the end of the cyclic schedule period and print the + // elapsed time for the period + std::chrono::microseconds endTime = _timer.elapsed_time(); + const auto cycle = + std::chrono::duration_cast(endTime - startTime); + tr_debug("Repeating cycle time is %" PRIu64 " milliseconds", cycle.count()); + + // TODO: implement loop exit when applicable + // Done + bool fStop = false; + core_util_atomic_load(&fStop); + if (fStop) { + break; + } + + } +} + +void BikeSystem::stop() { core_util_atomic_store_bool(&_stopFlag, true); } + +#if defined(MBED_TEST_MODE) +const advembsof::TaskLogger& BikeSystem::getTaskLogger() { return _taskLogger; } +#endif // defined(MBED_TEST_MODE) + +void BikeSystem::init() { + // start the timer + _timer.start(); + + // initialize the lcd display + disco::ReturnCode rc = _displayDevice.init(); + if (rc != disco::ReturnCode::Ok) { + tr_error("Failed to initialized the lcd display: %d", static_cast(rc)); + } + + // initialize the sensor device + bool present = _sensorDevice.init(); + if (!present) { + tr_error("Sensor not present or initialization failed"); + } + + // enable/disable task logging + _taskLogger.enable(true); +} + +void BikeSystem::gearTask() { + // gear task + auto taskStartTime = _timer.elapsed_time(); + + // no need to protect access to data members (single threaded) + _currentGear = _gearDevice.getCurrentGear(); + _currentGearSize = _gearDevice.getCurrentGearSize(); + + _taskLogger.logPeriodAndExecutionTime( + _timer, advembsof::TaskLogger::kGearTaskIndex, taskStartTime); +} + +void BikeSystem::speedDistanceTask() { + // speed and distance task + auto taskStartTime = _timer.elapsed_time(); + + const auto pedalRotationTime = _pedalDevice.getCurrentRotationTime(); + _speedometer.setCurrentRotationTime(pedalRotationTime); + _speedometer.setGearSize(_currentGearSize); + // no need to protect access to data members (single threaded) + _currentSpeed = _speedometer.getCurrentSpeed(); + _traveledDistance = _speedometer.getDistance(); + + _taskLogger.logPeriodAndExecutionTime( + _timer, advembsof::TaskLogger::kSpeedTaskIndex, taskStartTime); +} + +void BikeSystem::temperatureTask() { + auto taskStartTime = _timer.elapsed_time(); + + tr_warn("Tick1 %" PRIu64, _timer.elapsed_time().count()); + + // no need to protect access to data members (single threaded) + _currentTemperature = _sensorDevice.readTemperature(); + + tr_warn("Tick2 %" PRIu64, _timer.elapsed_time().count()); + + // simulate task computation by waiting for the required task computation time + + std::chrono::microseconds elapsedTime = std::chrono::microseconds::zero(); + while (elapsedTime < kTemperatureTaskComputationTime) { + elapsedTime = _timer.elapsed_time() - taskStartTime; + } + + _taskLogger.logPeriodAndExecutionTime( + _timer, advembsof::TaskLogger::kTemperatureTaskIndex, taskStartTime); +} + +void BikeSystem::resetTask() { + auto taskStartTime = _timer.elapsed_time(); + + if (_resetDevice.checkReset()) { + std::chrono::microseconds responseTime = + _timer.elapsed_time() - _resetDevice.getPressTime(); + tr_info("Reset task: response time is %" PRIu64 " usecs", responseTime.count()); + _speedometer.reset(); + } + + _taskLogger.logPeriodAndExecutionTime( + _timer, advembsof::TaskLogger::kResetTaskIndex, taskStartTime); +} + +void BikeSystem::displayTask1() { + auto taskStartTime = _timer.elapsed_time(); + + _displayDevice.displayGear(_currentGear); + _displayDevice.displaySpeed(_currentSpeed); + _displayDevice.displayDistance(_traveledDistance); + + // simulate task computation by waiting for the required task computation time + + std::chrono::microseconds elapsedTime = std::chrono::microseconds::zero(); + while (elapsedTime < kDisplayTask1ComputationTime) { + elapsedTime = _timer.elapsed_time() - taskStartTime; + } + + _taskLogger.logPeriodAndExecutionTime( + _timer, advembsof::TaskLogger::kDisplayTask1Index, taskStartTime); +} + +void BikeSystem::displayTask2() { + auto taskStartTime = _timer.elapsed_time(); + + _displayDevice.displayTemperature(_currentTemperature); + + // simulate task computation by waiting for the required task computation time + + std::chrono::microseconds elapsedTime = std::chrono::microseconds::zero(); + while (elapsedTime < kDisplayTask2ComputationTime) { + elapsedTime = _timer.elapsed_time() - taskStartTime; + } + _taskLogger.logPeriodAndExecutionTime( + _timer, advembsof::TaskLogger::kDisplayTask2Index, taskStartTime); +} + +} // namespace static_scheduling \ No newline at end of file diff --git a/static_scheduling/bike_system.hpp b/static_scheduling/bike_system.hpp new file mode 100644 index 0000000..445ccad --- /dev/null +++ b/static_scheduling/bike_system.hpp @@ -0,0 +1,98 @@ +// Copyright 2022 Haute école d'ingénierie et d'architecture de Fribourg +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/**************************************************************************** + * @file bike_system.hpp + * @author Serge Ayer + * + * @brief Bike System header file (static scheduling) + * + * @date 2023-08-20 + * @version 1.0.0 + ***************************************************************************/ + +#pragma once + +// from advembsof +#include "display_device.hpp" +#include "task_logger.hpp" + +// from common +#include "sensor_device.hpp" +#include "speedometer.hpp" + +// local +#include "gear_device.hpp" +#include "pedal_device.hpp" +#include "reset_device.hpp" + +namespace static_scheduling { + +class BikeSystem { + public: + // constructor + BikeSystem(); + + // make the class non copyable + BikeSystem(BikeSystem&) = delete; + BikeSystem& operator=(BikeSystem&) = delete; + + // method called in main() for starting the system + void start(); + + // method called for stopping the system + void stop(); + +#if defined(MBED_TEST_MODE) + const advembsof::TaskLogger& getTaskLogger(); +#endif // defined(MBED_TEST_MODE) + + private: + // private methods + void init(); + void gearTask(); + void speedDistanceTask(); + void temperatureTask(); + void resetTask(); + void displayTask1(); + void displayTask2(); + + // stop flag, used for stopping the super-loop (set in stop()) + bool _stopFlag = false; + // timer instance used for loggint task time and used by ResetDevice + Timer _timer; + // data member that represents the device for manipulating the gear + GearDevice _gearDevice; + uint8_t _currentGear = bike_computer::kMinGear; + uint8_t _currentGearSize = bike_computer::kMinGearSize; + // data member that represents the device for manipulating the pedal rotation + // speed/time + PedalDevice _pedalDevice; + float _currentSpeed = 0.0f; + float _traveledDistance = 0.0f; + // data member that represents the device used for resetting + ResetDevice _resetDevice; + // data member that represents the device display + advembsof::DisplayDevice _displayDevice; + // data member that represents the device for counting wheel rotations + bike_computer::Speedometer _speedometer; + // data member that represents the sensor device + bike_computer::SensorDevice _sensorDevice; + float _currentTemperature = 0.0f; + + // used for logging task info + advembsof::TaskLogger _taskLogger; +}; + +} // namespace static_scheduling \ No newline at end of file diff --git a/static_scheduling/reset_device.cpp b/static_scheduling/reset_device.cpp index bda773c..9b35df4 100644 --- a/static_scheduling/reset_device.cpp +++ b/static_scheduling/reset_device.cpp @@ -28,7 +28,7 @@ static constexpr uint8_t kPolarityPressed = 1; namespace static_scheduling { - static constexpr std::chrono::microseconds kTaskRunTime = 1000000us; + static constexpr std::chrono::microseconds kTaskRunTime = 100000us; ResetDevice::ResetDevice(Timer& timer) : _timer(timer), _resetButton(PUSH_BUTTON) { _resetButton.rise(callback(this, &ResetDevice::onRise));