From dce3665f63be6ce77691c00fe8e7ca59d3ecddf9 Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 29 Jun 2021 23:08:45 +1000 Subject: [PATCH] File worker helper. Slightly optimized iButton app code size. (#545) * File worker: file operations helper * Notification app: removed yield * File worker: write operations, calls to system file widgets * App ibutton: use file worker * App ibutton: small save fix, forgotten byte --- applications/ibutton/ibutton-app.cpp | 162 ++++----------- applications/ibutton/ibutton-app.h | 5 - .../notification/notification-app-api.c | 2 - lib/app-scened-template/file-worker.cpp | 195 +++++++++++++++++- lib/app-scened-template/file-worker.h | 130 +++++++++++- 5 files changed, 357 insertions(+), 137 deletions(-) diff --git a/applications/ibutton/ibutton-app.cpp b/applications/ibutton/ibutton-app.cpp index 49326dd2..111ac31b 100755 --- a/applications/ibutton/ibutton-app.cpp +++ b/applications/ibutton/ibutton-app.cpp @@ -2,6 +2,7 @@ #include #include #include +#include const char* iButtonApp::app_folder = "ibutton"; const char* iButtonApp::app_extension = ".ibtn"; @@ -33,9 +34,7 @@ void iButtonApp::run(void* args) { } iButtonApp::iButtonApp() - : fs_api{"sdcard"} - , sd_ex_api{"sdcard-ex"} - , notification{"notification"} { + : notification{"notification"} { api_hal_power_insomnia_enter(); key_worker = new KeyWorker(&ibutton_gpio); @@ -123,14 +122,6 @@ iButtonKey* iButtonApp::get_key() { return &key; } -SdCard_Api* iButtonApp::get_sd_ex_api() { - return sd_ex_api; -} - -FS_Api* iButtonApp::get_fs_api() { - return fs_api; -} - char* iButtonApp::get_file_name() { return file_name; } @@ -225,158 +216,105 @@ void iButtonApp::generate_random_name(char* name, uint8_t max_name_size) { } // file managment -void iButtonApp::show_file_error_message(const char* error_text) { - set_text_store(error_text); - get_sd_ex_api()->show_error(get_sd_ex_api()->context, get_text_store()); -} - bool iButtonApp::save_key(const char* key_name) { - File key_file; + FileWorker file_worker; string_t key_file_name; bool result = false; - FS_Error fs_result; - uint16_t write_count; // Create ibutton directory if necessary - fs_result = get_fs_api()->common.mkdir(app_folder); - if(fs_result != FSE_OK && fs_result != FSE_EXIST) { - show_file_error_message("Cannot create\napplication folder"); + if(!file_worker.mkdir(app_folder)) { return false; }; // First remove key if it was saved - string_init_set_str(key_file_name, app_folder); - string_cat_str(key_file_name, "/"); - string_cat_str(key_file_name, get_key()->get_name()); - string_cat_str(key_file_name, app_extension); - fs_result = get_fs_api()->common.remove(string_get_cstr(key_file_name)); - if(fs_result != FSE_OK && fs_result != FSE_NOT_EXIST) { + string_init_printf(key_file_name, "%s/%s%s", app_folder, get_key()->get_name(), app_extension); + if(!file_worker.remove(string_get_cstr(key_file_name))) { string_clear(key_file_name); - show_file_error_message("Cannot remove\nold key file"); return false; }; // Save the key get_key()->set_name(key_name); - string_set_str(key_file_name, app_folder); - string_cat_str(key_file_name, "/"); - string_cat_str(key_file_name, get_key()->get_name()); - string_cat_str(key_file_name, app_extension); + string_printf(key_file_name, "%s/%s%s", app_folder, get_key()->get_name(), app_extension); - bool res = get_fs_api()->file.open( - &key_file, string_get_cstr(key_file_name), FSAM_WRITE, FSOM_CREATE_ALWAYS); + bool res = file_worker.open(string_get_cstr(key_file_name), FSAM_WRITE, FSOM_CREATE_ALWAYS); string_clear(key_file_name); if(res) { // type header - const char* key_type = "E"; + const char* key_type = "E "; switch(get_key()->get_key_type()) { case iButtonKeyType::KeyCyfral: - key_type = "C"; + key_type = "C "; break; case iButtonKeyType::KeyDallas: - key_type = "D"; + key_type = "D "; break; case iButtonKeyType::KeyMetakom: - key_type = "M"; + key_type = "M "; break; } - write_count = get_fs_api()->file.write(&key_file, key_type, 1); - if(key_file.error_id != FSE_OK || write_count != 1) { - show_file_error_message("Cannot write\nto key file"); - get_fs_api()->file.close(&key_file); + if(!file_worker.write(key_type, 2)) { + file_worker.close(); return false; } - const uint8_t byte_text_size = 4; - char byte_text[byte_text_size]; - - for(uint8_t i = 0; i < get_key()->get_type_data_size(); i++) { - sniprintf(byte_text, byte_text_size, " %02X", get_key()->get_data()[i]); - write_count = get_fs_api()->file.write(&key_file, byte_text, 3); - if(key_file.error_id != FSE_OK || write_count != 3) { - show_file_error_message("Cannot write\nto key file"); - get_fs_api()->file.close(&key_file); - return false; - } + if(!file_worker.write_hex(get_key()->get_data(), get_key()->get_type_data_size())) { + file_worker.close(); + return false; } result = true; - } else { - show_file_error_message("Cannot create\nnew key file"); } - get_fs_api()->file.close(&key_file); - get_sd_ex_api()->check_error(get_sd_ex_api()->context); + file_worker.close(); return result; } bool iButtonApp::load_key_data(string_t key_path) { - File key_file; - uint16_t read_count; + FileWorker file_worker; // Open key file - get_fs_api()->file.open(&key_file, string_get_cstr(key_path), FSAM_READ, FSOM_OPEN_EXISTING); - if(key_file.error_id != FSE_OK) { - show_file_error_message("Cannot open\nkey file"); - get_fs_api()->file.close(&key_file); + if(!file_worker.open(string_get_cstr(key_path), FSAM_READ, FSOM_OPEN_EXISTING)) { + file_worker.close(); return false; } const uint8_t byte_text_size = 4; char byte_text[byte_text_size] = {0, 0, 0, 0}; - // load type header - read_count = get_fs_api()->file.read(&key_file, byte_text, 1); - if(key_file.error_id != FSE_OK || read_count != 1) { - show_file_error_message("Cannot read\nkey file"); - get_fs_api()->file.close(&key_file); + // Load type header + if(!file_worker.read(byte_text, 2)) { + file_worker.close(); return false; } iButtonKeyType key_type = iButtonKeyType::KeyCyfral; - if(strcmp(byte_text, "C") == 0) { + if(strcmp(byte_text, "C ") == 0) { key_type = iButtonKeyType::KeyCyfral; - } else if(strcmp(byte_text, "M") == 0) { + } else if(strcmp(byte_text, "M ") == 0) { key_type = iButtonKeyType::KeyMetakom; - } else if(strcmp(byte_text, "D") == 0) { + } else if(strcmp(byte_text, "D ") == 0) { key_type = iButtonKeyType::KeyDallas; } else { - show_file_error_message("Cannot parse\nkey file"); - get_fs_api()->file.close(&key_file); + file_worker.show_error("Cannot parse\nkey file"); + file_worker.close(); return false; } + iButtonKeyType old_type = get_key()->get_key_type(); get_key()->set_type(key_type); - // load data uint8_t key_data[IBUTTON_KEY_DATA_SIZE] = {0, 0, 0, 0, 0, 0, 0, 0}; - for(uint8_t i = 0; i < get_key()->get_type_data_size(); i++) { - // space - read_count = get_fs_api()->file.read(&key_file, byte_text, 1); - if(key_file.error_id != FSE_OK || read_count != 1) { - show_file_error_message("Cannot read\nkey file"); - get_fs_api()->file.close(&key_file); - return false; - } - - // value - read_count = get_fs_api()->file.read(&key_file, byte_text, 2); - if(key_file.error_id != FSE_OK || read_count != 2) { - show_file_error_message("Cannot read\nkey file"); - get_fs_api()->file.close(&key_file); - return false; - } - - // convert hex value to byte - key_data[i] = strtol(byte_text, NULL, 16); + if(!file_worker.read_hex(key_data, get_key()->get_type_data_size())) { + get_key()->set_type(old_type); + file_worker.close(); + return false; } - get_fs_api()->file.close(&key_file); - - get_key()->set_type(key_type); + file_worker.close(); get_key()->set_data(key_data, IBUTTON_KEY_DATA_SIZE); return true; @@ -406,24 +344,17 @@ bool iButtonApp::load_key(const char* key_name) { bool iButtonApp::load_key() { bool result = false; + FileWorker file_worker; // Input events and views are managed by file_select - bool res = get_sd_ex_api()->file_select( - get_sd_ex_api()->context, - app_folder, - app_extension, - get_file_name(), - get_file_name_size(), - get_key()->get_name()); + bool res = file_worker.file_select( + app_folder, app_extension, get_file_name(), get_file_name_size(), get_key()->get_name()); if(res) { string_t key_str; // Get key file path - string_init_set_str(key_str, app_folder); - string_cat_str(key_str, "/"); - string_cat_str(key_str, get_file_name()); - string_cat_str(key_str, app_extension); + string_init_printf(key_str, "%s/%s%s", app_folder, get_file_name(), app_extension); result = load_key_data(key_str); if(result) { @@ -432,22 +363,17 @@ bool iButtonApp::load_key() { string_clear(key_str); } - get_sd_ex_api()->check_error(get_sd_ex_api()->context); - return result; } bool iButtonApp::delete_key() { - iButtonKey* key = get_key(); - string_t key_file_name; + string_t file_name; bool result = false; + FileWorker file_worker; - string_init_set_str(key_file_name, app_folder); - string_cat_str(key_file_name, "/"); - string_cat_str(key_file_name, key->get_name()); - string_cat_str(key_file_name, app_extension); - result = (get_fs_api()->common.remove(string_get_cstr(key_file_name)) == FSE_OK); - string_clear(key_file_name); + string_init_printf(file_name, "%s/%s%s", app_folder, get_key()->get_name(), app_extension); + result = file_worker.remove(string_get_cstr(file_name)); + string_clear(file_name); return result; } \ No newline at end of file diff --git a/applications/ibutton/ibutton-app.h b/applications/ibutton/ibutton-app.h index 43d900e0..4da97ebf 100644 --- a/applications/ibutton/ibutton-app.h +++ b/applications/ibutton/ibutton-app.h @@ -90,8 +90,6 @@ public: char* get_text_store(); uint8_t get_text_store_size(); - SdCard_Api* get_sd_ex_api(); - FS_Api* get_fs_api(); char* get_file_name(); uint8_t get_file_name_size(); @@ -132,8 +130,6 @@ private: iButtonKey key; - RecordController fs_api; - RecordController sd_ex_api; RecordController notification; static const uint8_t file_name_size = 100; @@ -145,6 +141,5 @@ private: static const char* app_folder; static const char* app_extension; - void show_file_error_message(const char* error_text); bool load_key_data(string_t key_path); }; \ No newline at end of file diff --git a/applications/notification/notification-app-api.c b/applications/notification/notification-app-api.c index b1dc03c9..5905d30a 100644 --- a/applications/notification/notification-app-api.c +++ b/applications/notification/notification-app-api.c @@ -22,7 +22,6 @@ void notification_message_block(NotificationApp* app, const NotificationSequence .sequence = sequence, .back_event = osEventFlagsNew(NULL)}; furi_check(osMessageQueuePut(app->queue, &m, 0, osWaitForever) == osOK); - osThreadYield(); osEventFlagsWait(m.back_event, NOTIFICATION_EVENT_COMPLETE, osFlagsWaitAny, osWaitForever); osEventFlagsDelete(m.back_event); }; @@ -33,7 +32,6 @@ void notification_internal_message_block( NotificationAppMessage m = { .type = InternalLayerMessage, .sequence = sequence, .back_event = osEventFlagsNew(NULL)}; furi_check(osMessageQueuePut(app->queue, &m, 0, osWaitForever) == osOK); - osThreadYield(); osEventFlagsWait(m.back_event, NOTIFICATION_EVENT_COMPLETE, osFlagsWaitAny, osWaitForever); osEventFlagsDelete(m.back_event); }; \ No newline at end of file diff --git a/lib/app-scened-template/file-worker.cpp b/lib/app-scened-template/file-worker.cpp index 109ab5f5..9873d3ad 100644 --- a/lib/app-scened-template/file-worker.cpp +++ b/lib/app-scened-template/file-worker.cpp @@ -1,20 +1,19 @@ #include "file-worker.h" -FileWorker::FileWorker() +FileWorker::FileWorker(bool _silent) : fs_api{"sdcard"} , sd_ex_api{"sdcard-ex"} { - string_init(error_string); + silent = _silent; } FileWorker::~FileWorker() { - string_clear(error_string); } bool FileWorker::open(const char* filename, FS_AccessMode access_mode, FS_OpenMode open_mode) { bool result = fs_api.get()->file.open(&file, filename, access_mode, open_mode); if(!result) { - show_error_message("Cannot open\nfile"); + show_error_internal("Cannot open\nfile"); close(); return false; } @@ -32,7 +31,7 @@ bool FileWorker::mkdir(const char* dirname) { FS_Error fs_result = fs_api.get()->common.mkdir(dirname); if(fs_result != FSE_OK && fs_result != FSE_EXIST) { - show_error_message("Cannot create\nfolder"); + show_error_internal("Cannot create\nfolder"); return false; }; @@ -42,19 +41,197 @@ bool FileWorker::mkdir(const char* dirname) { bool FileWorker::remove(const char* filename) { FS_Error fs_result = fs_api.get()->common.remove(filename); if(fs_result != FSE_OK && fs_result != FSE_NOT_EXIST) { - show_error_message("Cannot remove\nold file"); + show_error_internal("Cannot remove\nold file"); return false; }; return check_common_errors(); } +bool FileWorker::read(void* buffer, uint16_t bytes_to_read) { + if(!read_internal(buffer, bytes_to_read)) { + return false; + } + + return check_common_errors(); +} + +bool FileWorker::read_until(string_t str_result, char separator) { + string_clean(str_result); + const uint8_t buffer_size = 32; + uint8_t buffer[buffer_size]; + + do { + uint16_t read_count = fs_api.get()->file.read(&file, buffer, buffer_size); + if(file.error_id != FSE_OK) { + show_error_internal("Cannot read\nfile"); + return false; + } + + bool result = false; + for(uint16_t i = 0; i < read_count; i++) { + if(buffer[i] == separator) { + uint64_t position; + if(!tell_internal(&position)) { + return false; + } + + position = position - read_count + i + 1; + + if(!seek_internal(position, true)) { + return false; + } + + result = true; + break; + } else { + string_push_back(str_result, buffer[i]); + } + } + + if(result || read_count == 0) { + break; + } + } while(true); + + return check_common_errors(); +} + +bool FileWorker::read_hex(uint8_t* buffer, uint16_t bytes_to_read) { + uint8_t text[2]; + + for(uint8_t i = 0; i < bytes_to_read; i++) { + if(i != 0) { + // space + if(!read_internal(text, 1)) { + return false; + } + } + + // actual data + if(!read_internal(text, 2)) { + return false; + } + + // convert hex value to byte + buffer[i] = strtol(reinterpret_cast(text), NULL, 16); + } + + return check_common_errors(); +} + +bool FileWorker::tell(uint64_t* position) { + if(!tell_internal(position)) { + return false; + } + + return check_common_errors(); +} + +bool FileWorker::seek(uint64_t position, bool from_start) { + if(!seek_internal(position, from_start)) { + return false; + } + + return check_common_errors(); +} + +bool FileWorker::write(const void* buffer, uint16_t bytes_to_write) { + if(!write_internal(buffer, bytes_to_write)) { + return false; + } + + return check_common_errors(); +} + +bool FileWorker::write_hex(const uint8_t* buffer, uint16_t bytes_to_write) { + const uint8_t byte_text_size = 3; + char byte_text[byte_text_size]; + + for(uint8_t i = 0; i < bytes_to_write; i++) { + sniprintf(byte_text, byte_text_size, "%02X", buffer[i]); + + if(i != 0) { + // space + const char* space = " "; + if(!write_internal(space, 1)) { + return false; + } + } + + if(!write_internal(byte_text, 2)) { + return false; + } + } + + return check_common_errors(); +} + +void FileWorker::show_error(const char* error_text) { + sd_ex_api.get()->show_error(sd_ex_api.get()->context, error_text); +} + +bool FileWorker::file_select( + const char* path, + const char* extension, + char* result, + uint8_t result_size, + char* selected_filename) { + return sd_ex_api.get()->file_select( + sd_ex_api.get()->context, path, extension, result, result_size, selected_filename); + +} + bool FileWorker::check_common_errors() { sd_ex_api.get()->check_error(sd_ex_api.get()->context); return true; } -void FileWorker::show_error_message(const char* error_text) { - string_set_str(error_string, error_text); - sd_ex_api.get()->show_error(sd_ex_api.get()->context, string_get_cstr(error_string)); +void FileWorker::show_error_internal(const char* error_text) { + if(!silent) { + show_error(error_text); + } } + +bool FileWorker::read_internal(void* buffer, uint16_t bytes_to_read) { + uint16_t read_count = fs_api.get()->file.read(&file, buffer, bytes_to_read); + + if(file.error_id != FSE_OK || read_count != bytes_to_read) { + show_error_internal("Cannot read\nfile"); + return false; + } + + return true; +} + +bool FileWorker::write_internal(const void* buffer, uint16_t bytes_to_write) { + uint16_t write_count = fs_api.get()->file.write(&file, buffer, bytes_to_write); + + if(file.error_id != FSE_OK || write_count != bytes_to_write) { + show_error_internal("Cannot write\nto file"); + return false; + } + + return true; +} + +bool FileWorker::tell_internal(uint64_t* position) { + *position = fs_api.get()->file.tell(&file); + + if(file.error_id != FSE_OK) { + show_error_internal("Cannot tell\nfile offset"); + return false; + } + + return true; +} + +bool FileWorker::seek_internal(uint64_t position, bool from_start) { + fs_api.get()->file.seek(&file, position, from_start); + if(file.error_id != FSE_OK) { + show_error_internal("Cannot seek\nfile"); + return false; + } + + return true; +} \ No newline at end of file diff --git a/lib/app-scened-template/file-worker.h b/lib/app-scened-template/file-worker.h index 7b261ead..73de3c05 100644 --- a/lib/app-scened-template/file-worker.h +++ b/lib/app-scened-template/file-worker.h @@ -4,24 +4,148 @@ #include #include +/** + * @brief File operations helper class. + * Automatically opens API records, shows error message on error. + */ class FileWorker { public: - FileWorker(); + FileWorker(bool silent = false); ~FileWorker(); RecordController fs_api; RecordController sd_ex_api; + /** + * @brief Open file + * + * @param filename + * @param access_mode + * @param open_mode + * @return true on success + */ bool open(const char* filename, FS_AccessMode access_mode, FS_OpenMode open_mode); + + /** + * @brief Close file + * + * @return true on success + */ bool close(); + /** + * @brief Creates a directory. Doesn't show error if directory exist. + * + * @param dirname + * @return true on success + */ bool mkdir(const char* dirname); + + /** + * @brief Removes the file. Doesn't show error if file doesn't exist. + * + * @param filename + * @return true on success + */ bool remove(const char* filename); + /** + * @brief Reads data from a file. + * + * @param buffer + * @param bytes_to_read + * @return true on success + */ + bool read(void* buffer, uint16_t bytes_to_read = 1); + + /** + * @brief Reads data from a file until separator or EOF is found. + * Moves seek pointer to the next symbol after the separator. The separator is not included in the result. + * + * @param result + * @param separator + * @return true on success + */ + bool read_until(string_t result, char separator = '\n'); + + /** + * @brief Reads data in hexadecimal space-delimited format. For example "AF FF" in a file - [175, 255] in a read buffer. + * + * @param buffer + * @param bytes_to_read + * @return true on success + */ + bool read_hex(uint8_t* buffer, uint16_t bytes_to_read = 1); + + /** + * @brief Read seek pointer value + * + * @param position + * @return true on success + */ + bool tell(uint64_t* position); + + /** + * @brief Set seek pointer value + * + * @param position + * @param from_start + * @return true on success + */ + bool seek(uint64_t position, bool from_start); + + /** + * @brief Write data to file. + * + * @param buffer + * @param bytes_to_write + * @return true on success + */ + bool write(const void* buffer, uint16_t bytes_to_write = 1); + + /** + * @brief Write data to file in hexadecimal space-delimited format. For example [175, 255] in a write buffer - "AF FF" in a file. + * + * @param buffer + * @param bytes_to_write + * @return true on success + */ + bool write_hex(const uint8_t* buffer, uint16_t bytes_to_write = 1); + + /** + * @brief Show system file error message + * + * @param error_text + */ + void show_error(const char* error_text); + + /** + * @brief Show system file select widget + * + * @param path + * @param extension + * @param result + * @param result_size + * @param selected_filename + * @return true if file was selected + */ + bool file_select( + const char* path, + const char* extension, + char* result, + uint8_t result_size, + char* selected_filename); + private: File file; + bool silent; bool check_common_errors(); - void show_error_message(const char* error_text); - string_t error_string; + + void show_error_internal(const char* error_text); + + bool read_internal(void* buffer, uint16_t bytes_to_read = 1); + bool write_internal(const void* buffer, uint16_t bytes_to_write = 1); + bool tell_internal(uint64_t* position); + bool seek_internal(uint64_t position, bool from_start); };