diff --git a/RotaxMonitor/src/datasave.cpp b/RotaxMonitor/src/datasave.cpp index 49b9c81..6c573af 100644 --- a/RotaxMonitor/src/datasave.cpp +++ b/RotaxMonitor/src/datasave.cpp @@ -1,7 +1,7 @@ #include "datasave.h" #include -static const size_t min_free = 1024 * 1024; // minimum free space in LittleFS to allow saving history (1MB) + LITTLEFSGuard::LITTLEFSGuard() { @@ -22,26 +22,26 @@ LITTLEFSGuard::~LITTLEFSGuard() LOG_INFO("LittleFS unmounted successfully"); } -void ignitionBoxStatusAverage::filter(int32_t &old, const int32_t value, const uint32_t k) +void ignitionBoxStatusFiltered::filter(int32_t &old, const int32_t value, const uint32_t k) { float alpha = 1.0f / (float)k; old = old + (int32_t)(alpha * (float)(value - old)); } -void ignitionBoxStatusAverage::filter(float &old, const float value, const uint32_t k) +void ignitionBoxStatusFiltered::filter(float &old, const float value, const uint32_t k) { float alpha = 1.0f / (float)k; old = old + (float)(alpha * (float)(value - old)); } -void ignitionBoxStatusAverage::reset() +void ignitionBoxStatusFiltered::reset() { m_last = ignitionBoxStatus(); m_count = 0; m_data_valid = false; } -void ignitionBoxStatusAverage::update(const ignitionBoxStatus &new_status) +void ignitionBoxStatusFiltered::update(const ignitionBoxStatus &new_status) { if (m_count == 0 && !m_data_valid) { @@ -81,7 +81,7 @@ void ignitionBoxStatusAverage::update(const ignitionBoxStatus &new_status) } } -const bool ignitionBoxStatusAverage::get(ignitionBoxStatus &status) const +const bool ignitionBoxStatusFiltered::get(ignitionBoxStatus &status) const { if (m_data_valid) { @@ -90,7 +90,7 @@ const bool ignitionBoxStatusAverage::get(ignitionBoxStatus &status) const return m_data_valid; } -const ArduinoJson::JsonDocument ignitionBoxStatusAverage::toJson() const +const ArduinoJson::JsonDocument ignitionBoxStatusFiltered::toJson() const { ArduinoJson::JsonDocument doc; if (m_data_valid) @@ -125,97 +125,3 @@ const ArduinoJson::JsonDocument ignitionBoxStatusAverage::toJson() const return doc; } -void saveHistoryTask(void *pvParameters) -{ - const auto *params = static_cast(pvParameters); - const auto &history = *params->history; - const auto &file_path = params->file_path; - if (!params) - { - LOG_ERROR("Invalid parameters for saveHistoryTask"); - return; - } - LOG_DEBUG("Starting saving: ", file_path.c_str()); - save_history(history, file_path); - vTaskDelete(NULL); -} - -void save_history(const PSRAMVector &history, const std::filesystem::path &file_name) -{ - // Initialize SPIFFS - if (!SAVE_HISTORY_TO_LITTLEFS) - return; - - auto littlefs_guard = LITTLEFSGuard(); // use RAII guard to ensure LittleFS is properly mounted and unmounted - - if (LittleFS.totalBytes() - LittleFS.usedBytes() < min_free) // check if at least 1MB is free for saving history - { - LOG_ERROR("Not enough space in SPIFFS to save history"); - return; - } - - std::filesystem::path file_path = file_name; - if (file_name.root_path() != "/littlefs") - file_path = std::filesystem::path("/littlefs") / file_name; - - auto save_flags = std::ios::out; - if (first_save && LittleFS.exists(file_path.c_str())) - { - first_save = false; - save_flags |= std::ios::trunc; // overwrite existing file - LittleFS.remove(file_path.c_str()); // ensure file is removed before saving to avoid issues with appending to existing file in SPIFFS - LOG_INFO("Saving history to LittleFS, new file:", file_path.c_str()); - } - else - { - save_flags |= std::ios::app; // append to new file - LOG_INFO("Saving history to LittleFS, appending to existing file:", file_path.c_str()); - } - - std::ofstream ofs(file_path, save_flags); - if (ofs.fail()) - { - LOG_ERROR("Failed to open file for writing"); - return; - } - - // write csv header - if (first_save) - { - ofs << "TS,\ - EVENTS_12,DLY_12,STAT_12,V_12_1,V_12_2,V_12_3,V_12_4,IGNITION_MODE_12,\ - EVENTS_34,DLY_34,STAT_34,V_34_1,V_34_2,V_34_3,V_34_4,IGNITION_MODE_34,\ - ENGINE_RPM,ADC_READTIME,N_QUEUE_ERRORS" - << std::endl; - ofs.flush(); - } - - for (const auto &entry : history) - { - ofs << std::to_string(entry.timestamp) << "," - << std::to_string(entry.coils12.n_events) << "," - << std::to_string(entry.coils12.spark_delay) << "," - << std::string(sparkStatusNames.at(entry.coils12.spark_status)) << "," - << std::to_string(entry.coils12.peak_p_in) << "," - << std::to_string(entry.coils12.peak_n_in) << "," - << std::to_string(entry.coils12.peak_p_out) << "," - << std::to_string(entry.coils12.peak_n_out) << "," - << std::string(softStartStatusNames.at(entry.coils12.sstart_status)) << "," - << std::to_string(entry.coils34.n_events) << "," - << std::to_string(entry.coils34.spark_delay) << "," - << std::string(sparkStatusNames.at(entry.coils34.spark_status)) << "," - << std::to_string(entry.coils34.peak_p_in) << "," - << std::to_string(entry.coils34.peak_n_in) << "," - << std::to_string(entry.coils34.peak_p_out) << "," - << std::to_string(entry.coils34.peak_n_out) << "," - << std::string(softStartStatusNames.at(entry.coils34.sstart_status)) << "," - << std::to_string(entry.eng_rpm) << "," - << std::to_string(entry.adc_read_time) << "," - << std::to_string(entry.n_queue_errors); - ofs << std::endl; - ofs.flush(); - } - - ofs.close(); - LOG_INFO("Ignition A history saved to LittleFS, records written: ", history.size()); -} diff --git a/RotaxMonitor/src/datasave.h b/RotaxMonitor/src/datasave.h index d834308..c39d21b 100644 --- a/RotaxMonitor/src/datasave.h +++ b/RotaxMonitor/src/datasave.h @@ -15,14 +15,6 @@ #include "psvector.h" const uint32_t max_history = 256; -const bool SAVE_HISTORY_TO_LITTLEFS = false; // Set to true to enable saving history to LittleFS, false to disable -static bool first_save = true; // flag to indicate if this is the first save (to write header) - -struct dataSaveParams -{ - const PSRAMVector *history; - const std::filesystem::path file_path; -}; class LITTLEFSGuard { @@ -31,7 +23,7 @@ public: ~LITTLEFSGuard(); }; -class ignitionBoxStatusAverage +class ignitionBoxStatusFiltered { private: ignitionBoxStatus m_last; @@ -40,8 +32,8 @@ private: bool m_data_valid = false; // flag to indicate if the average data is valid (i.e. at least one sample has been added) public: - ignitionBoxStatusAverage() = default; - ignitionBoxStatusAverage(const uint32_t max_count) : m_max_count(max_count) + ignitionBoxStatusFiltered() = default; + ignitionBoxStatusFiltered(const uint32_t max_count) : m_max_count(max_count) { m_data_valid = false; m_count = 0; diff --git a/RotaxMonitor/src/main.cpp b/RotaxMonitor/src/main.cpp index eaca716..81fd5eb 100644 --- a/RotaxMonitor/src/main.cpp +++ b/RotaxMonitor/src/main.cpp @@ -107,9 +107,9 @@ void loop() rtTaskParams taskA_params{ .rt_running = true, - .dev = &dev, + .dev = std::make_shared(dev), .rt_queue = rt_taskA_queue, - .rt_int = rtTaskInterrupts{ + .rt_int = rtTaskInterruptParams{ .isr_ptr = &trig_isr_A, .trig_pin_12p = TRIG_PIN_A12P, .trig_pin_12n = TRIG_PIN_A12N, @@ -117,14 +117,14 @@ void loop() .trig_pin_34n = TRIG_PIN_A34N, .spark_pin_12 = SPARK_PIN_A12, .spark_pin_34 = SPARK_PIN_A34}, - .rt_resets = rtTaskResets{.rst_io_peak = POT_CS_12A, .rst_io_sh = POT_CS_34A}}; + .rt_io = rtTaskIOParams{.rst_io_peak = 0, .rst_io_sh = 0}}; #ifdef CH_B_ENABLE rtTaskParams taskB_params{ .rt_running = true, .dev = &dev, .rt_queue = rt_taskB_queue, - .rt_int = rtTaskInterrupts{ + .rt_int = rtTaskInterruptParams{ .isr_ptr = &trig_isr_B, .trig_pin_12p = TRIG_PIN_B12P, .trig_pin_12n = TRIG_PIN_B12N, @@ -132,7 +132,7 @@ void loop() .trig_pin_34n = TRIG_PIN_B34N, .spark_pin_12 = SPARK_PIN_B12, .spark_pin_34 = SPARK_PIN_B34}, - .rt_resets = rtTaskResets{.rst_io_peak = SS_FORCE_A, .rst_io_sh = SS_INIBHIT_A12}}; + .rt_io = rtTaskIOParams{.rst_io_peak = SS_FORCE_A, .rst_io_sh = SS_INIBHIT_A12}}; #endif if (!rt_taskA_queue || !rt_taskB_queue) @@ -190,7 +190,7 @@ void loop() // Ignition A on Core 0 auto ignA_task_success = pdPASS; ignA_task_success = xTaskCreatePinnedToCore( - rtIgnitionTask_run, + rtIgnitionTask_realtime, "rtTask_A", RT_TASK_STACK, (void *)&taskA_params, @@ -204,7 +204,7 @@ void loop() #ifdef CH_B_ENABLE ignB_task_success = xTaskCreatePinnedToCore( - rtIgnitionTask_run, + rtIgnitionTask_realtime, "rtTask_B", RT_TASK_STACK, (void *)&taskB_params, @@ -236,8 +236,8 @@ void loop() ignitionBoxStatus ign_info_A; ignitionBoxStatus ign_info_B; - ignitionBoxStatusAverage ign_info_avg_A(filter_k); - ignitionBoxStatusAverage ign_info_avg_B(filter_k); + ignitionBoxStatusFiltered ign_info_avg_A(filter_k); + ignitionBoxStatusFiltered ign_info_avg_B(filter_k); LITTLEFSGuard fsGuard; WebPage webPage(80, LittleFS); // Initialize webserver and Websocket diff --git a/RotaxMonitor/src/tasks.cpp b/RotaxMonitor/src/tasks.cpp index 5734341..bdb1469 100644 --- a/RotaxMonitor/src/tasks.cpp +++ b/RotaxMonitor/src/tasks.cpp @@ -1,5 +1,8 @@ #include "tasks.h" #include +#include + +//// GLOBAL STATIC FUNCTIONS // Timeout callback for microsecond precision void spark_timeout_callback(void *arg) @@ -8,7 +11,18 @@ void spark_timeout_callback(void *arg) xTaskNotify(handle, SPARK_FLAG_TIMEOUT, eSetValueWithOverwrite); } -void rtIgnitionTask::rtIgnitionTask_run(void *pvParameters) +// Manages queue receive, save data and callback to external tasks for communication +void rtIgnitionTask::rtIgnitionTask_manager(void *pvParameters) +{ + rtIgnitionTask *cls = (rtIgnitionTask *)pvParameters; + while (cls->m_running) + { + cls->run(); + } +} + +// Static task function +void rtIgnitionTask::rtIgnitionTask_realtime(void *pvParameters) { // Invalid real time rt_task_ptr parameters, exit immediate @@ -18,19 +32,18 @@ void rtIgnitionTask::rtIgnitionTask_run(void *pvParameters) vTaskDelete(NULL); } - // Task Parameters and Devices rtTaskParams *params = (rtTaskParams *)pvParameters; - const rtTaskInterrupts rt_int = params->rt_int; // copy to avoid external override - const rtTaskResets rt_rst = params->rt_resets; // copy to avoid external override + const rtTaskInterruptParams rt_int = params->rt_int; // copy to avoid external override + const rtTaskIOParams rt_rst = params->rt_io; // copy to avoid external override QueueHandle_t rt_queue = params->rt_queue; - Devices *dev = params->dev; + Devices *dev = params->dev.get(); ADS1256 *adc = dev->adc_a; PCA9555 *io = dev->io; TaskStatus_t rt_task_info; vTaskGetInfo(NULL, &rt_task_info, pdFALSE, eInvalid); - + const auto rt_task_name = pcTaskGetName(rt_task_info.xHandle); LOG_INFO("rtTask Params OK [", rt_task_name, "]"); @@ -75,7 +88,6 @@ void rtIgnitionTask::rtIgnitionTask_run(void *pvParameters) .name = "spark_timeout"}; esp_timer_create(&timer_args, &timeout_timer); - // Attach Pin Interrupts attachInterruptArg(digitalPinToInterrupt(rt_int.trig_pin_12p), rt_int.isr_ptr, (void *)&isr_params_t12p, RISING); attachInterruptArg(digitalPinToInterrupt(rt_int.trig_pin_12n), rt_int.isr_ptr, (void *)&isr_params_t12n, RISING); @@ -235,17 +247,13 @@ void rtIgnitionTask::rtIgnitionTask_run(void *pvParameters) ign_box_sts.adc_read_time = (int32_t)(esp_timer_get_time() - start_adc_read); } else // simulate adc read timig - vTaskDelay(pdMS_TO_TICKS(1)); + vTaskDelay(pdMS_TO_TICKS(c_adc_time)); // reset peak detectors + sample and hold // outputs on io expander if (io) { - const uint16_t iostat = io->read(); - const uint16_t rst_bitmask = (0x0001 << rt_rst.rst_io_peak); - io->write(iostat | rst_bitmask); - vTaskDelay(pdMS_TO_TICKS(1)); - io->write(iostat & ~rst_bitmask); + // [TODO] code to reset sample and hold and arm trigger level detectors } else vTaskDelay(pdMS_TO_TICKS(1)); @@ -261,7 +269,7 @@ void rtIgnitionTask::rtIgnitionTask_run(void *pvParameters) } // Delete the timeout timer esp_timer_delete(timeout_timer); - LOG_WARN("Ending realTime Task"); + LOG_WARN("rtTask Ending [", rt_task_name, "]"); // Ignition A Interrupts DETACH detachInterrupt(rt_int.trig_pin_12p); detachInterrupt(rt_int.trig_pin_12n); @@ -272,3 +280,229 @@ void rtIgnitionTask::rtIgnitionTask_run(void *pvParameters) // delete present task vTaskDelete(NULL); } + +///////////// CLASS MEMBER DEFINITIONS ///////////// +rtIgnitionTask::rtIgnitionTask(const rtTaskParams params, const uint32_t history_size, const uint32_t queue_size, const uint8_t core, std::mutex &fs_mutex, fs::FS &filesystem = LittleFS) : m_params(params), m_filesystem(filesystem), m_fs_mutex(fs_mutex), m_core(core) +{ + // create queue buffers + m_queue = xQueueCreate(queue_size, sizeof(ignitionBoxStatus)); + if (!m_queue) + { + LOG_ERROR("Unable To Create Task [", params.name.c_str(), "] queues"); + m_manager_status = rtTaskStatus::ERROR; + return; + } + else + m_params.rt_queue = m_queue; + + // create PSram history vectors + m_history_0.resize(history_size); + m_history_1.resize(history_size); + // assing active and writable history + m_active_history = std::make_unique(m_history_0.data()); + m_save_history = std::make_unique(m_history_1.data()); + + LOG_WARN("Starting Manager for [", m_params.name.c_str(), "]"); + auto task_success = xTaskCreate( + rtIgnitionTask_manager, + (std::string("man_") + m_params.name).c_str(), + m_params.rt_stack_size, + (void *)this, + 1, + &m_manager_handle); + + if (task_success != pdPASS) + { + LOG_ERROR("Unable To Create Manager for [", params.name.c_str(), "]"); + m_manager_status = rtTaskStatus::ERROR; + return; + } + + // average every 10 samples + m_info_filtered = ignitionBoxStatusFiltered(10); + m_last_data = millis(); + m_manager_status = rtTaskStatus::OK; +} + +rtIgnitionTask::~rtIgnitionTask() +{ + if (m_rt_handle) + vTaskDelete(m_rt_handle); + if (m_manager_handle) + vTaskDelete(m_manager_handle); + if (m_queue) + vQueueDelete(m_queue); +} + +void rtIgnitionTask::run() +{ + // receive new data from the queue + auto new_data = xQueueReceive(m_queue, &m_last_status, 0); // non blocking receive + + if (new_data == pdPASS) + { + m_manager_status = rtTaskStatus::RUNNING; + // if history buffer is full swap buffers and if enabled save history buffer + if (m_counter_status >= m_active_history->size()) + { + m_counter_status = 0; + m_partial_save = false; // reset partial save flag on new data cycle + std::swap(m_active_history, m_save_history); + if (m_enable_save) + save_history(*m_save_history, m_history_path); // directly call the save task function to save without delay + } + + // update filtered data + m_info_filtered.update(m_last_status); + (*m_active_history)[m_counter_status] = m_last_status; + + // update data counter + m_counter_status++; + } + else + { + if (millis() - m_last_data > c_idle_time) + m_manager_status = rtTaskStatus::IDLE; + delay(5); // yeld to another task + } +} + +const bool rtIgnitionTask::start() +{ + LOG_WARN("Starting rtTask [", m_params.name.c_str(), "]"); + auto task_success = xTaskCreatePinnedToCore( + rtIgnitionTask_realtime, + m_params.name.c_str(), + m_params.rt_stack_size, + (void *)&m_params, + m_params.rt_priority, + &m_rt_handle, + m_core); + const bool success = task_success == pdPASS && m_rt_handle != nullptr; + if (success) + m_manager_status = rtTaskStatus::IDLE; + return success; +} + +const bool rtIgnitionTask::stop() +{ + LOG_WARN("Ending Task [", m_params.name.c_str(), "]"); + if (m_rt_handle) + { + m_params.rt_running = false; + m_rt_handle = nullptr; + m_manager_status = rtTaskStatus::STOPPED; + return true; + } + return false; +} + +const ignitionBoxStatus rtIgnitionTask::getLast() const +{ + return m_last_status; +} + +const ignitionBoxStatusFiltered rtIgnitionTask::getFiltered() const +{ + return m_info_filtered; +} + +const rtIgnitionTask::rtTaskStatus rtIgnitionTask::getStatus() const +{ + return m_manager_status; +} + +void rtIgnitionTask::enableSave(const bool enable, const std::filesystem::path filename) +{ + m_enable_save = enable; + if (enable && !filename.empty()) + { + LOG_WARN("Save History Enabled Task [", m_params.name.c_str(), "]"); + m_history_path = m_filesystem.mountpoint() / filename; + } + else + { + LOG_WARN("Save History Disabled Task [", m_params.name.c_str(), "]"); + } +} + +void rtIgnitionTask::saveHistory(const rtIgnitionTask::PSHistory &history, const std::filesystem::path &file_name) +{ + // Lock filesystem mutex to avoid concurrent access + std::lock_guard fs_lock(m_fs_mutex); + + // Check for free space + if (LittleFS.totalBytes() - LittleFS.usedBytes() < history.size() * sizeof(ignitionBoxStatus) * 200) // check if at least 1MB is free for saving history + { + LOG_ERROR("Not enough space in SPIFFS to save history"); + return; + } + + // create complete file path + const std::filesystem::path mount_point = std::filesystem::path(m_filesystem.mountpoint()); + std::filesystem::path file_path = file_name; + if (file_name.root_path() != mount_point) + file_path = mount_point / file_name; + + // if firt save remove old file and create new + auto save_flags = std::ios::out; + if (m_first_save && m_filesystem.exists(file_path.c_str())) + { + m_first_save = false; + save_flags |= std::ios::trunc; // overwrite existing file + m_filesystem.remove(file_path.c_str()); // ensure file is removed before saving to avoid issues with appending to existing file in SPIFFS + LOG_INFO("Saving history to Flash, new file:", file_path.c_str()); + } + else // else append to existing file + { + save_flags |= std::ios::app; // append to new file + LOG_INFO("Saving history to Flash, appending to existing file:", file_path.c_str()); + } + + std::ofstream ofs(file_path, save_flags); + if (ofs.fail()) + { + LOG_ERROR("Failed to open file for writing"); + return; + } + + // write csv header + if (m_first_save) + { + ofs << "TS,\ + EVENTS_12,DLY_12,STAT_12,V_12_1,V_12_2,V_12_3,V_12_4,IGNITION_MODE_12,\ + EVENTS_34,DLY_34,STAT_34,V_34_1,V_34_2,V_34_3,V_34_4,IGNITION_MODE_34,\ + ENGINE_RPM,ADC_READTIME,N_QUEUE_ERRORS" + << std::endl; + ofs.flush(); + } + + for (const auto &entry : history) + { + ofs << std::to_string(entry.timestamp) << "," + << std::to_string(entry.coils12.n_events) << "," + << std::to_string(entry.coils12.spark_delay) << "," + << std::string(sparkStatusNames.at(entry.coils12.spark_status)) << "," + << std::to_string(entry.coils12.peak_p_in) << "," + << std::to_string(entry.coils12.peak_n_in) << "," + << std::to_string(entry.coils12.peak_p_out) << "," + << std::to_string(entry.coils12.peak_n_out) << "," + << std::string(softStartStatusNames.at(entry.coils12.sstart_status)) << "," + << std::to_string(entry.coils34.n_events) << "," + << std::to_string(entry.coils34.spark_delay) << "," + << std::string(sparkStatusNames.at(entry.coils34.spark_status)) << "," + << std::to_string(entry.coils34.peak_p_in) << "," + << std::to_string(entry.coils34.peak_n_in) << "," + << std::to_string(entry.coils34.peak_p_out) << "," + << std::to_string(entry.coils34.peak_n_out) << "," + << std::string(softStartStatusNames.at(entry.coils34.sstart_status)) << "," + << std::to_string(entry.eng_rpm) << "," + << std::to_string(entry.adc_read_time) << "," + << std::to_string(entry.n_queue_errors); + ofs << std::endl; + ofs.flush(); + } + + ofs.close(); + LOG_INFO("Ignition Box history saved to Flash, records written: ", history.size()); +} diff --git a/RotaxMonitor/src/tasks.h b/RotaxMonitor/src/tasks.h index fd5d97f..3686925 100644 --- a/RotaxMonitor/src/tasks.h +++ b/RotaxMonitor/src/tasks.h @@ -10,6 +10,9 @@ #include "utils.h" #include #include +#include +#include +#include // ISR #include "isr.h" @@ -33,68 +36,115 @@ static const std::map names = { }; #endif -// RT task Interrupt parameters -struct rtTaskInterrupts -{ - void (*isr_ptr)(void *); - const uint8_t trig_pin_12p; - const uint8_t trig_pin_12n; - const uint8_t trig_pin_34p; - const uint8_t trig_pin_34n; - const uint8_t spark_pin_12; - const uint8_t spark_pin_34; -}; - -// RT Task Peak Detector Reset pins -struct rtTaskResets -{ - const uint8_t rst_io_peak; - const uint8_t rst_io_sh; -}; - -// RT task parameters -struct rtTaskParams -{ - bool rt_running; // run flag, false to terminate - Devices *dev; - const QueueHandle_t rt_queue; - const rtTaskInterrupts rt_int; // interrupt pins to attach - const rtTaskResets rt_resets; // reset ping for peak detectors -}; - - class rtIgnitionTask { - - using PShistory = PSRAMVector; - - public: - rtIgnitionTask(); + using PSHistory = PSRAMVector; + // RT task Interrupt parameters + struct rtTaskInterruptParams + { + void (*isr_ptr)(void *); + const uint8_t trig_pin_12p; + const uint8_t trig_pin_12n; + const uint8_t trig_pin_34p; + const uint8_t trig_pin_34n; + const uint8_t spark_pin_12; + const uint8_t spark_pin_34; + }; + + // RT Task Peak Detector Reset pins + struct rtTaskIOParams + { + const uint32_t expander_addr; + const uint8_t pot_cs_a12; + const uint8_t pot_cs_a34; + const uint8_t ss_force_a; + const uint8_t ss_inhibit_a12; + const uint8_t ss_inhibit_a34; + const uint8_t sh_disch_a12; + const uint8_t sh_disch_a34; + const uint8_t sh_arm_a12; + const uint8_t sh_arm_a34; + const uint8_t relay_in_a12; + const uint8_t relay_in_a34; + const uint8_t relay_out_a12; + const uint8_t relay_out_a34; + }; + + // RT task parameters + struct rtTaskParams + { + bool rt_running; // run flag, false to terminate + const std::string name; + const std::shared_ptr dev; + const uint32_t rt_stack_size; + const uint32_t rt_priority; + const rtTaskInterruptParams rt_int; // interrupt pins to attach + const rtTaskIOParams rt_io; // reset ping for peak detectors + QueueHandle_t rt_queue; // queue for task io + }; + + enum rtTaskStatus + { + INIT, + OK, + ERROR, + RUNNING, + IDLE, + STOPPED + }; + +public: + rtIgnitionTask(const rtTaskParams params, const uint32_t history_size, const uint32_t queue_size, const uint8_t core, std::mutex &fs_mutex, fs::FS &filesystem = LittleFS); ~rtIgnitionTask(); - - static void rtIgnitionTask_run(void *pvParameters); - + void run(); - void start(); - void stop(); + const bool start(); + const bool stop(); + + const ignitionBoxStatus getLast() const; + const ignitionBoxStatusFiltered getFiltered() const; + + const rtTaskStatus getStatus() const; + + void enableSave(const bool enable, const std::filesystem::path filename); + +private: + void rtIgnitionTask::saveHistory(const rtIgnitionTask::PSHistory &history, const std::filesystem::path &file_name); + +private: // static functions for FreeRTOS + static void rtIgnitionTask_manager(void *pvParameters); + static void rtIgnitionTask_realtime(void *pvParameters); private: bool m_running = true; + rtTaskStatus m_manager_status = INIT; - std::shared_ptr m_devices; - std::string m_name; - TaskHandle_t m_handle; - QueueHandle_t m_queue; rtTaskParams m_params; + const uint8_t m_core; - PShistory m_history_0; - PShistory m_history_1; + TaskHandle_t m_rt_handle = nullptr; + TaskHandle_t m_manager_handle = nullptr; + QueueHandle_t m_queue = nullptr; - std::unique_ptr m_active_history; - std::unique_ptr m_save_history; + bool m_enable_save = false; + std::filesystem::path m_history_path; + PSHistory m_history_0; + PSHistory m_history_1; + std::unique_ptr m_active_history; + std::unique_ptr m_save_history; + fs::FS &m_filesystem; + std::mutex &m_fs_mutex; + bool m_partial_save = false; + bool m_first_save = true; - fs::FS m_filesystem; - + uint32_t m_counter_status = 0; + uint32_t m_last_data = 0; + ignitionBoxStatus m_last_status; + ignitionBoxStatusFiltered m_info_filtered; + static const uint32_t c_idle_time = 2000; // in mS + static const uint8_t c_spark_timeout_max = 500; // uS + static const uint8_t c_adc_time = 4; // in mS + static const uint8_t c_io_time = 2; // in mS };