Infrared: fix RAW parsing in cli. Remove dead sources. (#1255)
* Infrared: fix RAW parsing in cli. Remove dead sources.
This commit is contained in:
		
							parent
							
								
									8d737133fe
								
							
						
					
					
						commit
						8a81b79e00
					
				| @ -1,6 +1,5 @@ | |||||||
| #include <furi_hal_delay.h> | #include <furi_hal_delay.h> | ||||||
| #include <infrared.h> | #include <infrared.h> | ||||||
| #include <app_template.h> |  | ||||||
| #include <cli/cli.h> | #include <cli/cli.h> | ||||||
| #include <cmsis_os2.h> | #include <cmsis_os2.h> | ||||||
| #include <infrared_worker.h> | #include <infrared_worker.h> | ||||||
| @ -86,7 +85,7 @@ static void infrared_cli_print_usage(void) { | |||||||
|     } |     } | ||||||
|     printf("\r\n"); |     printf("\r\n"); | ||||||
|     printf("\tRaw format:\r\n"); |     printf("\tRaw format:\r\n"); | ||||||
|     printf("\tir_tx RAW F:<frequency> DC:<duty_cycle> <sample0> <sample1>...\r\n"); |     printf("\tir tx RAW F:<frequency> DC:<duty_cycle> <sample0> <sample1>...\r\n"); | ||||||
|     printf( |     printf( | ||||||
|         "\tFrequency (%d - %d), Duty cycle (0 - 100), max 512 samples\r\n", |         "\tFrequency (%d - %d), Duty cycle (0 - 100), max 512 samples\r\n", | ||||||
|         INFRARED_MIN_FREQUENCY, |         INFRARED_MIN_FREQUENCY, | ||||||
| @ -178,7 +177,7 @@ static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) { | |||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|             if(string_get_cstr(args)[size] == ' ') { |             if(string_get_cstr(args)[size] == ' ') { | ||||||
|                 string_right(args, size); |                 string_right(args, size + 1); | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -1,443 +0,0 @@ | |||||||
| #include "file_worker.h" |  | ||||||
| #include <m-string.h> |  | ||||||
| #include <lib/toolbox/hex.h> |  | ||||||
| #include <dialogs/dialogs.h> |  | ||||||
| #include <furi.h> |  | ||||||
| 
 |  | ||||||
| struct FileWorker { |  | ||||||
|     Storage* api; |  | ||||||
|     bool silent; |  | ||||||
|     File* file; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| bool file_worker_check_common_errors(FileWorker* file_worker); |  | ||||||
| void file_worker_show_error_internal(FileWorker* file_worker, const char* error_text); |  | ||||||
| bool file_worker_read_internal(FileWorker* file_worker, void* buffer, uint16_t bytes_to_read); |  | ||||||
| bool file_worker_write_internal( |  | ||||||
|     FileWorker* file_worker, |  | ||||||
|     const void* buffer, |  | ||||||
|     uint16_t bytes_to_write); |  | ||||||
| bool file_worker_tell_internal(FileWorker* file_worker, uint64_t* position); |  | ||||||
| bool file_worker_seek_internal(FileWorker* file_worker, uint64_t position, bool from_start); |  | ||||||
| 
 |  | ||||||
| FileWorker* file_worker_alloc(bool _silent) { |  | ||||||
|     FileWorker* file_worker = malloc(sizeof(FileWorker)); |  | ||||||
|     file_worker->silent = _silent; |  | ||||||
|     file_worker->api = furi_record_open("storage"); |  | ||||||
|     file_worker->file = storage_file_alloc(file_worker->api); |  | ||||||
| 
 |  | ||||||
|     return file_worker; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void file_worker_free(FileWorker* file_worker) { |  | ||||||
|     storage_file_free(file_worker->file); |  | ||||||
|     furi_record_close("storage"); |  | ||||||
|     free(file_worker); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_open( |  | ||||||
|     FileWorker* file_worker, |  | ||||||
|     const char* filename, |  | ||||||
|     FS_AccessMode access_mode, |  | ||||||
|     FS_OpenMode open_mode) { |  | ||||||
|     bool result = storage_file_open(file_worker->file, filename, access_mode, open_mode); |  | ||||||
| 
 |  | ||||||
|     if(!result) { |  | ||||||
|         file_worker_show_error_internal(file_worker, "Cannot open\nfile"); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return file_worker_check_common_errors(file_worker); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_close(FileWorker* file_worker) { |  | ||||||
|     if(storage_file_is_open(file_worker->file)) { |  | ||||||
|         storage_file_close(file_worker->file); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return file_worker_check_common_errors(file_worker); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_mkdir(FileWorker* file_worker, const char* dirname) { |  | ||||||
|     FS_Error fs_result = storage_common_mkdir(file_worker->api, dirname); |  | ||||||
| 
 |  | ||||||
|     if(fs_result != FSE_OK && fs_result != FSE_EXIST) { |  | ||||||
|         file_worker_show_error_internal(file_worker, "Cannot create\nfolder"); |  | ||||||
|         return false; |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     return file_worker_check_common_errors(file_worker); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_remove(FileWorker* file_worker, const char* filename) { |  | ||||||
|     FS_Error fs_result = storage_common_remove(file_worker->api, filename); |  | ||||||
|     if(fs_result != FSE_OK && fs_result != FSE_NOT_EXIST) { |  | ||||||
|         file_worker_show_error_internal(file_worker, "Cannot remove\nold file"); |  | ||||||
|         return false; |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     return file_worker_check_common_errors(file_worker); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void file_worker_get_next_filename( |  | ||||||
|     FileWorker* file_worker, |  | ||||||
|     const char* dirname, |  | ||||||
|     const char* filename, |  | ||||||
|     const char* fileextension, |  | ||||||
|     string_t nextfilename) { |  | ||||||
|     string_t temp_str; |  | ||||||
|     string_init(temp_str); |  | ||||||
|     uint16_t num = 0; |  | ||||||
| 
 |  | ||||||
|     string_printf(temp_str, "%s/%s%s", dirname, filename, fileextension); |  | ||||||
| 
 |  | ||||||
|     while(storage_common_stat(file_worker->api, string_get_cstr(temp_str), NULL) == FSE_OK) { |  | ||||||
|         num++; |  | ||||||
|         string_printf(temp_str, "%s/%s%d%s", dirname, filename, num, fileextension); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(num) { |  | ||||||
|         string_printf(nextfilename, "%s%d", filename, num); |  | ||||||
|     } else { |  | ||||||
|         string_printf(nextfilename, "%s", filename); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     string_clear(temp_str); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_read(FileWorker* file_worker, void* buffer, uint16_t bytes_to_read) { |  | ||||||
|     if(!file_worker_read_internal(file_worker, buffer, bytes_to_read)) { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return file_worker_check_common_errors(file_worker); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_read_until(FileWorker* file_worker, string_t str_result, char separator) { |  | ||||||
|     string_reset(str_result); |  | ||||||
|     const uint8_t buffer_size = 32; |  | ||||||
|     uint8_t buffer[buffer_size]; |  | ||||||
| 
 |  | ||||||
|     do { |  | ||||||
|         uint16_t read_count = storage_file_read(file_worker->file, buffer, buffer_size); |  | ||||||
|         if(storage_file_get_error(file_worker->file) != FSE_OK) { |  | ||||||
|             file_worker_show_error_internal(file_worker, "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(!file_worker_tell_internal(file_worker, &position)) { |  | ||||||
|                     return false; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 position = position - read_count + i + 1; |  | ||||||
| 
 |  | ||||||
|                 if(!file_worker_seek_internal(file_worker, position, true)) { |  | ||||||
|                     return false; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 result = true; |  | ||||||
|                 break; |  | ||||||
|             } else { |  | ||||||
|                 string_push_back(str_result, buffer[i]); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(result || read_count == 0) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } while(true); |  | ||||||
| 
 |  | ||||||
|     return file_worker_check_common_errors(file_worker); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_read_hex(FileWorker* file_worker, uint8_t* buffer, uint16_t bytes_to_read) { |  | ||||||
|     uint8_t hi_nibble_value, low_nibble_value; |  | ||||||
|     uint8_t text[2]; |  | ||||||
| 
 |  | ||||||
|     for(uint8_t i = 0; i < bytes_to_read; i++) { |  | ||||||
|         if(i != 0) { |  | ||||||
|             // space
 |  | ||||||
|             if(!file_worker_read_internal(file_worker, text, 1)) { |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // actual data
 |  | ||||||
|         if(!file_worker_read_internal(file_worker, text, 2)) { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // convert hex value to byte
 |  | ||||||
|         if(hex_char_to_hex_nibble(text[0], &hi_nibble_value) && |  | ||||||
|            hex_char_to_hex_nibble(text[1], &low_nibble_value)) { |  | ||||||
|             buffer[i] = (hi_nibble_value << 4) | low_nibble_value; |  | ||||||
|         } else { |  | ||||||
|             file_worker_show_error_internal(file_worker, "Cannot parse\nfile"); |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return file_worker_check_common_errors(file_worker); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_tell(FileWorker* file_worker, uint64_t* position) { |  | ||||||
|     if(!file_worker_tell_internal(file_worker, position)) { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return file_worker_check_common_errors(file_worker); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_seek(FileWorker* file_worker, uint64_t position, bool from_start) { |  | ||||||
|     if(!file_worker_seek_internal(file_worker, position, from_start)) { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return file_worker_check_common_errors(file_worker); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_write(FileWorker* file_worker, const void* buffer, uint16_t bytes_to_write) { |  | ||||||
|     if(!file_worker_write_internal(file_worker, buffer, bytes_to_write)) { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return file_worker_check_common_errors(file_worker); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_write_hex(FileWorker* file_worker, 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(!file_worker_write_internal(file_worker, space, 1)) { |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if(!file_worker_write_internal(file_worker, byte_text, 2)) { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return file_worker_check_common_errors(file_worker); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void file_worker_show_error(FileWorker* file_worker, const char* error_text) { |  | ||||||
|     UNUSED(file_worker); |  | ||||||
|     DialogsApp* dialogs = furi_record_open("dialogs"); |  | ||||||
| 
 |  | ||||||
|     DialogMessage* message = dialog_message_alloc(); |  | ||||||
|     dialog_message_set_text(message, error_text, 88, 32, AlignCenter, AlignCenter); |  | ||||||
|     dialog_message_set_icon(message, &I_SDQuestion_35x43, 5, 6); |  | ||||||
|     dialog_message_set_buttons(message, "Back", NULL, NULL); |  | ||||||
|     dialog_message_show(dialogs, message); |  | ||||||
|     dialog_message_free(message); |  | ||||||
| 
 |  | ||||||
|     furi_record_close("dialogs"); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_file_select( |  | ||||||
|     FileWorker* file_worker, |  | ||||||
|     const char* path, |  | ||||||
|     const char* extension, |  | ||||||
|     char* result, |  | ||||||
|     uint8_t result_size, |  | ||||||
|     const char* selected_filename) { |  | ||||||
|     UNUSED(file_worker); |  | ||||||
|     DialogsApp* dialogs = furi_record_open("dialogs"); |  | ||||||
|     bool ret = |  | ||||||
|         dialog_file_select_show(dialogs, path, extension, result, result_size, selected_filename); |  | ||||||
|     furi_record_close("dialogs"); |  | ||||||
|     return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_check_common_errors(FileWorker* file_worker) { |  | ||||||
|     UNUSED(file_worker); |  | ||||||
|     //TODO remove
 |  | ||||||
|     /* TODO: [FL-1431] Add return value to file_parser.get_sd_api().check_error() and replace get_fs_info(). */ |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void file_worker_show_error_internal(FileWorker* file_worker, const char* error_text) { |  | ||||||
|     if(!file_worker->silent) { |  | ||||||
|         file_worker_show_error(file_worker, error_text); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_read_internal(FileWorker* file_worker, void* buffer, uint16_t bytes_to_read) { |  | ||||||
|     uint16_t read_count = storage_file_read(file_worker->file, buffer, bytes_to_read); |  | ||||||
| 
 |  | ||||||
|     if(storage_file_get_error(file_worker->file) != FSE_OK || read_count != bytes_to_read) { |  | ||||||
|         file_worker_show_error_internal(file_worker, "Cannot read\nfile"); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_write_internal( |  | ||||||
|     FileWorker* file_worker, |  | ||||||
|     const void* buffer, |  | ||||||
|     uint16_t bytes_to_write) { |  | ||||||
|     uint16_t write_count = storage_file_write(file_worker->file, buffer, bytes_to_write); |  | ||||||
| 
 |  | ||||||
|     if(storage_file_get_error(file_worker->file) != FSE_OK || write_count != bytes_to_write) { |  | ||||||
|         file_worker_show_error_internal(file_worker, "Cannot write\nto file"); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_tell_internal(FileWorker* file_worker, uint64_t* position) { |  | ||||||
|     *position = storage_file_tell(file_worker->file); |  | ||||||
| 
 |  | ||||||
|     if(storage_file_get_error(file_worker->file) != FSE_OK) { |  | ||||||
|         file_worker_show_error_internal(file_worker, "Cannot tell\nfile offset"); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_seek_internal(FileWorker* file_worker, uint64_t position, bool from_start) { |  | ||||||
|     storage_file_seek(file_worker->file, position, from_start); |  | ||||||
|     if(storage_file_get_error(file_worker->file) != FSE_OK) { |  | ||||||
|         file_worker_show_error_internal(file_worker, "Cannot seek\nfile"); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_read_until_buffered( |  | ||||||
|     FileWorker* file_worker, |  | ||||||
|     string_t str_result, |  | ||||||
|     char* file_buf, |  | ||||||
|     size_t* file_buf_cnt, |  | ||||||
|     size_t file_buf_size, |  | ||||||
|     char separator) { |  | ||||||
|     furi_assert(string_capacity(str_result) > 0); |  | ||||||
| 
 |  | ||||||
|     // fs_api->file.read now supports up to 512 bytes reading at a time
 |  | ||||||
|     furi_assert(file_buf_size <= 512); |  | ||||||
| 
 |  | ||||||
|     string_reset(str_result); |  | ||||||
|     size_t newline_index = 0; |  | ||||||
|     bool found_eol = false; |  | ||||||
|     bool max_length_exceeded = false; |  | ||||||
|     size_t max_length = string_capacity(str_result) - 1; |  | ||||||
| 
 |  | ||||||
|     while(1) { |  | ||||||
|         if(*file_buf_cnt > 0) { |  | ||||||
|             size_t end_index = 0; |  | ||||||
|             char* endline_ptr = (char*)memchr(file_buf, separator, *file_buf_cnt); |  | ||||||
|             newline_index = endline_ptr - file_buf; |  | ||||||
| 
 |  | ||||||
|             if(endline_ptr == 0) { |  | ||||||
|                 end_index = *file_buf_cnt; |  | ||||||
|             } else if(newline_index < *file_buf_cnt) { |  | ||||||
|                 end_index = newline_index + 1; |  | ||||||
|                 found_eol = true; |  | ||||||
|             } else { |  | ||||||
|                 furi_assert(0); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if(max_length && (string_size(str_result) + end_index > max_length)) |  | ||||||
|                 max_length_exceeded = true; |  | ||||||
| 
 |  | ||||||
|             if(!max_length_exceeded) { |  | ||||||
|                 for(size_t i = 0; i < end_index; ++i) { |  | ||||||
|                     string_push_back(str_result, file_buf[i]); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             memmove(file_buf, &file_buf[end_index], *file_buf_cnt - end_index); |  | ||||||
|             *file_buf_cnt = *file_buf_cnt - end_index; |  | ||||||
|             if(found_eol) break; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         *file_buf_cnt += storage_file_read( |  | ||||||
|             file_worker->file, &file_buf[*file_buf_cnt], file_buf_size - *file_buf_cnt); |  | ||||||
|         if(storage_file_get_error(file_worker->file) != FSE_OK) { |  | ||||||
|             file_worker_show_error_internal(file_worker, "Cannot read\nfile"); |  | ||||||
|             string_reset(str_result); |  | ||||||
|             *file_buf_cnt = 0; |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         if(*file_buf_cnt == 0) { |  | ||||||
|             break; // end of reading
 |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if(max_length_exceeded) string_reset(str_result); |  | ||||||
| 
 |  | ||||||
|     return string_size(str_result) || *file_buf_cnt; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_get_value_from_key( |  | ||||||
|     FileWorker* file_worker, |  | ||||||
|     string_t key, |  | ||||||
|     char delimiter, |  | ||||||
|     string_t value) { |  | ||||||
|     bool found = false; |  | ||||||
|     string_t next_line; |  | ||||||
|     string_t next_key; |  | ||||||
|     string_init(next_line); |  | ||||||
|     string_init(next_key); |  | ||||||
|     size_t delim_pos = 0; |  | ||||||
| 
 |  | ||||||
|     while(file_worker_read_until(file_worker, next_line, '\n')) { |  | ||||||
|         delim_pos = string_search_char(next_line, delimiter); |  | ||||||
|         if(delim_pos == STRING_FAILURE) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         string_set_n(next_key, next_line, 0, delim_pos); |  | ||||||
|         if(string_equal_p(next_key, key)) { |  | ||||||
|             string_right(next_line, delim_pos); |  | ||||||
|             string_strim(next_line); |  | ||||||
|             string_set(value, next_line); |  | ||||||
|             found = true; |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     string_clear(next_line); |  | ||||||
|     string_clear(next_key); |  | ||||||
|     return found; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_rename(FileWorker* file_worker, const char* old_path, const char* new_path) { |  | ||||||
|     FS_Error fs_result = storage_common_rename(file_worker->api, old_path, new_path); |  | ||||||
| 
 |  | ||||||
|     if(fs_result != FSE_OK && fs_result != FSE_EXIST) { |  | ||||||
|         file_worker_show_error_internal(file_worker, "Cannot rename\n file/directory"); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return file_worker_check_common_errors(file_worker); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_check_errors(FileWorker* file_worker) { |  | ||||||
|     return file_worker_check_common_errors(file_worker); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool file_worker_is_file_exist(FileWorker* file_worker, const char* filename, bool* exist) { |  | ||||||
|     File* file = storage_file_alloc(file_worker->api); |  | ||||||
| 
 |  | ||||||
|     *exist = storage_file_open(file, filename, FSAM_READ, FSOM_OPEN_EXISTING); |  | ||||||
|     storage_file_close(file); |  | ||||||
|     storage_file_free(file); |  | ||||||
| 
 |  | ||||||
|     bool result = file_worker_check_common_errors(file_worker); |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| @ -1,249 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include <m-string.h> |  | ||||||
| #include <storage/storage.h> |  | ||||||
| 
 |  | ||||||
| #ifdef __cplusplus |  | ||||||
| extern "C" { |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief File operations helper class. |  | ||||||
|  * Automatically opens API records, shows error message on error. |  | ||||||
|  */ |  | ||||||
| typedef struct FileWorker FileWorker; |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Allocate FileWorker |  | ||||||
|  *  |  | ||||||
|  * @param silent do not show errors except from file_worker_show_error fn |  | ||||||
|  * @return FileWorker*  |  | ||||||
|  */ |  | ||||||
| FileWorker* file_worker_alloc(bool silent); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief free FileWorker |  | ||||||
|  *  |  | ||||||
|  * @param file_worker  |  | ||||||
|  */ |  | ||||||
| void file_worker_free(FileWorker* file_worker); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Open file |  | ||||||
|  *  |  | ||||||
|  * @param file_worker FileWorker instance  |  | ||||||
|  * @param filename  |  | ||||||
|  * @param access_mode  |  | ||||||
|  * @param open_mode  |  | ||||||
|  * @return true on success |  | ||||||
|  */ |  | ||||||
| bool file_worker_open( |  | ||||||
|     FileWorker* file_worker, |  | ||||||
|     const char* filename, |  | ||||||
|     FS_AccessMode access_mode, |  | ||||||
|     FS_OpenMode open_mode); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Close file |  | ||||||
|  *  |  | ||||||
|  * @param file_worker FileWorker instance  |  | ||||||
|  * @return true on success |  | ||||||
|  */ |  | ||||||
| bool file_worker_close(FileWorker* file_worker); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Creates a directory. Doesn't show error if directory exist.  |  | ||||||
|  *  |  | ||||||
|  * @param file_worker FileWorker instance  |  | ||||||
|  * @param dirname  |  | ||||||
|  * @return true on success |  | ||||||
|  */ |  | ||||||
| bool file_worker_mkdir(FileWorker* file_worker, const char* dirname); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Removes the file. Doesn't show error if file doesn't exist. |  | ||||||
|  *  |  | ||||||
|  * @param file_worker FileWorker instance  |  | ||||||
|  * @param filename  |  | ||||||
|  * @return true on success   |  | ||||||
|  */ |  | ||||||
| bool file_worker_remove(FileWorker* file_worker, const char* filename); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Get next free filename. |  | ||||||
|  *  |  | ||||||
|  * @param file_worker FileWorker instance  |  | ||||||
|  * @param dirname  |  | ||||||
|  * @param filename  |  | ||||||
|  * @param fileextension  |  | ||||||
|  * @param nextfilename return name |  | ||||||
|  */ |  | ||||||
| void file_worker_get_next_filename( |  | ||||||
|     FileWorker* file_worker, |  | ||||||
|     const char* dirname, |  | ||||||
|     const char* filename, |  | ||||||
|     const char* fileextension, |  | ||||||
|     string_t nextfilename); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Reads data from a file. |  | ||||||
|  *  |  | ||||||
|  * @param file_worker FileWorker instance  |  | ||||||
|  * @param buffer  |  | ||||||
|  * @param bytes_to_read  |  | ||||||
|  * @return true on success   |  | ||||||
|  */ |  | ||||||
| bool file_worker_read(FileWorker* file_worker, void* buffer, uint16_t bytes_to_read); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @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 file_worker FileWorker instance  |  | ||||||
|  * @param result  |  | ||||||
|  * @param separator  |  | ||||||
|  * @return true on success   |  | ||||||
|  */ |  | ||||||
| bool file_worker_read_until(FileWorker* file_worker, string_t result, char separator); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Reads data in hexadecimal space-delimited format. For example "AF FF" in a file - [175, 255] in a read buffer. |  | ||||||
|  *  |  | ||||||
|  * @param file_worker FileWorker instance  |  | ||||||
|  * @param buffer  |  | ||||||
|  * @param bytes_to_read  |  | ||||||
|  * @return true on success   |  | ||||||
|  */ |  | ||||||
| bool file_worker_read_hex(FileWorker* file_worker, uint8_t* buffer, uint16_t bytes_to_read); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Read seek pointer value |  | ||||||
|  *  |  | ||||||
|  * @param file_worker FileWorker instance  |  | ||||||
|  * @param position  |  | ||||||
|  * @return true on success   |  | ||||||
|  */ |  | ||||||
| bool file_worker_tell(FileWorker* file_worker, uint64_t* position); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Set seek pointer value |  | ||||||
|  *  |  | ||||||
|  * @param file_worker FileWorker instance  |  | ||||||
|  * @param position  |  | ||||||
|  * @param from_start  |  | ||||||
|  * @return true on success   |  | ||||||
|  */ |  | ||||||
| bool file_worker_seek(FileWorker* file_worker, uint64_t position, bool from_start); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Write data to file. |  | ||||||
|  *  |  | ||||||
|  * @param file_worker FileWorker instance  |  | ||||||
|  * @param buffer  |  | ||||||
|  * @param bytes_to_write  |  | ||||||
|  * @return true on success   |  | ||||||
|  */ |  | ||||||
| bool file_worker_write(FileWorker* file_worker, const void* buffer, uint16_t bytes_to_write); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Write data to file in hexadecimal space-delimited format. For example [175, 255] in a write buffer - "AF FF" in a file. |  | ||||||
|  *  |  | ||||||
|  * @param file_worker FileWorker instance  |  | ||||||
|  * @param buffer  |  | ||||||
|  * @param bytes_to_write  |  | ||||||
|  * @return true on success   |  | ||||||
|  */ |  | ||||||
| bool file_worker_write_hex(FileWorker* file_worker, const uint8_t* buffer, uint16_t bytes_to_write); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Show system file error message |  | ||||||
|  *  |  | ||||||
|  * @param file_worker FileWorker instance  |  | ||||||
|  * @param error_text  |  | ||||||
|  */ |  | ||||||
| void file_worker_show_error(FileWorker* file_worker, const char* error_text); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Show system file select widget |  | ||||||
|  *  |  | ||||||
|  * @param file_worker FileWorker instance  |  | ||||||
|  * @param path path to directory |  | ||||||
|  * @param extension file extension to be offered for selection |  | ||||||
|  * @param selected_filename buffer where the selected filename will be saved |  | ||||||
|  * @param selected_filename_size and the size of this buffer |  | ||||||
|  * @param preselected_filename filename to be preselected |  | ||||||
|  * @return bool whether a file was selected |  | ||||||
|  */ |  | ||||||
| bool file_worker_file_select( |  | ||||||
|     FileWorker* file_worker, |  | ||||||
|     const char* path, |  | ||||||
|     const char* extension, |  | ||||||
|     char* selected_filename, |  | ||||||
|     uint8_t selected_filename_size, |  | ||||||
|     const char* preselected_filename); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Reads data from a file until separator or EOF is found. |  | ||||||
|  * The separator is included in the result. |  | ||||||
|  * |  | ||||||
|  * @param file_worker FileWorker instance |  | ||||||
|  * @param str_result |  | ||||||
|  * @param file_buf |  | ||||||
|  * @param file_buf_cnt |  | ||||||
|  * @param max_length |  | ||||||
|  * @param separator |  | ||||||
|  * @return true on success |  | ||||||
|  */ |  | ||||||
| bool file_worker_read_until_buffered( |  | ||||||
|     FileWorker* file_worker, |  | ||||||
|     string_t str_result, |  | ||||||
|     char* file_buf, |  | ||||||
|     size_t* file_buf_cnt, |  | ||||||
|     size_t max_length, |  | ||||||
|     char separator); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Gets value from key |  | ||||||
|  * |  | ||||||
|  * @param file_worker FileWorker instance |  | ||||||
|  * @param key key |  | ||||||
|  * @param delimeter key-value delimeter |  | ||||||
|  * @param value value for given key |  | ||||||
|  * @return true on success |  | ||||||
|  */ |  | ||||||
| bool file_worker_get_value_from_key( |  | ||||||
|     FileWorker* file_worker, |  | ||||||
|     string_t key, |  | ||||||
|     char delimiter, |  | ||||||
|     string_t value); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Check whether file exist or not |  | ||||||
|  * |  | ||||||
|  * @param file_worker FileWorker instance |  | ||||||
|  * @param filename |  | ||||||
|  * @param exist - flag to show file exist |  | ||||||
|  * @return true on success |  | ||||||
|  */ |  | ||||||
| bool file_worker_is_file_exist(FileWorker* file_worker, const char* filename, bool* exist); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Rename file or directory |  | ||||||
|  * |  | ||||||
|  * @param file_worker FileWorker instance |  | ||||||
|  * @param old_filename |  | ||||||
|  * @param new_filename |  | ||||||
|  * @return true on success |  | ||||||
|  */ |  | ||||||
| bool file_worker_rename(FileWorker* file_worker, const char* old_path, const char* new_path); |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief Check errors |  | ||||||
|  * |  | ||||||
|  * @param file_worker FileWorker instance |  | ||||||
|  * @return true on success |  | ||||||
|  */ |  | ||||||
| bool file_worker_check_errors(FileWorker* file_worker); |  | ||||||
| 
 |  | ||||||
| #ifdef __cplusplus |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
| @ -1,94 +0,0 @@ | |||||||
| #include "file_worker_cpp.h" |  | ||||||
| #include <lib/toolbox/hex.h> |  | ||||||
| 
 |  | ||||||
| FileWorkerCpp::FileWorkerCpp(bool _silent) { |  | ||||||
|     file_worker = file_worker_alloc(_silent); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| FileWorkerCpp::~FileWorkerCpp() { |  | ||||||
|     file_worker_free(file_worker); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool FileWorkerCpp::open(const char* filename, FS_AccessMode access_mode, FS_OpenMode open_mode) { |  | ||||||
|     return file_worker_open(file_worker, filename, access_mode, open_mode); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool FileWorkerCpp::close() { |  | ||||||
|     return file_worker_close(file_worker); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool FileWorkerCpp::mkdir(const char* dirname) { |  | ||||||
|     return file_worker_mkdir(file_worker, dirname); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool FileWorkerCpp::remove(const char* filename) { |  | ||||||
|     return file_worker_remove(file_worker, filename); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool FileWorkerCpp::read(void* buffer, uint16_t bytes_to_read) { |  | ||||||
|     return file_worker_read(file_worker, buffer, bytes_to_read); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool FileWorkerCpp::read_until(string_t str_result, char separator) { |  | ||||||
|     return file_worker_read_until(file_worker, str_result, separator); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool FileWorkerCpp::read_hex(uint8_t* buffer, uint16_t bytes_to_read) { |  | ||||||
|     return file_worker_read_hex(file_worker, buffer, bytes_to_read); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool FileWorkerCpp::tell(uint64_t* position) { |  | ||||||
|     return file_worker_tell(file_worker, position); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool FileWorkerCpp::seek(uint64_t position, bool from_start) { |  | ||||||
|     return file_worker_seek(file_worker, position, from_start); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool FileWorkerCpp::write(const void* buffer, uint16_t bytes_to_write) { |  | ||||||
|     return file_worker_write(file_worker, buffer, bytes_to_write); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool FileWorkerCpp::write_hex(const uint8_t* buffer, uint16_t bytes_to_write) { |  | ||||||
|     return file_worker_write_hex(file_worker, buffer, bytes_to_write); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void FileWorkerCpp::show_error(const char* error_text) { |  | ||||||
|     file_worker_show_error(file_worker, error_text); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool FileWorkerCpp::file_select( |  | ||||||
|     const char* path, |  | ||||||
|     const char* extension, |  | ||||||
|     char* result, |  | ||||||
|     uint8_t result_size, |  | ||||||
|     const char* selected_filename) { |  | ||||||
|     return file_worker_file_select( |  | ||||||
|         file_worker, path, extension, result, result_size, selected_filename); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool FileWorkerCpp::read_until_buffered( |  | ||||||
|     string_t str_result, |  | ||||||
|     char* file_buf, |  | ||||||
|     size_t* file_buf_cnt, |  | ||||||
|     size_t max_length, |  | ||||||
|     char separator) { |  | ||||||
|     return file_worker_read_until_buffered( |  | ||||||
|         file_worker, str_result, file_buf, file_buf_cnt, max_length, separator); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool FileWorkerCpp::get_value_from_key(string_t key, char delimiter, string_t value) { |  | ||||||
|     return file_worker_get_value_from_key(file_worker, key, delimiter, value); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool FileWorkerCpp::is_file_exist(const char* filename, bool* exist) { |  | ||||||
|     return file_worker_is_file_exist(file_worker, filename, exist); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool FileWorkerCpp::rename(const char* old_path, const char* new_path) { |  | ||||||
|     return file_worker_rename(file_worker, old_path, new_path); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool FileWorkerCpp::check_errors() { |  | ||||||
|     return file_worker_check_errors(file_worker); |  | ||||||
| } |  | ||||||
| @ -1,190 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include "file_worker.h" |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * @brief File operations helper class. |  | ||||||
|  * Automatically opens API records, shows error message on error. |  | ||||||
|  */ |  | ||||||
| class FileWorkerCpp { |  | ||||||
| public: |  | ||||||
|     FileWorkerCpp(bool silent = false); |  | ||||||
|     ~FileWorkerCpp(); |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * @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, |  | ||||||
|         const char* selected_filename); |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * @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 included in the result. |  | ||||||
|      * |  | ||||||
|      * @param result |  | ||||||
|      * @param file_buf |  | ||||||
|      * @param file_buf_cnt |  | ||||||
|      * @param max_length |  | ||||||
|      * @param separator |  | ||||||
|      * @return true on success |  | ||||||
|      */ |  | ||||||
|     bool read_until_buffered( |  | ||||||
|         string_t str_result, |  | ||||||
|         char* file_buf, |  | ||||||
|         size_t* file_buf_cnt, |  | ||||||
|         size_t max_length, |  | ||||||
|         char separator = '\n'); |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * @brief Gets value from key |  | ||||||
|      * |  | ||||||
|      * @param file_worker FileWorker instance |  | ||||||
|      * @param key key |  | ||||||
|      * @param delimeter key-value delimeter |  | ||||||
|      * @param value value for given key |  | ||||||
|      * @return true on success |  | ||||||
|      */ |  | ||||||
|     bool get_value_from_key(string_t key, char delimiter, string_t value); |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * @brief Check whether file exist or not |  | ||||||
|      * |  | ||||||
|      * @param file_worker FileWorker instance |  | ||||||
|      * @param filename |  | ||||||
|      * @param exist - flag to show file exist |  | ||||||
|      * @return true on success |  | ||||||
|      */ |  | ||||||
|     bool is_file_exist(const char* filename, bool* exist); |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * @brief Rename file or directory |  | ||||||
|      * |  | ||||||
|      * @param old_filename |  | ||||||
|      * @param new_filename |  | ||||||
|      * @return true on success |  | ||||||
|      */ |  | ||||||
|     bool rename(const char* old_path, const char* new_path); |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * @brief Check errors |  | ||||||
|      * |  | ||||||
|      * @return true if no errors |  | ||||||
|      */ |  | ||||||
|     bool check_errors(); |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     FileWorker* file_worker; |  | ||||||
| }; |  | ||||||
| @ -1,102 +0,0 @@ | |||||||
| #include "app_template.h" |  | ||||||
| 
 |  | ||||||
| /*
 |  | ||||||
| To use this example you need to rename |  | ||||||
| AppExampleState - class to hold app state |  | ||||||
| AppExampleEvent - class to hold app event |  | ||||||
| AppExample      - app class |  | ||||||
| app_cpp_example - function that launch app |  | ||||||
| */ |  | ||||||
| 
 |  | ||||||
| // event enumeration type
 |  | ||||||
| typedef uint8_t event_t; |  | ||||||
| 
 |  | ||||||
| // app state class
 |  | ||||||
| class AppExampleState { |  | ||||||
| public: |  | ||||||
|     // state data
 |  | ||||||
|     uint8_t example_data; |  | ||||||
| 
 |  | ||||||
|     // state initializer
 |  | ||||||
|     AppExampleState() { |  | ||||||
|         example_data = 0; |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // app events class
 |  | ||||||
| class AppExampleEvent { |  | ||||||
| public: |  | ||||||
|     // events enum
 |  | ||||||
|     static const event_t EventTypeTick = 0; |  | ||||||
|     static const event_t EventTypeKey = 1; |  | ||||||
| 
 |  | ||||||
|     // payload
 |  | ||||||
|     union { |  | ||||||
|         InputEvent input; |  | ||||||
|     } value; |  | ||||||
| 
 |  | ||||||
|     // event type
 |  | ||||||
|     event_t type; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // our app derived from base AppTemplate class
 |  | ||||||
| // with template variables <state, events>
 |  | ||||||
| class AppExample : public AppTemplate<AppExampleState, AppExampleEvent> { |  | ||||||
| public: |  | ||||||
|     uint8_t run(); |  | ||||||
|     void render(Canvas* canvas); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // start app
 |  | ||||||
| uint8_t AppExample::run() { |  | ||||||
|     // here we dont need to acquire or release state
 |  | ||||||
|     // because before we call app_ready our application is "single threaded"
 |  | ||||||
|     state.example_data = 12; |  | ||||||
| 
 |  | ||||||
|     // signal that we ready to render and ipc
 |  | ||||||
|     app_ready(); |  | ||||||
| 
 |  | ||||||
|     // from here, any data that pass in render function must be guarded
 |  | ||||||
|     // by calling acquire_state and release_state
 |  | ||||||
| 
 |  | ||||||
|     AppExampleEvent event; |  | ||||||
|     while(1) { |  | ||||||
|         if(get_event(&event, 1000)) { |  | ||||||
|             if(event.type == AppExampleEvent::EventTypeKey) { |  | ||||||
|                 // press events
 |  | ||||||
|                 if(event.value.input.type == InputTypeShort && |  | ||||||
|                    event.value.input.key == InputKeyBack) { |  | ||||||
|                     printf("bye!\n"); |  | ||||||
|                     return exit(); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 if(event.value.input.type == InputTypeShort && |  | ||||||
|                    event.value.input.key == InputKeyUp) { |  | ||||||
|                     // to read or write state you need to execute
 |  | ||||||
|                     // acquire modify release state
 |  | ||||||
|                     acquire_state(); |  | ||||||
|                     state.example_data = 24; |  | ||||||
|                     release_state(); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // signal to force gui update
 |  | ||||||
|         update_gui(); |  | ||||||
|     }; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // render app
 |  | ||||||
| void AppExample::render(Canvas* canvas) { |  | ||||||
|     // here you dont need to call acquire_state or release_state
 |  | ||||||
|     // to read or write app state, that already handled by caller
 |  | ||||||
|     canvas_set_color(canvas, ColorBlack); |  | ||||||
|     canvas_set_font(canvas, FontPrimary); |  | ||||||
|     canvas_draw_str(canvas, 2, state.example_data, "Example app"); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // app enter function
 |  | ||||||
| extern "C" uint8_t app_cpp_example(void* p) { |  | ||||||
|     AppExample* app = new AppExample(); |  | ||||||
|     return app->run(); |  | ||||||
| } |  | ||||||
| @ -1,113 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include "callback-connector.h" |  | ||||||
| #include <furi.h> |  | ||||||
| #include <furi_hal.h> |  | ||||||
| #include <gui/gui.h> |  | ||||||
| #include <input/input.h> |  | ||||||
| 
 |  | ||||||
| // simple app class with template variables <state, events>
 |  | ||||||
| template <class TState, class TEvent> class AppTemplate { |  | ||||||
| public: |  | ||||||
|     ViewPort* view_port; |  | ||||||
|     osMessageQueueId_t event_queue; |  | ||||||
|     TState state; |  | ||||||
|     ValueMutex state_mutex; |  | ||||||
|     Gui* gui; |  | ||||||
| 
 |  | ||||||
|     AppTemplate(); |  | ||||||
|     ~AppTemplate(); |  | ||||||
|     void input_callback(InputEvent* input_event, void* ctx); |  | ||||||
|     void draw_callback(Canvas* canvas, void* ctx); |  | ||||||
|     virtual void render(Canvas* canvas) = 0; |  | ||||||
|     void acquire_state(void); |  | ||||||
|     void release_state(void); |  | ||||||
|     bool get_event(TEvent* event, uint32_t timeout); |  | ||||||
|     void app_ready(void); |  | ||||||
|     uint8_t exit(void); |  | ||||||
|     void update_gui(void); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| template <class TState, class TEvent> AppTemplate<TState, TEvent>::AppTemplate() { |  | ||||||
|     // allocate events queue
 |  | ||||||
|     event_queue = osMessageQueueNew(10, sizeof(TEvent), NULL); |  | ||||||
| 
 |  | ||||||
|     // allocate valuemutex
 |  | ||||||
|     // TODO: use plain os mutex?
 |  | ||||||
|     if(!init_mutex(&state_mutex, &state, sizeof(TState))) { |  | ||||||
|         printf("cannot create mutex\n"); |  | ||||||
|         furi_crash(NULL); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // open gui
 |  | ||||||
|     gui = (Gui*)furi_record_open("gui"); |  | ||||||
| 
 |  | ||||||
|     // allocate view_port
 |  | ||||||
|     view_port = view_port_alloc(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| template <class TState, class TEvent> AppTemplate<TState, TEvent>::~AppTemplate() { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // generic input callback
 |  | ||||||
| template <class TState, class TEvent> |  | ||||||
| void AppTemplate<TState, TEvent>::input_callback(InputEvent* input_event, void* ctx) { |  | ||||||
|     AppTemplate* app = static_cast<AppTemplate*>(ctx); |  | ||||||
| 
 |  | ||||||
|     TEvent event; |  | ||||||
|     event.type = TEvent::EventTypeKey; |  | ||||||
|     event.value.input = *input_event; |  | ||||||
|     osMessageQueuePut(app->event_queue, &event, 0, 0); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // generic draw callback
 |  | ||||||
| template <class TState, class TEvent> |  | ||||||
| void AppTemplate<TState, TEvent>::draw_callback(Canvas* canvas, void* ctx) { |  | ||||||
|     AppTemplate* app = static_cast<AppTemplate*>(ctx); |  | ||||||
|     app->acquire_state(); |  | ||||||
| 
 |  | ||||||
|     canvas_clear(canvas); |  | ||||||
|     app->render(canvas); |  | ||||||
| 
 |  | ||||||
|     app->release_state(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| template <class TState, class TEvent> void AppTemplate<TState, TEvent>::acquire_state(void) { |  | ||||||
|     acquire_mutex(&state_mutex, osWaitForever); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| template <class TState, class TEvent> void AppTemplate<TState, TEvent>::release_state(void) { |  | ||||||
|     release_mutex(&state_mutex, &state); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| template <class TState, class TEvent> |  | ||||||
| bool AppTemplate<TState, TEvent>::get_event(TEvent* event, uint32_t timeout) { |  | ||||||
|     osStatus_t event_status = osMessageQueueGet(event_queue, event, NULL, timeout); |  | ||||||
| 
 |  | ||||||
|     return (event_status == osOK); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // signal that app is ready, and we can render something
 |  | ||||||
| // also unblock dependent tasks
 |  | ||||||
| template <class TState, class TEvent> void AppTemplate<TState, TEvent>::app_ready(void) { |  | ||||||
|     // connect view_port with input callback
 |  | ||||||
|     auto input_cb_ref = cbc::obtain_connector(this, &AppTemplate::input_callback); |  | ||||||
|     view_port_input_callback_set(view_port, input_cb_ref, this); |  | ||||||
| 
 |  | ||||||
|     // connect view_port with draw callback
 |  | ||||||
|     auto draw_cb_ref = cbc::obtain_connector(this, &AppTemplate::draw_callback); |  | ||||||
|     view_port_draw_callback_set(view_port, draw_cb_ref, this); |  | ||||||
| 
 |  | ||||||
|     // add view_port
 |  | ||||||
|     gui_add_view_port(gui, view_port, GuiLayerFullscreen); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| template <class TState, class TEvent> uint8_t AppTemplate<TState, TEvent>::exit(void) { |  | ||||||
|     // TODO remove all view_ports create by app
 |  | ||||||
|     view_port_enabled_set(view_port, false); |  | ||||||
|     return 255; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| template <class TState, class TEvent> void AppTemplate<TState, TEvent>::update_gui(void) { |  | ||||||
|     view_port_update(view_port); |  | ||||||
| } |  | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 あく
						あく