diff --git a/applications/storage/storage.h b/applications/storage/storage.h index eecd1beb..1e6ced23 100644 --- a/applications/storage/storage.h +++ b/applications/storage/storage.h @@ -282,7 +282,7 @@ FS_Error storage_int_restore(Storage* api, const char* dstname); /***************** Simplified Functions ******************/ /** - * Removes a file/directory from the repository, the directory must be empty and the file/directory must not be open + * Removes a file/directory, the directory must be empty and the file/directory must not be open * @param storage pointer to the api * @param path * @return true on success or if file/dir is not exist @@ -290,7 +290,7 @@ FS_Error storage_int_restore(Storage* api, const char* dstname); bool storage_simply_remove(Storage* storage, const char* path); /** - * Removes a file/directory from the repository, the directory can be not empty + * Recursively removes a file/directory, the directory can be not empty * @param storage pointer to the api * @param path * @return true on success or if file/dir is not exist diff --git a/applications/storage/storage_cli.c b/applications/storage/storage_cli.c index 70da21d2..b35e37a8 100644 --- a/applications/storage/storage_cli.c +++ b/applications/storage/storage_cli.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,7 @@ static void storage_cli_print_usage() { printf("\tinfo\t - get FS info\r\n"); printf("\tformat\t - format filesystem\r\n"); printf("\tlist\t - list files and dirs\r\n"); + printf("\ttree\t - list files and dirs, recursive\r\n"); printf("\tremove\t - delete the file or directory\r\n"); printf("\tread\t - read text from file and print file size and content to cli\r\n"); printf( @@ -138,6 +140,44 @@ static void storage_cli_list(Cli* cli, string_t path) { } } +static void storage_cli_tree(Cli* cli, string_t path) { + if(string_cmp_str(path, "/") == 0) { + string_set(path, "/int"); + storage_cli_tree(cli, path); + string_set(path, "/ext"); + storage_cli_tree(cli, path); + } else { + Storage* api = furi_record_open("storage"); + DirWalk* dir_walk = dir_walk_alloc(api); + string_t name; + string_init(name); + + if(dir_walk_open(dir_walk, string_get_cstr(path))) { + FileInfo fileinfo; + bool read_done = false; + + while(dir_walk_read(dir_walk, name, &fileinfo) == DirWalkOK) { + read_done = true; + if(fileinfo.flags & FSF_DIRECTORY) { + printf("\t[D] %s\r\n", string_get_cstr(name)); + } else { + printf("\t[F] %s %lub\r\n", string_get_cstr(name), (uint32_t)(fileinfo.size)); + } + } + + if(!read_done) { + printf("\tEmpty\r\n"); + } + } else { + storage_cli_print_error(dir_walk_get_error(dir_walk)); + } + + string_clear(name); + dir_walk_free(dir_walk); + furi_record_close("storage"); + } +} + static void storage_cli_read(Cli* cli, string_t path) { UNUSED(cli); Storage* api = furi_record_open("storage"); @@ -474,6 +514,11 @@ void storage_cli(Cli* cli, string_t args, void* context) { break; } + if(string_cmp_str(cmd, "tree") == 0) { + storage_cli_tree(cli, path); + break; + } + if(string_cmp_str(cmd, "read") == 0) { storage_cli_read(cli, path); break; diff --git a/applications/storage/storage_external_api.c b/applications/storage/storage_external_api.c index 1335c708..7f600af2 100644 --- a/applications/storage/storage_external_api.c +++ b/applications/storage/storage_external_api.c @@ -4,6 +4,7 @@ #include "storage_i.h" #include "storage_message.h" #include +#include #define MAX_NAME_LENGTH 256 @@ -332,12 +333,69 @@ FS_Error storage_common_remove(Storage* storage, const char* path) { FS_Error storage_common_rename(Storage* storage, const char* old_path, const char* new_path) { FS_Error error = storage_common_copy(storage, old_path, new_path); if(error == FSE_OK) { - error = storage_common_remove(storage, old_path); + if(storage_simply_remove_recursive(storage, old_path)) { + error = FSE_OK; + } else { + error = FSE_INTERNAL; + } } return error; } +static FS_Error + storage_copy_recursive(Storage* storage, const char* old_path, const char* new_path) { + FS_Error error = storage_common_mkdir(storage, new_path); + DirWalk* dir_walk = dir_walk_alloc(storage); + string_t path; + string_t tmp_new_path; + string_t tmp_old_path; + FileInfo fileinfo; + string_init(path); + string_init(tmp_new_path); + string_init(tmp_old_path); + + do { + if(error != FSE_OK) break; + + if(!dir_walk_open(dir_walk, old_path)) { + error = dir_walk_get_error(dir_walk); + break; + } + + while(1) { + DirWalkResult res = dir_walk_read(dir_walk, path, &fileinfo); + + if(res == DirWalkError) { + error = dir_walk_get_error(dir_walk); + break; + } else if(res == DirWalkLast) { + break; + } else { + string_set(tmp_old_path, path); + string_right(path, strlen(old_path)); + string_printf(tmp_new_path, "%s%s", new_path, string_get_cstr(path)); + + if(fileinfo.flags & FSF_DIRECTORY) { + error = storage_common_mkdir(storage, string_get_cstr(tmp_new_path)); + } else { + error = storage_common_copy( + storage, string_get_cstr(tmp_old_path), string_get_cstr(tmp_new_path)); + } + + if(error != FSE_OK) break; + } + } + + } while(false); + + string_clear(tmp_new_path); + string_clear(tmp_old_path); + string_clear(path); + dir_walk_free(dir_walk); + return error; +} + FS_Error storage_common_copy(Storage* storage, const char* old_path, const char* new_path) { FS_Error error; @@ -346,7 +404,7 @@ FS_Error storage_common_copy(Storage* storage, const char* old_path, const char* if(error == FSE_OK) { if(fileinfo.flags & FSF_DIRECTORY) { - error = storage_common_mkdir(storage, new_path); + error = storage_copy_recursive(storage, old_path, new_path); } else { Stream* stream_from = file_stream_alloc(storage); Stream* stream_to = file_stream_alloc(storage); diff --git a/applications/unit_tests/storage/dirwalk_test.c b/applications/unit_tests/storage/dirwalk_test.c new file mode 100644 index 00000000..25e12259 --- /dev/null +++ b/applications/unit_tests/storage/dirwalk_test.c @@ -0,0 +1,272 @@ +#include "../minunit.h" +#include +#include +#include + +static const char* const storage_test_dirwalk_paths[] = { + "1", + "11", + "111", + "1/2", + "1/22", + "1/222", + "11/2", + "111/2", + "111/22", + "111/22/33", +}; + +static const char* const storage_test_dirwalk_files[] = { + "file1.test", + "file2.test", + "file3.ext_test", + "1/file1.test", + "111/22/33/file1.test", + "111/22/33/file2.test", + "111/22/33/file3.ext_test", + "111/22/33/file4.ext_test", +}; + +typedef struct { + const char* const path; + bool is_dir; +} StorageTestPathDesc; + +const StorageTestPathDesc storage_test_dirwalk_full[] = { + {.path = "1", .is_dir = true}, + {.path = "11", .is_dir = true}, + {.path = "111", .is_dir = true}, + {.path = "1/2", .is_dir = true}, + {.path = "1/22", .is_dir = true}, + {.path = "1/222", .is_dir = true}, + {.path = "11/2", .is_dir = true}, + {.path = "111/2", .is_dir = true}, + {.path = "111/22", .is_dir = true}, + {.path = "111/22/33", .is_dir = true}, + {.path = "file1.test", .is_dir = false}, + {.path = "file2.test", .is_dir = false}, + {.path = "file3.ext_test", .is_dir = false}, + {.path = "1/file1.test", .is_dir = false}, + {.path = "111/22/33/file1.test", .is_dir = false}, + {.path = "111/22/33/file2.test", .is_dir = false}, + {.path = "111/22/33/file3.ext_test", .is_dir = false}, + {.path = "111/22/33/file4.ext_test", .is_dir = false}, +}; + +const StorageTestPathDesc storage_test_dirwalk_no_recursive[] = { + {.path = "1", .is_dir = true}, + {.path = "11", .is_dir = true}, + {.path = "111", .is_dir = true}, + {.path = "file1.test", .is_dir = false}, + {.path = "file2.test", .is_dir = false}, + {.path = "file3.ext_test", .is_dir = false}, +}; + +const StorageTestPathDesc storage_test_dirwalk_filter[] = { + {.path = "file1.test", .is_dir = false}, + {.path = "file2.test", .is_dir = false}, + {.path = "1/file1.test", .is_dir = false}, + {.path = "111/22/33/file1.test", .is_dir = false}, + {.path = "111/22/33/file2.test", .is_dir = false}, +}; + +typedef struct { + bool is_dir; + bool visited; +} StorageTestPath; + +DICT_DEF2(StorageTestPathDict, string_t, STRING_OPLIST, StorageTestPath, M_POD_OPLIST) + +static StorageTestPathDict_t* + storage_test_paths_alloc(const StorageTestPathDesc paths[], size_t paths_count) { + StorageTestPathDict_t* data = malloc(sizeof(StorageTestPathDict_t)); + StorageTestPathDict_init(*data); + + for(size_t i = 0; i < paths_count; i++) { + string_t key; + string_init_set(key, paths[i].path); + StorageTestPath value = { + .is_dir = paths[i].is_dir, + .visited = false, + }; + + StorageTestPathDict_set_at(*data, key, value); + string_clear(key); + } + + return data; +} + +static void storage_test_paths_free(StorageTestPathDict_t* data) { + StorageTestPathDict_clear(*data); + free(data); +} + +static bool storage_test_paths_mark(StorageTestPathDict_t* data, string_t path, bool is_dir) { + bool found = false; + + StorageTestPath* record = StorageTestPathDict_get(*data, path); + if(record) { + if(is_dir == record->is_dir) { + if(record->visited == false) { + record->visited = true; + found = true; + } + } + } + + return found; +} + +static bool storage_test_paths_check(StorageTestPathDict_t* data) { + bool error = false; + + StorageTestPathDict_it_t it; + for(StorageTestPathDict_it(it, *data); !StorageTestPathDict_end_p(it); + StorageTestPathDict_next(it)) { + const StorageTestPathDict_itref_t* itref = StorageTestPathDict_cref(it); + + if(itref->value.visited == false) { + error = true; + break; + } + } + + return error; +} + +static bool write_file_13DA(Storage* storage, const char* path) { + File* file = storage_file_alloc(storage); + bool result = false; + if(storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + result = storage_file_write(file, "13DA", 4) == 4; + } + storage_file_close(file); + storage_file_free(file); + + return result; +} + +static void storage_dirs_create(Storage* storage, const char* base) { + string_t path; + string_init(path); + + storage_common_mkdir(storage, base); + + for(size_t i = 0; i < COUNT_OF(storage_test_dirwalk_paths); i++) { + string_printf(path, "%s/%s", base, storage_test_dirwalk_paths[i]); + storage_common_mkdir(storage, string_get_cstr(path)); + } + + for(size_t i = 0; i < COUNT_OF(storage_test_dirwalk_files); i++) { + string_printf(path, "%s/%s", base, storage_test_dirwalk_files[i]); + write_file_13DA(storage, string_get_cstr(path)); + } + + string_clear(path); +} + +MU_TEST_1(test_dirwalk_full, Storage* storage) { + string_t path; + string_init(path); + FileInfo fileinfo; + + StorageTestPathDict_t* paths = + storage_test_paths_alloc(storage_test_dirwalk_full, COUNT_OF(storage_test_dirwalk_full)); + + DirWalk* dir_walk = dir_walk_alloc(storage); + mu_check(dir_walk_open(dir_walk, "/ext/dirwalk")); + + while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) { + string_right(path, strlen("/ext/dirwalk/")); + mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY))); + } + + dir_walk_free(dir_walk); + string_clear(path); + + mu_check(storage_test_paths_check(paths) == false); + + storage_test_paths_free(paths); +} + +MU_TEST_1(test_dirwalk_no_recursive, Storage* storage) { + string_t path; + string_init(path); + FileInfo fileinfo; + + StorageTestPathDict_t* paths = storage_test_paths_alloc( + storage_test_dirwalk_no_recursive, COUNT_OF(storage_test_dirwalk_no_recursive)); + + DirWalk* dir_walk = dir_walk_alloc(storage); + dir_walk_set_recursive(dir_walk, false); + mu_check(dir_walk_open(dir_walk, "/ext/dirwalk")); + + while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) { + string_right(path, strlen("/ext/dirwalk/")); + mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY))); + } + + dir_walk_free(dir_walk); + string_clear(path); + + mu_check(storage_test_paths_check(paths) == false); + + storage_test_paths_free(paths); +} + +static bool test_dirwalk_filter_no_folder_ext(const char* name, FileInfo* fileinfo, void* ctx) { + UNUSED(ctx); + + // only files + if(!(fileinfo->flags & FSF_DIRECTORY)) { + // with ".test" in name + if(strstr(name, ".test") != NULL) { + return true; + } + } + + return false; +} + +MU_TEST_1(test_dirwalk_filter, Storage* storage) { + string_t path; + string_init(path); + FileInfo fileinfo; + + StorageTestPathDict_t* paths = storage_test_paths_alloc( + storage_test_dirwalk_filter, COUNT_OF(storage_test_dirwalk_filter)); + + DirWalk* dir_walk = dir_walk_alloc(storage); + dir_walk_set_filter_cb(dir_walk, test_dirwalk_filter_no_folder_ext, NULL); + mu_check(dir_walk_open(dir_walk, "/ext/dirwalk")); + + while(dir_walk_read(dir_walk, path, &fileinfo) == DirWalkOK) { + string_right(path, strlen("/ext/dirwalk/")); + mu_check(storage_test_paths_mark(paths, path, (fileinfo.flags & FSF_DIRECTORY))); + } + + dir_walk_free(dir_walk); + string_clear(path); + + mu_check(storage_test_paths_check(paths) == false); + + storage_test_paths_free(paths); +} + +MU_TEST_SUITE(test_dirwalk_suite) { + Storage* storage = furi_record_open("storage"); + storage_dirs_create(storage, "/ext/dirwalk"); + + MU_RUN_TEST_1(test_dirwalk_full, storage); + MU_RUN_TEST_1(test_dirwalk_no_recursive, storage); + MU_RUN_TEST_1(test_dirwalk_filter, storage); + + storage_simply_remove_recursive(storage, "/ext/dirwalk"); + furi_record_close("storage"); +} + +int run_minunit_test_dirwalk() { + MU_RUN_SUITE(test_dirwalk_suite); + return MU_EXIT_CODE; +} \ No newline at end of file diff --git a/applications/unit_tests/storage/storage_test.c b/applications/unit_tests/storage/storage_test.c index adc5f330..9f237bd0 100644 --- a/applications/unit_tests/storage/storage_test.c +++ b/applications/unit_tests/storage/storage_test.c @@ -164,8 +164,153 @@ MU_TEST_SUITE(storage_dir) { MU_RUN_TEST(storage_dir_open_lock); } +static const char* const storage_copy_test_paths[] = { + "1", + "11", + "111", + "1/2", + "1/22", + "1/222", + "11/1", + "111/2", + "111/22", + "111/22/33", +}; + +static const char* const storage_copy_test_files[] = { + "file.test", + "1/file.test", + "111/22/33/file.test", +}; + +static bool write_file_13DA(Storage* storage, const char* path) { + File* file = storage_file_alloc(storage); + bool result = false; + if(storage_file_open(file, path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + result = storage_file_write(file, "13DA", 4) == 4; + } + storage_file_close(file); + storage_file_free(file); + + return result; +} + +static bool check_file_13DA(Storage* storage, const char* path) { + File* file = storage_file_alloc(storage); + bool result = false; + if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) { + char data[10] = {0}; + result = storage_file_read(file, data, 4) == 4; + if(result) { + result = memcmp(data, "13DA", 4) == 0; + } + } + storage_file_close(file); + storage_file_free(file); + + return result; +} + +static void storage_dir_create(Storage* storage, const char* base) { + string_t path; + string_init(path); + + storage_common_mkdir(storage, base); + + for(size_t i = 0; i < COUNT_OF(storage_copy_test_paths); i++) { + string_printf(path, "%s/%s", base, storage_copy_test_paths[i]); + storage_common_mkdir(storage, string_get_cstr(path)); + } + + for(size_t i = 0; i < COUNT_OF(storage_copy_test_files); i++) { + string_printf(path, "%s/%s", base, storage_copy_test_files[i]); + write_file_13DA(storage, string_get_cstr(path)); + } + + string_clear(path); +} + +static void storage_dir_remove(Storage* storage, const char* base) { + storage_simply_remove_recursive(storage, base); +} + +static bool storage_dir_rename_check(Storage* storage, const char* base) { + bool result = false; + string_t path; + string_init(path); + + result = (storage_common_stat(storage, base, NULL) == FSE_OK); + + if(result) { + for(size_t i = 0; i < COUNT_OF(storage_copy_test_paths); i++) { + string_printf(path, "%s/%s", base, storage_copy_test_paths[i]); + result = (storage_common_stat(storage, string_get_cstr(path), NULL) == FSE_OK); + if(!result) { + break; + } + } + } + + if(result) { + for(size_t i = 0; i < COUNT_OF(storage_copy_test_files); i++) { + string_printf(path, "%s/%s", base, storage_copy_test_files[i]); + result = check_file_13DA(storage, string_get_cstr(path)); + if(!result) { + break; + } + } + } + + string_clear(path); + return result; +} + +MU_TEST(storage_file_rename) { + Storage* storage = furi_record_open("storage"); + File* file = storage_file_alloc(storage); + + mu_check(write_file_13DA(storage, "/ext/file.old")); + mu_check(check_file_13DA(storage, "/ext/file.old")); + mu_assert_int_eq(FSE_OK, storage_common_rename(storage, "/ext/file.old", "/ext/file.new")); + mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, "/ext/file.old", NULL)); + mu_assert_int_eq(FSE_OK, storage_common_stat(storage, "/ext/file.new", NULL)); + mu_check(check_file_13DA(storage, "/ext/file.new")); + mu_assert_int_eq(FSE_OK, storage_common_remove(storage, "/ext/file.new")); + + storage_file_free(file); + furi_record_close("storage"); +} + +MU_TEST(storage_dir_rename) { + Storage* storage = furi_record_open("storage"); + + storage_dir_create(storage, "/ext/dir.old"); + + mu_check(storage_dir_rename_check(storage, "/ext/dir.old")); + + mu_assert_int_eq(FSE_OK, storage_common_rename(storage, "/ext/dir.old", "/ext/dir.new")); + mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, "/ext/dir.old", NULL)); + mu_check(storage_dir_rename_check(storage, "/ext/dir.new")); + + storage_dir_remove(storage, "/ext/dir.new"); + mu_assert_int_eq(FSE_NOT_EXIST, storage_common_stat(storage, "/ext/dir.new", NULL)); + + furi_record_close("storage"); +} + +MU_TEST_SUITE(storage_rename) { + MU_RUN_TEST(storage_file_rename); + MU_RUN_TEST(storage_dir_rename); + + Storage* storage = furi_record_open("storage"); + storage_dir_remove(storage, "/ext/dir.old"); + storage_dir_remove(storage, "/ext/dir.new"); + furi_record_close("storage"); +} + int run_minunit_test_storage() { MU_RUN_SUITE(storage_file); MU_RUN_SUITE(storage_dir); + MU_RUN_SUITE(storage_rename); return MU_EXIT_CODE; } diff --git a/applications/unit_tests/subghz/subghz_test.c b/applications/unit_tests/subghz/subghz_test.c index e0fb8b6b..191ca745 100644 --- a/applications/unit_tests/subghz/subghz_test.c +++ b/applications/unit_tests/subghz/subghz_test.c @@ -32,7 +32,7 @@ static void subghz_test_rx_callback( string_init(text); subghz_protocol_decoder_base_get_string(decoder_base, text); subghz_receiver_reset(receiver_handler); - FURI_LOG_I(TAG, "\r\n%s", string_get_cstr(text)); + FURI_LOG_T(TAG, "\r\n%s", string_get_cstr(text)); string_clear(text); subghz_test_decoder_count++; } @@ -54,7 +54,7 @@ static void subghz_test_deinit(void) { subghz_environment_free(environment_handler); } -static bool subghz_decode_test(const char* path, const char* name_decoder) { +static bool subghz_decoder_test(const char* path, const char* name_decoder) { subghz_test_decoder_count = 0; uint32_t test_start = furi_hal_get_tick(); @@ -64,18 +64,18 @@ static bool subghz_decode_test(const char* path, const char* name_decoder) { if(decoder) { file_worker_encoder_handler = subghz_file_encoder_worker_alloc(); if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path)) { - //the worker needs a file in order to open and read part of the file + // the worker needs a file in order to open and read part of the file osDelay(100); LevelDuration level_duration; while(furi_hal_get_tick() - test_start < TEST_TIMEOUT) { - furi_hal_delay_us( - 500); //you need to have time to read from the file from the SD card level_duration = subghz_file_encoder_worker_get_level_duration(file_worker_encoder_handler); if(!level_duration_is_reset(level_duration)) { bool level = level_duration_get_level(level_duration); uint32_t duration = level_duration_get_duration(level_duration); + // Yield, to load data inside the worker + osThreadYield(); decoder->protocol->decoder->feed(decoder, level, duration); } else { break; @@ -88,7 +88,7 @@ static bool subghz_decode_test(const char* path, const char* name_decoder) { } subghz_file_encoder_worker_free(file_worker_encoder_handler); } - FURI_LOG_I(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count); + FURI_LOG_T(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count); if(furi_hal_get_tick() - test_start > TEST_TIMEOUT) { printf("\033[0;31mTest decoder %s ERROR TimeOut\033[0m\r\n", name_decoder); return false; @@ -97,24 +97,25 @@ static bool subghz_decode_test(const char* path, const char* name_decoder) { } } -static bool subghz_decode_ramdom_test(const char* path) { +static bool subghz_decode_random_test(const char* path) { subghz_test_decoder_count = 0; subghz_receiver_reset(receiver_handler); uint32_t test_start = furi_hal_get_tick(); file_worker_encoder_handler = subghz_file_encoder_worker_alloc(); if(subghz_file_encoder_worker_start(file_worker_encoder_handler, path)) { - //the worker needs a file in order to open and read part of the file + // the worker needs a file in order to open and read part of the file osDelay(100); LevelDuration level_duration; while(furi_hal_get_tick() - test_start < TEST_TIMEOUT * 10) { - furi_hal_delay_us(500); //you need to have time to read from the file from the SD card level_duration = subghz_file_encoder_worker_get_level_duration(file_worker_encoder_handler); if(!level_duration_is_reset(level_duration)) { bool level = level_duration_get_level(level_duration); uint32_t duration = level_duration_get_duration(level_duration); + // Yield, to load data inside the worker + osThreadYield(); subghz_receiver_decode(receiver_handler, level, duration); } else { break; @@ -126,7 +127,7 @@ static bool subghz_decode_ramdom_test(const char* path) { } subghz_file_encoder_worker_free(file_worker_encoder_handler); } - FURI_LOG_I(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count); + FURI_LOG_T(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count); if(furi_hal_get_tick() - test_start > TEST_TIMEOUT * 10) { printf("\033[0;31mRandom test ERROR TimeOut\033[0m\r\n"); return false; @@ -137,7 +138,7 @@ static bool subghz_decode_ramdom_test(const char* path) { } } -static bool subghz_ecode_test(const char* path) { +static bool subghz_encoder_test(const char* path) { subghz_test_decoder_count = 0; uint32_t test_start = furi_hal_get_tick(); string_t temp_str; @@ -189,7 +190,7 @@ static bool subghz_ecode_test(const char* path) { subghz_transmitter_free(transmitter); } flipper_format_free(fff_data_file); - FURI_LOG_I(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count); + FURI_LOG_T(TAG, "\r\n Decoder count parse \033[0;33m%d\033[0m ", subghz_test_decoder_count); if(furi_hal_get_tick() - test_start > TEST_TIMEOUT) { printf("\033[0;31mTest encoder %s ERROR TimeOut\033[0m\r\n", string_get_cstr(temp_str)); subghz_test_decoder_count = 0; @@ -207,167 +208,166 @@ MU_TEST(subghz_keystore_test) { MU_TEST(subghz_decoder_came_atomo_test) { mu_assert( - subghz_decode_test( + subghz_decoder_test( "/ext/unit_tests/subghz/came_atomo_raw.sub", SUBGHZ_PROTOCOL_CAME_ATOMO_NAME), "Test decoder " SUBGHZ_PROTOCOL_CAME_ATOMO_NAME " error\r\n"); } MU_TEST(subghz_decoder_came_test) { mu_assert( - subghz_decode_test("/ext/unit_tests/subghz/came_raw.sub", SUBGHZ_PROTOCOL_CAME_NAME), + subghz_decoder_test("/ext/unit_tests/subghz/came_raw.sub", SUBGHZ_PROTOCOL_CAME_NAME), "Test decoder " SUBGHZ_PROTOCOL_CAME_NAME " error\r\n"); } MU_TEST(subghz_decoder_came_twee_test) { mu_assert( - subghz_decode_test( + subghz_decoder_test( "/ext/unit_tests/subghz/came_twee_raw.sub", SUBGHZ_PROTOCOL_CAME_TWEE_NAME), "Test decoder " SUBGHZ_PROTOCOL_CAME_TWEE_NAME " error\r\n"); } MU_TEST(subghz_decoder_faac_slh_test) { mu_assert( - subghz_decode_test( + subghz_decoder_test( "/ext/unit_tests/subghz/faac_slh_raw.sub", SUBGHZ_PROTOCOL_FAAC_SLH_NAME), "Test decoder " SUBGHZ_PROTOCOL_FAAC_SLH_NAME " error\r\n"); } MU_TEST(subghz_decoder_gate_tx_test) { mu_assert( - subghz_decode_test("/ext/unit_tests/subghz/gate_tx_raw.sub", SUBGHZ_PROTOCOL_GATE_TX_NAME), + subghz_decoder_test("/ext/unit_tests/subghz/gate_tx_raw.sub", SUBGHZ_PROTOCOL_GATE_TX_NAME), "Test decoder " SUBGHZ_PROTOCOL_GATE_TX_NAME " error\r\n"); } MU_TEST(subghz_decoder_hormann_hsm_test) { mu_assert( - subghz_decode_test( + subghz_decoder_test( "/ext/unit_tests/subghz/hormann_hsm_raw.sub", SUBGHZ_PROTOCOL_HORMANN_HSM_NAME), "Test decoder " SUBGHZ_PROTOCOL_HORMANN_HSM_NAME " error\r\n"); } MU_TEST(subghz_decoder_ido_test) { mu_assert( - subghz_decode_test("/ext/unit_tests/subghz/ido_117_111_raw.sub", SUBGHZ_PROTOCOL_IDO_NAME), + subghz_decoder_test("/ext/unit_tests/subghz/ido_117_111_raw.sub", SUBGHZ_PROTOCOL_IDO_NAME), "Test decoder " SUBGHZ_PROTOCOL_IDO_NAME " error\r\n"); } MU_TEST(subghz_decoder_keelog_test) { mu_assert( - subghz_decode_test("/ext/unit_tests/subghz/doorhan_raw.sub", SUBGHZ_PROTOCOL_KEELOQ_NAME), + subghz_decoder_test("/ext/unit_tests/subghz/doorhan_raw.sub", SUBGHZ_PROTOCOL_KEELOQ_NAME), "Test decoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n"); } MU_TEST(subghz_decoder_kia_seed_test) { mu_assert( - subghz_decode_test("/ext/unit_tests/subghz/kia_seed_raw.sub", SUBGHZ_PROTOCOL_KIA_NAME), + subghz_decoder_test("/ext/unit_tests/subghz/kia_seed_raw.sub", SUBGHZ_PROTOCOL_KIA_NAME), "Test decoder " SUBGHZ_PROTOCOL_KIA_NAME " error\r\n"); } MU_TEST(subghz_decoder_nero_radio_test) { mu_assert( - subghz_decode_test( + subghz_decoder_test( "/ext/unit_tests/subghz/nero_radio_raw.sub", SUBGHZ_PROTOCOL_NERO_RADIO_NAME), "Test decoder " SUBGHZ_PROTOCOL_NERO_RADIO_NAME " error\r\n"); } MU_TEST(subghz_decoder_nero_sketch_test) { mu_assert( - subghz_decode_test( + subghz_decoder_test( "/ext/unit_tests/subghz/nero_sketch_raw.sub", SUBGHZ_PROTOCOL_NERO_SKETCH_NAME), "Test decoder " SUBGHZ_PROTOCOL_NERO_SKETCH_NAME " error\r\n"); } MU_TEST(subghz_decoder_nice_flo_test) { mu_assert( - subghz_decode_test( + subghz_decoder_test( "/ext/unit_tests/subghz/nice_flo_raw.sub", SUBGHZ_PROTOCOL_NICE_FLO_NAME), "Test decoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n"); } MU_TEST(subghz_decoder_nice_flor_s_test) { mu_assert( - subghz_decode_test( + subghz_decoder_test( "/ext/unit_tests/subghz/nice_flor_s_raw.sub", SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME), "Test decoder " SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME " error\r\n"); } MU_TEST(subghz_decoder_princeton_test) { mu_assert( - subghz_decode_test( + subghz_decoder_test( "/ext/unit_tests/subghz/Princeton_raw.sub", SUBGHZ_PROTOCOL_PRINCETON_NAME), "Test decoder " SUBGHZ_PROTOCOL_PRINCETON_NAME " error\r\n"); } MU_TEST(subghz_decoder_scher_khan_magic_code_test) { mu_assert( - subghz_decode_test( + subghz_decoder_test( "/ext/unit_tests/subghz/scher_khan_magic_code.sub", SUBGHZ_PROTOCOL_SCHER_KHAN_NAME), "Test decoder " SUBGHZ_PROTOCOL_SCHER_KHAN_NAME " error\r\n"); } MU_TEST(subghz_decoder_somfy_keytis_test) { mu_assert( - subghz_decode_test( + subghz_decoder_test( "/ext/unit_tests/subghz/Somfy_keytis_raw.sub", SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME), "Test decoder " SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME " error\r\n"); } MU_TEST(subghz_decoder_somfy_telis_test) { mu_assert( - subghz_decode_test( + subghz_decoder_test( "/ext/unit_tests/subghz/somfy_telis_raw.sub", SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME), "Test decoder " SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME " error\r\n"); } MU_TEST(subghz_decoder_star_line_test) { mu_assert( - subghz_decode_test("/ext/unit_tests/subghz/cenmax_raw.sub", SUBGHZ_PROTOCOL_STAR_LINE_NAME), + subghz_decoder_test( + "/ext/unit_tests/subghz/cenmax_raw.sub", SUBGHZ_PROTOCOL_STAR_LINE_NAME), "Test decoder " SUBGHZ_PROTOCOL_STAR_LINE_NAME " error\r\n"); } -MU_TEST(subghz_ecoder_princeton_test) { +MU_TEST(subghz_encoder_princeton_test) { mu_assert( - subghz_ecode_test("/ext/unit_tests/subghz/princeton.sub"), + subghz_encoder_test("/ext/unit_tests/subghz/princeton.sub"), "Test encoder " SUBGHZ_PROTOCOL_PRINCETON_NAME " error\r\n"); } -MU_TEST(subghz_ecoder_came_test) { +MU_TEST(subghz_encoder_came_test) { mu_assert( - subghz_ecode_test("/ext/unit_tests/subghz/came.sub"), + subghz_encoder_test("/ext/unit_tests/subghz/came.sub"), "Test encoder " SUBGHZ_PROTOCOL_CAME_NAME " error\r\n"); } -MU_TEST(subghz_ecoder_came_twee_test) { +MU_TEST(subghz_encoder_came_twee_test) { mu_assert( - subghz_ecode_test("/ext/unit_tests/subghz/came_twee.sub"), + subghz_encoder_test("/ext/unit_tests/subghz/came_twee.sub"), "Test encoder " SUBGHZ_PROTOCOL_CAME_TWEE_NAME " error\r\n"); } -MU_TEST(subghz_ecoder_gate_tx_test) { +MU_TEST(subghz_encoder_gate_tx_test) { mu_assert( - subghz_ecode_test("/ext/unit_tests/subghz/gate_tx.sub"), + subghz_encoder_test("/ext/unit_tests/subghz/gate_tx.sub"), "Test encoder " SUBGHZ_PROTOCOL_GATE_TX_NAME " error\r\n"); } -MU_TEST(subghz_ecoder_nice_flo_test) { +MU_TEST(subghz_encoder_nice_flo_test) { mu_assert( - subghz_ecode_test("/ext/unit_tests/subghz/nice_flo.sub"), + subghz_encoder_test("/ext/unit_tests/subghz/nice_flo.sub"), "Test encoder " SUBGHZ_PROTOCOL_NICE_FLO_NAME " error\r\n"); } -MU_TEST(subghz_ecoder_keelog_test) { +MU_TEST(subghz_encoder_keelog_test) { mu_assert( - subghz_ecode_test("/ext/unit_tests/subghz/doorhan.sub"), + subghz_encoder_test("/ext/unit_tests/subghz/doorhan.sub"), "Test encoder " SUBGHZ_PROTOCOL_KEELOQ_NAME " error\r\n"); } MU_TEST(subghz_random_test) { - mu_assert(subghz_decode_ramdom_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); + mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); } MU_TEST_SUITE(subghz) { - //MU_SUITE_CONFIGURE(&subghz_test_init, &subghz_test_deinit); - subghz_test_init(); MU_RUN_TEST(subghz_keystore_test); @@ -390,12 +390,12 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_somfy_telis_test); MU_RUN_TEST(subghz_decoder_star_line_test); - MU_RUN_TEST(subghz_ecoder_princeton_test); - MU_RUN_TEST(subghz_ecoder_came_test); - MU_RUN_TEST(subghz_ecoder_came_twee_test); - MU_RUN_TEST(subghz_ecoder_gate_tx_test); - MU_RUN_TEST(subghz_ecoder_nice_flo_test); - MU_RUN_TEST(subghz_ecoder_keelog_test); + MU_RUN_TEST(subghz_encoder_princeton_test); + MU_RUN_TEST(subghz_encoder_came_test); + MU_RUN_TEST(subghz_encoder_came_twee_test); + MU_RUN_TEST(subghz_encoder_gate_tx_test); + MU_RUN_TEST(subghz_encoder_nice_flo_test); + MU_RUN_TEST(subghz_encoder_keelog_test); MU_RUN_TEST(subghz_random_test); subghz_test_deinit(); diff --git a/applications/unit_tests/test_index.c b/applications/unit_tests/test_index.c index eda0fe4a..4045c8e2 100644 --- a/applications/unit_tests/test_index.c +++ b/applications/unit_tests/test_index.c @@ -18,6 +18,7 @@ int run_minunit_test_flipper_format_string(); int run_minunit_test_stream(); int run_minunit_test_storage(); int run_minunit_test_subghz(); +int run_minunit_test_dirwalk(); void minunit_print_progress(void) { static char progress[] = {'\\', '|', '/', '-'}; @@ -60,6 +61,7 @@ void unit_tests_cli(Cli* cli, string_t args, void* context) { test_result |= run_minunit(); test_result |= run_minunit_test_storage(); test_result |= run_minunit_test_stream(); + test_result |= run_minunit_test_dirwalk(); test_result |= run_minunit_test_flipper_format(); test_result |= run_minunit_test_flipper_format_string(); test_result |= run_minunit_test_infrared_decoder_encoder(); diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c index abc679bc..83cf90b7 100644 --- a/lib/subghz/subghz_file_encoder_worker.c +++ b/lib/subghz/subghz_file_encoder_worker.c @@ -54,27 +54,24 @@ void subghz_file_encoder_worker_add_level_duration( } } -bool subghz_file_encoder_worker_data_parse( - SubGhzFileEncoderWorker* instance, - const char* strStart, - size_t len) { +bool subghz_file_encoder_worker_data_parse(SubGhzFileEncoderWorker* instance, const char* strStart) { char* str1; - size_t ind_start = (size_t)strStart; //store the start address of the beginning of the line bool res = false; + // Line sample: "RAW_Data: -1, 2, -2..." + + // Look for a key in the line + str1 = strstr(strStart, "RAW_Data: "); - str1 = strstr( - strStart, "RAW_Data: "); //looking for the beginning of the desired title in the line if(str1 != NULL) { - str1 = strchr( - str1, - ' '); //if found, shift the pointer by 1 element per line "RAW_Data: -1, 2, -2..." - while( - strchr(str1, ' ') != NULL && - ((size_t)str1 < - (len + - ind_start))) { //check that there is still an element in the line and that it has not gone beyond the line + // Skip key + str1 = strchr(str1, ' '); + + // Check that there is still an element in the line + while(strchr(str1, ' ') != NULL) { str1 = strchr(str1, ' '); - str1 += 1; //if found, shift the pointer by next element per line + + // Skip space + str1 += 1; subghz_file_encoder_worker_add_level_duration(instance, atoi(str1)); } res = true; @@ -143,9 +140,7 @@ static int32_t subghz_file_encoder_worker_thread(void* context) { if(stream_read_line(stream, instance->str_data)) { string_strim(instance->str_data); if(!subghz_file_encoder_worker_data_parse( - instance, - string_get_cstr(instance->str_data), - strlen(string_get_cstr(instance->str_data)))) { + instance, string_get_cstr(instance->str_data))) { //to stop DMA correctly subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); diff --git a/lib/toolbox/dir_walk.c b/lib/toolbox/dir_walk.c new file mode 100644 index 00000000..2efbc0f1 --- /dev/null +++ b/lib/toolbox/dir_walk.c @@ -0,0 +1,152 @@ +#include "dir_walk.h" +#include + +LIST_DEF(DirIndexList, uint32_t); + +struct DirWalk { + File* file; + string_t path; + DirIndexList_t index_list; + uint32_t current_index; + bool recursive; + DirWalkFilterCb filter_cb; + void* filter_context; +}; + +DirWalk* dir_walk_alloc(Storage* storage) { + DirWalk* dir_walk = malloc(sizeof(DirWalk)); + string_init(dir_walk->path); + dir_walk->file = storage_file_alloc(storage); + DirIndexList_init(dir_walk->index_list); + dir_walk->recursive = true; + dir_walk->filter_cb = NULL; + return dir_walk; +} + +void dir_walk_free(DirWalk* dir_walk) { + storage_file_free(dir_walk->file); + string_clear(dir_walk->path); + DirIndexList_clear(dir_walk->index_list); + free(dir_walk); +} + +void dir_walk_set_recursive(DirWalk* dir_walk, bool recursive) { + dir_walk->recursive = recursive; +} + +void dir_walk_set_filter_cb(DirWalk* dir_walk, DirWalkFilterCb cb, void* context) { + dir_walk->filter_cb = cb; + dir_walk->filter_context = context; +} + +bool dir_walk_open(DirWalk* dir_walk, const char* path) { + string_set(dir_walk->path, path); + dir_walk->current_index = 0; + return storage_dir_open(dir_walk->file, path); +} + +static bool dir_walk_filter(DirWalk* dir_walk, const char* name, FileInfo* fileinfo) { + if(dir_walk->filter_cb) { + return dir_walk->filter_cb(name, fileinfo, dir_walk->filter_context); + } else { + return true; + } +} + +static DirWalkResult dir_walk_iter(DirWalk* dir_walk, string_t return_path, FileInfo* fileinfo) { + DirWalkResult result = DirWalkError; + char* name = malloc(256); + FileInfo info; + bool end = false; + + while(!end) { + storage_dir_read(dir_walk->file, &info, name, 255); + + if(storage_file_get_error(dir_walk->file) == FSE_OK) { + result = DirWalkOK; + dir_walk->current_index++; + + if(dir_walk_filter(dir_walk, name, &info)) { + if(return_path != NULL) { + string_printf(return_path, "%s/%s", string_get_cstr(dir_walk->path), name); + } + + if(fileinfo != NULL) { + memcpy(fileinfo, &info, sizeof(FileInfo)); + } + + end = true; + } + + if((info.flags & FSF_DIRECTORY) && dir_walk->recursive) { + // step into + DirIndexList_push_back(dir_walk->index_list, dir_walk->current_index); + dir_walk->current_index = 0; + storage_dir_close(dir_walk->file); + + string_cat_printf(dir_walk->path, "/%s", name); + storage_dir_open(dir_walk->file, string_get_cstr(dir_walk->path)); + } + } else if(storage_file_get_error(dir_walk->file) == FSE_NOT_EXIST) { + if(DirIndexList_size(dir_walk->index_list) == 0) { + // last + result = DirWalkLast; + end = true; + } else { + // step out + uint32_t index; + DirIndexList_pop_back(&index, dir_walk->index_list); + dir_walk->current_index = 0; + + storage_dir_close(dir_walk->file); + + size_t last_char = string_search_rchar(dir_walk->path, '/'); + if(last_char != STRING_FAILURE) { + string_left(dir_walk->path, last_char); + } + + storage_dir_open(dir_walk->file, string_get_cstr(dir_walk->path)); + + // rewind + while(true) { + if(index == dir_walk->current_index) { + result = DirWalkOK; + break; + } + + if(!storage_dir_read(dir_walk->file, &info, name, 255)) { + result = DirWalkError; + end = true; + break; + } + + dir_walk->current_index++; + } + } + } else { + result = DirWalkError; + end = true; + } + } + + free(name); + return result; +} + +FS_Error dir_walk_get_error(DirWalk* dir_walk) { + return storage_file_get_error(dir_walk->file); +} + +DirWalkResult dir_walk_read(DirWalk* dir_walk, string_t return_path, FileInfo* fileinfo) { + return dir_walk_iter(dir_walk, return_path, fileinfo); +} + +void dir_walk_close(DirWalk* dir_walk) { + if(storage_file_is_open(dir_walk->file)) { + storage_dir_close(dir_walk->file); + } + + DirIndexList_reset(dir_walk->index_list); + string_reset(dir_walk->path); + dir_walk->current_index = 0; +} diff --git a/lib/toolbox/dir_walk.h b/lib/toolbox/dir_walk.h new file mode 100644 index 00000000..fc651042 --- /dev/null +++ b/lib/toolbox/dir_walk.h @@ -0,0 +1,79 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DirWalk DirWalk; + +typedef enum { + DirWalkOK, /**< OK */ + DirWalkError, /**< Error */ + DirWalkLast, /**< Last element */ +} DirWalkResult; + +typedef bool (*DirWalkFilterCb)(const char* name, FileInfo* fileinfo, void* ctx); + +/** + * Allocate DirWalk + * @param storage + * @return DirWalk* + */ +DirWalk* dir_walk_alloc(Storage* storage); + +/** + * Free DirWalk + * @param dir_walk + */ +void dir_walk_free(DirWalk* dir_walk); + +/** + * Set recursive mode (true by default) + * @param dir_walk + * @param recursive + */ +void dir_walk_set_recursive(DirWalk* dir_walk, bool recursive); + +/** + * Set filter callback (Should return true if the data is valid) + * @param dir_walk + * @param cb + * @param context + */ +void dir_walk_set_filter_cb(DirWalk* dir_walk, DirWalkFilterCb cb, void* context); + +/** + * Open directory + * @param dir_walk + * @param path + * @return true + * @return false + */ +bool dir_walk_open(DirWalk* dir_walk, const char* path); + +/** + * Get error id + * @param dir_walk + * @return FS_Error + */ +FS_Error dir_walk_get_error(DirWalk* dir_walk); + +/** + * Read next element from directory + * @param dir_walk + * @param return_path + * @param fileinfo + * @return DirWalkResult + */ +DirWalkResult dir_walk_read(DirWalk* dir_walk, string_t return_path, FileInfo* fileinfo); + +/** + * Close directory + * @param dir_walk + */ +void dir_walk_close(DirWalk* dir_walk); + +#ifdef __cplusplus +} +#endif \ No newline at end of file