[FL-1946] RPC App launch (#758)
* RPC: Add App start, lock status - Add RPC commands Application start, lock status acquiring. - Write tests for RPC App system. - Replace Unit Tests application with CLI command. This is for CI needs and for tests that run application. * Fix NDEBUG build * Update PB submodule * Fix RPC print (ENABLE DEBUG PRINT!) * snprintf -> string_t * Fix Hard Fault (early mutex free) * printf -> string_t, format, enable tests * Update submodule: protobuf * Applications: rollback unit test naming scheme. Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									a643bd14be
								
							
						
					
					
						commit
						1db29eaea8
					
				| @ -20,7 +20,7 @@ extern int32_t desktop_srv(void* p); | |||||||
| extern int32_t accessor_app(void* p); | extern int32_t accessor_app(void* p); | ||||||
| extern int32_t archive_app(void* p); | extern int32_t archive_app(void* p); | ||||||
| extern int32_t blink_test_app(void* p); | extern int32_t blink_test_app(void* p); | ||||||
| extern int32_t flipper_test_app(void* p); | extern int32_t delay_test_app(void* p); | ||||||
| extern int32_t gpio_app(void* p); | extern int32_t gpio_app(void* p); | ||||||
| extern int32_t ibutton_app(void* p); | extern int32_t ibutton_app(void* p); | ||||||
| extern int32_t irda_app(void* p); | extern int32_t irda_app(void* p); | ||||||
| @ -49,6 +49,7 @@ extern void nfc_cli_init(); | |||||||
| extern void storage_cli_init(); | extern void storage_cli_init(); | ||||||
| extern void subghz_cli_init(); | extern void subghz_cli_init(); | ||||||
| extern void power_cli_init(); | extern void power_cli_init(); | ||||||
|  | extern void unit_tests_cli_init(); | ||||||
| 
 | 
 | ||||||
| // Settings
 | // Settings
 | ||||||
| extern int32_t notification_settings_app(void* p); | extern int32_t notification_settings_app(void* p); | ||||||
| @ -183,6 +184,10 @@ const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[] = { | |||||||
| #ifdef SRV_STORAGE | #ifdef SRV_STORAGE | ||||||
|     storage_cli_init, |     storage_cli_init, | ||||||
| #endif | #endif | ||||||
|  | 
 | ||||||
|  | #ifdef APP_UNIT_TESTS | ||||||
|  |     unit_tests_cli_init, | ||||||
|  | #endif | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const size_t FLIPPER_ON_SYSTEM_START_COUNT = | const size_t FLIPPER_ON_SYSTEM_START_COUNT = | ||||||
| @ -220,10 +225,6 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = { | |||||||
|     {.app = usb_test_app, .name = "USB Test", .stack_size = 1024, .icon = &A_Plugins_14}, |     {.app = usb_test_app, .name = "USB Test", .stack_size = 1024, .icon = &A_Plugins_14}, | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef APP_UNIT_TESTS |  | ||||||
|     {.app = flipper_test_app, .name = "Unit Tests", .stack_size = 1024 * 8, .icon = &A_Plugins_14}, |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #ifdef APP_IRDA_MONITOR | #ifdef APP_IRDA_MONITOR | ||||||
|     {.app = irda_monitor_app, .name = "Irda Monitor", .stack_size = 1024, .icon = &A_Plugins_14}, |     {.app = irda_monitor_app, .name = "Irda Monitor", .stack_size = 1024, .icon = &A_Plugins_14}, | ||||||
| #endif | #endif | ||||||
| @ -239,6 +240,10 @@ const FlipperApplication FLIPPER_DEBUG_APPS[] = { | |||||||
| #ifdef SRV_BT | #ifdef SRV_BT | ||||||
|     {.app = bt_debug_app, .name = "Bluetooth Debug", .stack_size = 1024, .icon = NULL}, |     {.app = bt_debug_app, .name = "Bluetooth Debug", .stack_size = 1024, .icon = NULL}, | ||||||
| #endif | #endif | ||||||
|  | 
 | ||||||
|  | #ifdef APP_UNIT_TESTS | ||||||
|  |     {.app = delay_test_app, .name = "Delay Test App", .stack_size = 1024, .icon = &A_Plugins_14}, | ||||||
|  | #endif | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const size_t FLIPPER_DEBUG_APPS_COUNT = sizeof(FLIPPER_DEBUG_APPS) / sizeof(FlipperApplication); | const size_t FLIPPER_DEBUG_APPS_COUNT = sizeof(FLIPPER_DEBUG_APPS) / sizeof(FlipperApplication); | ||||||
|  | |||||||
| @ -42,7 +42,6 @@ APP_BLINK	= 1 | |||||||
| APP_IRDA_MONITOR = 1 | APP_IRDA_MONITOR = 1 | ||||||
| APP_KEYPAD_TEST = 1 | APP_KEYPAD_TEST = 1 | ||||||
| APP_SD_TEST	= 1 | APP_SD_TEST	= 1 | ||||||
| APP_UNIT_TESTS = 0 |  | ||||||
| APP_VIBRO_DEMO = 1 | APP_VIBRO_DEMO = 1 | ||||||
| APP_USB_TEST = 1 | APP_USB_TEST = 1 | ||||||
| endif | endif | ||||||
|  | |||||||
| @ -1,3 +1,4 @@ | |||||||
|  | #include "loader/loader.h" | ||||||
| #include "loader_i.h" | #include "loader_i.h" | ||||||
| 
 | 
 | ||||||
| #define LOADER_THREAD_FLAG_SHOW_MENU (1 << 0) | #define LOADER_THREAD_FLAG_SHOW_MENU (1 << 0) | ||||||
| @ -56,7 +57,7 @@ static void loader_cli_callback(Cli* cli, string_t args, void* _ctx) { | |||||||
|     furi_thread_start(loader_instance->thread); |     furi_thread_start(loader_instance->thread); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool loader_start(Loader* instance, const char* name, const char* args) { | LoaderStatus loader_start(Loader* instance, const char* name, const char* args) { | ||||||
|     furi_assert(name); |     furi_assert(name); | ||||||
| 
 | 
 | ||||||
|     const FlipperApplication* flipper_app = NULL; |     const FlipperApplication* flipper_app = NULL; | ||||||
| @ -79,14 +80,15 @@ bool loader_start(Loader* instance, const char* name, const char* args) { | |||||||
| 
 | 
 | ||||||
|     if(!flipper_app) { |     if(!flipper_app) { | ||||||
|         FURI_LOG_E(LOADER_LOG_TAG, "Can't find application with name %s", name); |         FURI_LOG_E(LOADER_LOG_TAG, "Can't find application with name %s", name); | ||||||
|         return false; |         return LoaderStatusErrorUnknownApp; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     loader_lock(instance); |     bool locked = loader_lock(instance); | ||||||
| 
 | 
 | ||||||
|     if(furi_thread_get_state(instance->thread) != FuriThreadStateStopped) { |     if(!locked || (furi_thread_get_state(instance->thread) != FuriThreadStateStopped)) { | ||||||
|         FURI_LOG_E(LOADER_LOG_TAG, "Can't start app. %s is running", instance->current_app->name); |         FURI_LOG_E(LOADER_LOG_TAG, "Can't start app. %s is running", instance->current_app->name); | ||||||
|         return false; |         /* no need to call loader_unlock() - it is called as soon as application stops */ | ||||||
|  |         return LoaderStatusErrorAppStarted; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     instance->current_app = flipper_app; |     instance->current_app = flipper_app; | ||||||
| @ -106,7 +108,8 @@ bool loader_start(Loader* instance, const char* name, const char* args) { | |||||||
|     furi_thread_set_context(instance->thread, thread_args); |     furi_thread_set_context(instance->thread, thread_args); | ||||||
|     furi_thread_set_callback(instance->thread, flipper_app->app); |     furi_thread_set_callback(instance->thread, flipper_app->app); | ||||||
| 
 | 
 | ||||||
|     return furi_thread_start(instance->thread); |     bool thread_started = furi_thread_start(instance->thread); | ||||||
|  |     return thread_started ? LoaderStatusOk : LoaderStatusErrorInternal; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool loader_lock(Loader* instance) { | bool loader_lock(Loader* instance) { | ||||||
| @ -127,6 +130,10 @@ void loader_unlock(Loader* instance) { | |||||||
|     furi_check(osMutexRelease(instance->mutex) == osOK); |     furi_check(osMutexRelease(instance->mutex) == osOK); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool loader_is_locked(Loader* instance) { | ||||||
|  |     return (instance->lock_semaphore > 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void loader_thread_state_callback(FuriThreadState thread_state, void* context) { | static void loader_thread_state_callback(FuriThreadState thread_state, void* context) { | ||||||
|     furi_assert(context); |     furi_assert(context); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4,12 +4,19 @@ | |||||||
| 
 | 
 | ||||||
| typedef struct Loader Loader; | typedef struct Loader Loader; | ||||||
| 
 | 
 | ||||||
|  | typedef enum { | ||||||
|  |     LoaderStatusOk, | ||||||
|  |     LoaderStatusErrorAppStarted, | ||||||
|  |     LoaderStatusErrorUnknownApp, | ||||||
|  |     LoaderStatusErrorInternal, | ||||||
|  | } LoaderStatus; | ||||||
|  | 
 | ||||||
| /** Start application
 | /** Start application
 | ||||||
|  * @param name - application name |  * @param name - application name | ||||||
|  * @param args - application arguments |  * @param args - application arguments | ||||||
|  * @retval true on success |  * @retval true on success | ||||||
|  */ |  */ | ||||||
| bool loader_start(Loader* instance, const char* name, const char* args); | LoaderStatus loader_start(Loader* instance, const char* name, const char* args); | ||||||
| 
 | 
 | ||||||
| /** Lock application start
 | /** Lock application start
 | ||||||
|  * @retval true on success |  * @retval true on success | ||||||
| @ -19,5 +26,8 @@ bool loader_lock(Loader* instance); | |||||||
| /** Unlock application start */ | /** Unlock application start */ | ||||||
| void loader_unlock(Loader* instance); | void loader_unlock(Loader* instance); | ||||||
| 
 | 
 | ||||||
|  | /** Get loader lock status */ | ||||||
|  | bool loader_is_locked(Loader* instance); | ||||||
|  | 
 | ||||||
| /** Show primary loader */ | /** Show primary loader */ | ||||||
| void loader_show_menu(); | void loader_show_menu(); | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ | |||||||
| #include "furi-hal-delay.h" | #include "furi-hal-delay.h" | ||||||
| #include "furi/check.h" | #include "furi/check.h" | ||||||
| #include "furi/log.h" | #include "furi/log.h" | ||||||
|  | #include <m-string.h> | ||||||
| #include "pb.h" | #include "pb.h" | ||||||
| #include "pb_decode.h" | #include "pb_decode.h" | ||||||
| #include "pb_encode.h" | #include "pb_encode.h" | ||||||
| @ -42,6 +43,10 @@ static RpcSystemCallbacks rpc_systems[] = { | |||||||
|         .alloc = rpc_system_storage_alloc, |         .alloc = rpc_system_storage_alloc, | ||||||
|         .free = rpc_system_storage_free, |         .free = rpc_system_storage_free, | ||||||
|     }, |     }, | ||||||
|  |     { | ||||||
|  |         .alloc = rpc_system_app_alloc, | ||||||
|  |         .free = NULL, | ||||||
|  |     }, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct RpcSession { | struct RpcSession { | ||||||
| @ -65,31 +70,28 @@ struct Rpc { | |||||||
| 
 | 
 | ||||||
| static bool content_callback(pb_istream_t* stream, const pb_field_t* field, void** arg); | static bool content_callback(pb_istream_t* stream, const pb_field_t* field, void** arg); | ||||||
| 
 | 
 | ||||||
| static size_t rpc_sprint_msg_file( | static size_t rpc_sprintf_msg_file( | ||||||
|     char* str, |     string_t str, | ||||||
|     size_t str_size, |  | ||||||
|     const char* prefix, |     const char* prefix, | ||||||
|     const PB_Storage_File* msg_file, |     const PB_Storage_File* msg_file, | ||||||
|     size_t msg_files_size) { |     size_t msg_files_size) { | ||||||
|     size_t cnt = 0; |     size_t cnt = 0; | ||||||
| 
 | 
 | ||||||
|     for(int i = 0; i < msg_files_size; ++i, ++msg_file) { |     for(int i = 0; i < msg_files_size; ++i, ++msg_file) { | ||||||
|         cnt += snprintf( |         string_cat_printf( | ||||||
|             str + cnt, |             str, | ||||||
|             str_size - cnt, |  | ||||||
|             "%s[%c] size: %5ld", |             "%s[%c] size: %5ld", | ||||||
|             prefix, |             prefix, | ||||||
|             msg_file->type == PB_Storage_File_FileType_DIR ? 'd' : 'f', |             msg_file->type == PB_Storage_File_FileType_DIR ? 'd' : 'f', | ||||||
|             msg_file->size); |             msg_file->size); | ||||||
| 
 | 
 | ||||||
|         if(msg_file->name) { |         if(msg_file->name) { | ||||||
|             cnt += snprintf(str + cnt, str_size - cnt, " \'%s\'", msg_file->name); |             string_cat_printf(str, " \'%s\'", msg_file->name); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(msg_file->data && msg_file->data->size) { |         if(msg_file->data && msg_file->data->size) { | ||||||
|             cnt += snprintf( |             string_cat_printf( | ||||||
|                 str + cnt, |                 str, | ||||||
|                 str_size - cnt, |  | ||||||
|                 " (%d):\'%.*s%s\'", |                 " (%d):\'%.*s%s\'", | ||||||
|                 msg_file->data->size, |                 msg_file->data->size, | ||||||
|                 MIN(msg_file->data->size, 30), |                 MIN(msg_file->data->size, 30), | ||||||
| @ -97,23 +99,18 @@ static size_t rpc_sprint_msg_file( | |||||||
|                 msg_file->data->size > 30 ? "..." : ""); |                 msg_file->data->size > 30 ? "..." : ""); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         cnt += snprintf(str + cnt, str_size - cnt, "\r\n"); |         string_cat_printf(str, "\r\n"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return cnt; |     return cnt; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #define ADD_STR(s, c, ...) snprintf(s + c, sizeof(s) - c, ##__VA_ARGS__); |  | ||||||
| 
 |  | ||||||
| #define ADD_STR_ELEMENT(s, c, ...) rpc_sprint_msg_file(s + c, sizeof(s) - c, ##__VA_ARGS__); |  | ||||||
| 
 |  | ||||||
| void rpc_print_message(const PB_Main* message) { | void rpc_print_message(const PB_Main* message) { | ||||||
|     char str[500]; |     string_t str; | ||||||
|     size_t cnt = 0; |     string_init(str); | ||||||
| 
 | 
 | ||||||
|     cnt += snprintf( |     string_cat_printf( | ||||||
|         str + cnt, |         str, | ||||||
|         sizeof(str) - cnt, |  | ||||||
|         "PB_Main: {\r\n\tresult: %d cmd_id: %ld (%s)\r\n", |         "PB_Main: {\r\n\tresult: %d cmd_id: %ld (%s)\r\n", | ||||||
|         message->command_status, |         message->command_status, | ||||||
|         message->command_id, |         message->command_id, | ||||||
| @ -121,88 +118,112 @@ void rpc_print_message(const PB_Main* message) { | |||||||
|     switch(message->which_content) { |     switch(message->which_content) { | ||||||
|     default: |     default: | ||||||
|         /* not implemented yet */ |         /* not implemented yet */ | ||||||
|         cnt += ADD_STR(str, cnt, "\tNOT_IMPLEMENTED (%d) {\r\n", message->which_content); |         string_cat_printf(str, "\tNOT_IMPLEMENTED (%d) {\r\n", message->which_content); | ||||||
|         break; |         break; | ||||||
|  |     case PB_Main_app_start_tag: { | ||||||
|  |         string_cat_printf(str, "\tapp_start {\r\n"); | ||||||
|  |         const char* name = message->content.app_start.name; | ||||||
|  |         const char* args = message->content.app_start.args; | ||||||
|  |         if(name) { | ||||||
|  |             string_cat_printf(str, "\t\tname: %s\r\n", name); | ||||||
|  |         } | ||||||
|  |         if(args) { | ||||||
|  |             string_cat_printf(str, "\t\targs: %s\r\n", args); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     case PB_Main_app_lock_status_request_tag: { | ||||||
|  |         string_cat_printf(str, "\tapp_lock_status_request {\r\n"); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     case PB_Main_app_lock_status_response_tag: { | ||||||
|  |         string_cat_printf(str, "\tapp_lock_status_response {\r\n"); | ||||||
|  |         bool lock_status = message->content.app_lock_status_response.locked; | ||||||
|  |         string_cat_printf(str, "\t\tlocked: %s\r\n", lock_status ? "true" : "false"); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|     case PB_Main_storage_md5sum_request_tag: { |     case PB_Main_storage_md5sum_request_tag: { | ||||||
|         cnt += ADD_STR(str, cnt, "\tmd5sum_request {\r\n"); |         string_cat_printf(str, "\tmd5sum_request {\r\n"); | ||||||
|         const char* path = message->content.storage_md5sum_request.path; |         const char* path = message->content.storage_md5sum_request.path; | ||||||
|         if(path) { |         if(path) { | ||||||
|             cnt += ADD_STR(str, cnt, "\t\tpath: %s\r\n", path); |             string_cat_printf(str, "\t\tpath: %s\r\n", path); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     case PB_Main_storage_md5sum_response_tag: { |     case PB_Main_storage_md5sum_response_tag: { | ||||||
|         cnt += ADD_STR(str, cnt, "\tmd5sum_response {\r\n"); |         string_cat_printf(str, "\tmd5sum_response {\r\n"); | ||||||
|         const char* path = message->content.storage_md5sum_response.md5sum; |         const char* path = message->content.storage_md5sum_response.md5sum; | ||||||
|         if(path) { |         if(path) { | ||||||
|             cnt += ADD_STR(str, cnt, "\t\tmd5sum: %s\r\n", path); |             string_cat_printf(str, "\t\tmd5sum: %s\r\n", path); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     case PB_Main_ping_request_tag: |     case PB_Main_ping_request_tag: | ||||||
|         cnt += ADD_STR(str, cnt, "\tping_request {\r\n"); |         string_cat_printf(str, "\tping_request {\r\n"); | ||||||
|         break; |         break; | ||||||
|     case PB_Main_ping_response_tag: |     case PB_Main_ping_response_tag: | ||||||
|         cnt += ADD_STR(str, cnt, "\tping_response {\r\n"); |         string_cat_printf(str, "\tping_response {\r\n"); | ||||||
|         break; |         break; | ||||||
|     case PB_Main_storage_mkdir_request_tag: |     case PB_Main_storage_mkdir_request_tag: | ||||||
|         cnt += ADD_STR(str, cnt, "\tmkdir {\r\n"); |         string_cat_printf(str, "\tmkdir {\r\n"); | ||||||
|         break; |         break; | ||||||
|     case PB_Main_storage_delete_request_tag: { |     case PB_Main_storage_delete_request_tag: { | ||||||
|         cnt += ADD_STR(str, cnt, "\tdelete {\r\n"); |         string_cat_printf(str, "\tdelete {\r\n"); | ||||||
|         const char* path = message->content.storage_delete_request.path; |         const char* path = message->content.storage_delete_request.path; | ||||||
|         if(path) { |         if(path) { | ||||||
|             cnt += ADD_STR(str, cnt, "\t\tpath: %s\r\n", path); |             string_cat_printf(str, "\t\tpath: %s\r\n", path); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     case PB_Main_empty_tag: |     case PB_Main_empty_tag: | ||||||
|         cnt += ADD_STR(str, cnt, "\tempty {\r\n"); |         string_cat_printf(str, "\tempty {\r\n"); | ||||||
|         break; |         break; | ||||||
|     case PB_Main_storage_list_request_tag: { |     case PB_Main_storage_list_request_tag: { | ||||||
|         cnt += ADD_STR(str, cnt, "\tlist_request {\r\n"); |         string_cat_printf(str, "\tlist_request {\r\n"); | ||||||
|         const char* path = message->content.storage_list_request.path; |         const char* path = message->content.storage_list_request.path; | ||||||
|         if(path) { |         if(path) { | ||||||
|             cnt += ADD_STR(str, cnt, "\t\tpath: %s\r\n", path); |             string_cat_printf(str, "\t\tpath: %s\r\n", path); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     case PB_Main_storage_read_request_tag: { |     case PB_Main_storage_read_request_tag: { | ||||||
|         cnt += ADD_STR(str, cnt, "\tread_request {\r\n"); |         string_cat_printf(str, "\tread_request {\r\n"); | ||||||
|         const char* path = message->content.storage_read_request.path; |         const char* path = message->content.storage_read_request.path; | ||||||
|         if(path) { |         if(path) { | ||||||
|             cnt += ADD_STR(str, cnt, "\t\tpath: %s\r\n", path); |             string_cat_printf(str, "\t\tpath: %s\r\n", path); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     case PB_Main_storage_write_request_tag: { |     case PB_Main_storage_write_request_tag: { | ||||||
|         cnt += ADD_STR(str, cnt, "\twrite_request {\r\n"); |         string_cat_printf(str, "\twrite_request {\r\n"); | ||||||
|         const char* path = message->content.storage_write_request.path; |         const char* path = message->content.storage_write_request.path; | ||||||
|         if(path) { |         if(path) { | ||||||
|             cnt += ADD_STR(str, cnt, "\t\tpath: %s\r\n", path); |             string_cat_printf(str, "\t\tpath: %s\r\n", path); | ||||||
|         } |         } | ||||||
|         if(message->content.storage_write_request.has_file) { |         if(message->content.storage_write_request.has_file) { | ||||||
|             const PB_Storage_File* msg_file = &message->content.storage_write_request.file; |             const PB_Storage_File* msg_file = &message->content.storage_write_request.file; | ||||||
|             cnt += ADD_STR_ELEMENT(str, cnt, "\t\t\t", msg_file, 1); |             rpc_sprintf_msg_file(str, "\t\t\t", msg_file, 1); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     case PB_Main_storage_read_response_tag: |     case PB_Main_storage_read_response_tag: | ||||||
|         cnt += ADD_STR(str, cnt, "\tread_response {\r\n"); |         string_cat_printf(str, "\tread_response {\r\n"); | ||||||
|         if(message->content.storage_read_response.has_file) { |         if(message->content.storage_read_response.has_file) { | ||||||
|             const PB_Storage_File* msg_file = &message->content.storage_read_response.file; |             const PB_Storage_File* msg_file = &message->content.storage_read_response.file; | ||||||
|             cnt += ADD_STR_ELEMENT(str, cnt, "\t\t\t", msg_file, 1); |             rpc_sprintf_msg_file(str, "\t\t\t", msg_file, 1); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|     case PB_Main_storage_list_response_tag: { |     case PB_Main_storage_list_response_tag: { | ||||||
|         const PB_Storage_File* msg_file = message->content.storage_list_response.file; |         const PB_Storage_File* msg_file = message->content.storage_list_response.file; | ||||||
|         size_t msg_file_count = message->content.storage_list_response.file_count; |         size_t msg_file_count = message->content.storage_list_response.file_count; | ||||||
|         cnt += ADD_STR(str, cnt, "\tlist_response {\r\n"); |         string_cat_printf(str, "\tlist_response {\r\n"); | ||||||
|         cnt += ADD_STR_ELEMENT(str, cnt, "\t\t", msg_file, msg_file_count); |         rpc_sprintf_msg_file(str, "\t\t", msg_file, msg_file_count); | ||||||
|     } |     } | ||||||
|     } |     } | ||||||
|     cnt += ADD_STR(str, cnt, "\t}\r\n}\r\n"); |     string_cat_printf(str, "\t}\r\n}\r\n"); | ||||||
|     printf("%s", str); |     printf("%s", string_get_cstr(str)); | ||||||
|  | 
 | ||||||
|  |     string_clear(str); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static Rpc* rpc_alloc(void) { | static Rpc* rpc_alloc(void) { | ||||||
| @ -253,7 +274,6 @@ void rpc_close_session(RpcSession* session) { | |||||||
|     furi_assert(session->rpc); |     furi_assert(session->rpc); | ||||||
|     furi_assert(session->rpc->busy); |     furi_assert(session->rpc->busy); | ||||||
| 
 | 
 | ||||||
|     osMutexDelete(session->send_bytes_mutex); |  | ||||||
|     rpc_set_send_bytes_callback(session, NULL, NULL); |     rpc_set_send_bytes_callback(session, NULL, NULL); | ||||||
|     osEventFlagsSet(session->rpc->events, RPC_EVENT_DISCONNECT); |     osEventFlagsSet(session->rpc->events, RPC_EVENT_DISCONNECT); | ||||||
| } | } | ||||||
| @ -328,22 +348,31 @@ void rpc_encode_and_send(Rpc* rpc, PB_Main* main_message) { | |||||||
|     pb_encode_ex(&ostream, &PB_Main_msg, main_message, PB_ENCODE_DELIMITED); |     pb_encode_ex(&ostream, &PB_Main_msg, main_message, PB_ENCODE_DELIMITED); | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         osMutexAcquire(session->send_bytes_mutex, osWaitForever); |  | ||||||
| 
 |  | ||||||
| #if DEBUG_PRINT | #if DEBUG_PRINT | ||||||
|         printf("\r\nREPONSE DEC(%d): {", ostream.bytes_written); |         string_t str; | ||||||
|         for(int i = 0; i < ostream.bytes_written; ++i) { |         string_init(str); | ||||||
|             printf("%d, ", buffer[i]); |         string_reserve(str, 100 + ostream.bytes_written * 5); | ||||||
|         } |  | ||||||
|         printf("}\r\n"); |  | ||||||
| 
 | 
 | ||||||
|         printf("REPONSE HEX(%d): {", ostream.bytes_written); |         string_cat_printf(str, "\r\nREPONSE DEC(%d): {", ostream.bytes_written); | ||||||
|         for(int i = 0; i < ostream.bytes_written; ++i) { |         for(int i = 0; i < ostream.bytes_written; ++i) { | ||||||
|             printf("%02X", buffer[i]); |             string_cat_printf(str, "%d, ", buffer[i]); | ||||||
|         } |         } | ||||||
|         printf("}\r\n\r\n"); |         string_cat_printf(str, "}\r\n"); | ||||||
|  | 
 | ||||||
|  |         printf("%s", string_get_cstr(str)); | ||||||
|  |         string_clean(str); | ||||||
|  |         string_reserve(str, 100 + ostream.bytes_written * 3); | ||||||
|  | 
 | ||||||
|  |         string_cat_printf(str, "REPONSE HEX(%d): {", ostream.bytes_written); | ||||||
|  |         for(int i = 0; i < ostream.bytes_written; ++i) { | ||||||
|  |             string_cat_printf(str, "%02X", buffer[i]); | ||||||
|  |         } | ||||||
|  |         string_cat_printf(str, "}\r\n\r\n"); | ||||||
|  | 
 | ||||||
|  |         printf("%s", string_get_cstr(str)); | ||||||
| #endif // DEBUG_PRINT
 | #endif // DEBUG_PRINT
 | ||||||
| 
 | 
 | ||||||
|  |         osMutexAcquire(session->send_bytes_mutex, osWaitForever); | ||||||
|         if(session->send_bytes_callback) { |         if(session->send_bytes_callback) { | ||||||
|             session->send_bytes_callback( |             session->send_bytes_callback( | ||||||
|                 session->send_bytes_context, buffer, ostream.bytes_written); |                 session->send_bytes_context, buffer, ostream.bytes_written); | ||||||
| @ -408,6 +437,7 @@ int32_t rpc_srv(void* p) { | |||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 free(session->system_contexts); |                 free(session->system_contexts); | ||||||
|  |                 osMutexDelete(session->send_bytes_mutex); | ||||||
|                 RpcHandlerDict_clean(rpc->handlers); |                 RpcHandlerDict_clean(rpc->handlers); | ||||||
|                 rpc->busy = false; |                 rpc->busy = false; | ||||||
|             } else { |             } else { | ||||||
|  | |||||||
							
								
								
									
										78
									
								
								applications/rpc/rpc_app.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								applications/rpc/rpc_app.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,78 @@ | |||||||
|  | #include "flipper.pb.h" | ||||||
|  | #include "furi/record.h" | ||||||
|  | #include "status.pb.h" | ||||||
|  | #include "rpc_i.h" | ||||||
|  | #include <furi.h> | ||||||
|  | #include <loader/loader.h> | ||||||
|  | 
 | ||||||
|  | void rpc_system_app_start_process(const PB_Main* request, void* context) { | ||||||
|  |     Rpc* rpc = context; | ||||||
|  |     furi_assert(rpc); | ||||||
|  |     furi_assert(request); | ||||||
|  |     furi_assert(request->which_content == PB_Main_app_start_tag); | ||||||
|  |     PB_CommandStatus result = PB_CommandStatus_ERROR_APP_CANT_START; | ||||||
|  | 
 | ||||||
|  |     Loader* loader = furi_record_open("loader"); | ||||||
|  |     const char* app_name = request->content.app_start.name; | ||||||
|  |     if(app_name) { | ||||||
|  |         const char* app_args = request->content.app_start.args; | ||||||
|  |         LoaderStatus status = loader_start(loader, app_name, app_args); | ||||||
|  |         if(status == LoaderStatusErrorAppStarted) { | ||||||
|  |             result = PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED; | ||||||
|  |         } else if(status == LoaderStatusErrorInternal) { | ||||||
|  |             result = PB_CommandStatus_ERROR_APP_CANT_START; | ||||||
|  |         } else if(status == LoaderStatusErrorUnknownApp) { | ||||||
|  |             result = PB_CommandStatus_ERROR_INVALID_PARAMETERS; | ||||||
|  |         } else if(status == LoaderStatusOk) { | ||||||
|  |             result = PB_CommandStatus_OK; | ||||||
|  |         } else { | ||||||
|  |             furi_assert(0); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         result = PB_CommandStatus_ERROR_INVALID_PARAMETERS; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_record_close("loader"); | ||||||
|  | 
 | ||||||
|  |     rpc_encode_and_send_empty(rpc, request->command_id, result); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void rpc_system_app_lock_status_process(const PB_Main* request, void* context) { | ||||||
|  |     Rpc* rpc = context; | ||||||
|  |     furi_assert(rpc); | ||||||
|  |     furi_assert(request); | ||||||
|  |     furi_assert(request->which_content == PB_Main_app_lock_status_request_tag); | ||||||
|  | 
 | ||||||
|  |     Loader* loader = furi_record_open("loader"); | ||||||
|  | 
 | ||||||
|  |     PB_Main response = { | ||||||
|  |         .has_next = false, | ||||||
|  |         .command_status = PB_CommandStatus_OK, | ||||||
|  |         .command_id = request->command_id, | ||||||
|  |         .which_content = PB_Main_app_lock_status_response_tag, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     response.content.app_lock_status_response.locked = loader_is_locked(loader); | ||||||
|  | 
 | ||||||
|  |     furi_record_close("loader"); | ||||||
|  | 
 | ||||||
|  |     rpc_encode_and_send(rpc, &response); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void* rpc_system_app_alloc(Rpc* rpc) { | ||||||
|  |     furi_assert(rpc); | ||||||
|  | 
 | ||||||
|  |     RpcHandler rpc_handler = { | ||||||
|  |         .message_handler = NULL, | ||||||
|  |         .decode_submessage = NULL, | ||||||
|  |         .context = rpc, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     rpc_handler.message_handler = rpc_system_app_start_process; | ||||||
|  |     rpc_add_handler(rpc, PB_Main_app_start_tag, &rpc_handler); | ||||||
|  | 
 | ||||||
|  |     rpc_handler.message_handler = rpc_system_app_lock_status_process; | ||||||
|  |     rpc_add_handler(rpc, PB_Main_app_lock_status_request_tag, &rpc_handler); | ||||||
|  | 
 | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
| @ -22,4 +22,6 @@ void rpc_add_handler(Rpc* rpc, pb_size_t message_tag, RpcHandler* handler); | |||||||
| void* rpc_system_status_alloc(Rpc* rpc); | void* rpc_system_status_alloc(Rpc* rpc); | ||||||
| void* rpc_system_storage_alloc(Rpc* rpc); | void* rpc_system_storage_alloc(Rpc* rpc); | ||||||
| void rpc_system_storage_free(void* ctx); | void rpc_system_storage_free(void* ctx); | ||||||
|  | void* rpc_system_app_alloc(Rpc* rpc); | ||||||
|  | 
 | ||||||
| void rpc_print_message(const PB_Main* message); | void rpc_print_message(const PB_Main* message); | ||||||
|  | |||||||
| @ -340,6 +340,7 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont | |||||||
| 
 | 
 | ||||||
|         char* md5sum = response.content.storage_md5sum_response.md5sum; |         char* md5sum = response.content.storage_md5sum_response.md5sum; | ||||||
|         size_t md5sum_size = sizeof(response.content.storage_md5sum_response.md5sum); |         size_t md5sum_size = sizeof(response.content.storage_md5sum_response.md5sum); | ||||||
|  |         (void)md5sum_size; | ||||||
|         furi_assert(hash_size <= ((md5sum_size - 1) / 2)); |         furi_assert(hash_size <= ((md5sum_size - 1) / 2)); | ||||||
|         for(uint8_t i = 0; i < hash_size; i++) { |         for(uint8_t i = 0; i < hash_size; i++) { | ||||||
|             md5sum += sprintf(md5sum, "%02x", hash[i]); |             md5sum += sprintf(md5sum, "%02x", hash[i]); | ||||||
|  | |||||||
| @ -41,7 +41,7 @@ static uint32_t subghz_frequency_analyzer_worker_expRunningAverageAdaptive( | |||||||
| static int32_t subghz_frequency_analyzer_worker_thread(void* context) { | static int32_t subghz_frequency_analyzer_worker_thread(void* context) { | ||||||
|     SubGhzFrequencyAnalyzerWorker* instance = context; |     SubGhzFrequencyAnalyzerWorker* instance = context; | ||||||
| 
 | 
 | ||||||
|     FrequencyRSSI frequency_rssi; |     FrequencyRSSI frequency_rssi = {.frequency = 0, .rssi = 0}; | ||||||
|     float rssi; |     float rssi; | ||||||
|     uint32_t frequency; |     uint32_t frequency; | ||||||
|     uint32_t frequency_start; |     uint32_t frequency_start; | ||||||
|  | |||||||
| @ -14,6 +14,8 @@ | |||||||
| #include <pb_encode.h> | #include <pb_encode.h> | ||||||
| #include <m-list.h> | #include <m-list.h> | ||||||
| #include <lib/toolbox/md5.h> | #include <lib/toolbox/md5.h> | ||||||
|  | #include <cli/cli.h> | ||||||
|  | #include <loader/loader.h> | ||||||
| 
 | 
 | ||||||
| LIST_DEF(MsgList, PB_Main, M_POD_OPLIST) | LIST_DEF(MsgList, PB_Main, M_POD_OPLIST) | ||||||
| #define M_OPL_MsgList_t() LIST_OPLIST(MsgList) | #define M_OPL_MsgList_t() LIST_OPLIST(MsgList) | ||||||
| @ -108,6 +110,7 @@ static void clean_directory(Storage* fs_api, const char* clean_dir) { | |||||||
|         free(name); |         free(name); | ||||||
|     } else { |     } else { | ||||||
|         FS_Error error = storage_common_mkdir(fs_api, clean_dir); |         FS_Error error = storage_common_mkdir(fs_api, clean_dir); | ||||||
|  |         (void)error; | ||||||
|         furi_assert(error == FSE_OK); |         furi_assert(error == FSE_OK); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -172,6 +175,7 @@ static void output_bytes_callback(void* ctx, uint8_t* got_bytes, size_t got_size | |||||||
|     StreamBufferHandle_t stream_buffer = ctx; |     StreamBufferHandle_t stream_buffer = ctx; | ||||||
| 
 | 
 | ||||||
|     size_t bytes_sent = xStreamBufferSend(stream_buffer, got_bytes, got_size, osWaitForever); |     size_t bytes_sent = xStreamBufferSend(stream_buffer, got_bytes, got_size, osWaitForever); | ||||||
|  |     (void)bytes_sent; | ||||||
|     furi_assert(bytes_sent == got_size); |     furi_assert(bytes_sent == got_size); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -346,6 +350,12 @@ static void test_rpc_compare_messages(PB_Main* result, PB_Main* expected) { | |||||||
|         /* rpc doesn't send it */ |         /* rpc doesn't send it */ | ||||||
|         mu_check(0); |         mu_check(0); | ||||||
|         break; |         break; | ||||||
|  |     case PB_Main_app_lock_status_response_tag: { | ||||||
|  |         bool result_locked = result->content.app_lock_status_response.locked; | ||||||
|  |         bool expected_locked = expected->content.app_lock_status_response.locked; | ||||||
|  |         mu_check(result_locked == expected_locked); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|     case PB_Main_storage_read_response_tag: { |     case PB_Main_storage_read_response_tag: { | ||||||
|         bool result_has_msg_file = result->content.storage_read_response.has_file; |         bool result_has_msg_file = result->content.storage_read_response.has_file; | ||||||
|         bool expected_has_msg_file = expected->content.storage_read_response.has_file; |         bool expected_has_msg_file = expected->content.storage_read_response.has_file; | ||||||
| @ -588,6 +598,7 @@ static void test_storage_read_run(const char* path, uint32_t command_id) { | |||||||
| static void test_create_dir(const char* path) { | static void test_create_dir(const char* path) { | ||||||
|     Storage* fs_api = furi_record_open("storage"); |     Storage* fs_api = furi_record_open("storage"); | ||||||
|     FS_Error error = storage_common_mkdir(fs_api, path); |     FS_Error error = storage_common_mkdir(fs_api, path); | ||||||
|  |     (void)error; | ||||||
|     furi_assert((error == FSE_OK) || (error == FSE_EXIST)); |     furi_assert((error == FSE_OK) || (error == FSE_EXIST)); | ||||||
|     furi_record_close("storage"); |     furi_record_close("storage"); | ||||||
| } | } | ||||||
| @ -717,7 +728,7 @@ MU_TEST(test_storage_write) { | |||||||
|         ++command_id, |         ++command_id, | ||||||
|         PB_CommandStatus_ERROR_STORAGE_NOT_EXIST); |         PB_CommandStatus_ERROR_STORAGE_NOT_EXIST); | ||||||
|     test_storage_write_run(TEST_DIR "test2.txt", 1, 50, ++command_id, PB_CommandStatus_OK); |     test_storage_write_run(TEST_DIR "test2.txt", 1, 50, ++command_id, PB_CommandStatus_OK); | ||||||
|     test_storage_write_run(TEST_DIR "test2.txt", 4096, 1, ++command_id, PB_CommandStatus_OK); |     test_storage_write_run(TEST_DIR "test2.txt", 512, 3, ++command_id, PB_CommandStatus_OK); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MU_TEST(test_storage_interrupt_continuous_same_system) { | MU_TEST(test_storage_interrupt_continuous_same_system) { | ||||||
| @ -866,6 +877,7 @@ MU_TEST(test_storage_mkdir) { | |||||||
| 
 | 
 | ||||||
|     Storage* fs_api = furi_record_open("storage"); |     Storage* fs_api = furi_record_open("storage"); | ||||||
|     FS_Error error = storage_common_remove(fs_api, TEST_DIR "dir1"); |     FS_Error error = storage_common_remove(fs_api, TEST_DIR "dir1"); | ||||||
|  |     (void)error; | ||||||
|     furi_assert(error == FSE_OK); |     furi_assert(error == FSE_OK); | ||||||
|     furi_record_close("storage"); |     furi_record_close("storage"); | ||||||
| 
 | 
 | ||||||
| @ -1018,10 +1030,134 @@ MU_TEST_SUITE(test_rpc_storage) { | |||||||
|     MU_RUN_TEST(test_storage_interrupt_continuous_another_system); |     MU_RUN_TEST(test_storage_interrupt_continuous_another_system); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void test_app_create_request( | ||||||
|  |     PB_Main* request, | ||||||
|  |     const char* app_name, | ||||||
|  |     const char* app_args, | ||||||
|  |     uint32_t command_id) { | ||||||
|  |     request->command_id = command_id; | ||||||
|  |     request->command_status = PB_CommandStatus_OK; | ||||||
|  |     request->cb_content.funcs.encode = NULL; | ||||||
|  |     request->which_content = PB_Main_app_start_tag; | ||||||
|  |     request->has_next = false; | ||||||
|  | 
 | ||||||
|  |     if(app_name) { | ||||||
|  |         char* msg_app_name = furi_alloc(strlen(app_name) + 1); | ||||||
|  |         strcpy(msg_app_name, app_name); | ||||||
|  |         request->content.app_start.name = msg_app_name; | ||||||
|  |     } else { | ||||||
|  |         request->content.app_start.name = NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(app_args) { | ||||||
|  |         char* msg_app_args = furi_alloc(strlen(app_args) + 1); | ||||||
|  |         strcpy(msg_app_args, app_args); | ||||||
|  |         request->content.app_start.args = msg_app_args; | ||||||
|  |     } else { | ||||||
|  |         request->content.app_start.args = NULL; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void test_app_start_run( | ||||||
|  |     const char* app_name, | ||||||
|  |     const char* app_args, | ||||||
|  |     PB_CommandStatus status, | ||||||
|  |     uint32_t command_id) { | ||||||
|  |     PB_Main request; | ||||||
|  |     MsgList_t expected_msg_list; | ||||||
|  |     MsgList_init(expected_msg_list); | ||||||
|  | 
 | ||||||
|  |     test_app_create_request(&request, app_name, app_args, command_id); | ||||||
|  |     test_rpc_add_empty_to_list(expected_msg_list, status, command_id); | ||||||
|  | 
 | ||||||
|  |     test_rpc_encode_and_feed_one(&request); | ||||||
|  |     test_rpc_decode_and_compare(expected_msg_list); | ||||||
|  | 
 | ||||||
|  |     pb_release(&PB_Main_msg, &request); | ||||||
|  |     test_rpc_free_msg_list(expected_msg_list); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void test_app_get_status_lock_run(bool locked_expected, uint32_t command_id) { | ||||||
|  |     PB_Main request = { | ||||||
|  |         .command_id = command_id, | ||||||
|  |         .command_status = PB_CommandStatus_OK, | ||||||
|  |         .which_content = PB_Main_app_lock_status_request_tag, | ||||||
|  |         .has_next = false, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     MsgList_t expected_msg_list; | ||||||
|  |     MsgList_init(expected_msg_list); | ||||||
|  |     PB_Main* response = MsgList_push_new(expected_msg_list); | ||||||
|  |     response->command_id = command_id; | ||||||
|  |     response->command_status = PB_CommandStatus_OK; | ||||||
|  |     response->which_content = PB_Main_app_lock_status_response_tag; | ||||||
|  |     response->has_next = false; | ||||||
|  |     response->content.app_lock_status_response.locked = locked_expected; | ||||||
|  | 
 | ||||||
|  |     test_rpc_encode_and_feed_one(&request); | ||||||
|  |     test_rpc_decode_and_compare(expected_msg_list); | ||||||
|  | 
 | ||||||
|  |     pb_release(&PB_Main_msg, &request); | ||||||
|  |     test_rpc_free_msg_list(expected_msg_list); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MU_TEST(test_app_start_and_lock_status) { | ||||||
|  |     test_app_get_status_lock_run(false, ++command_id); | ||||||
|  |     test_app_start_run(NULL, "/ext/file", PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id); | ||||||
|  |     test_app_start_run(NULL, NULL, PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id); | ||||||
|  |     test_app_get_status_lock_run(false, ++command_id); | ||||||
|  |     test_app_start_run( | ||||||
|  |         "skynet_destroy_world_app", NULL, PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id); | ||||||
|  |     test_app_get_status_lock_run(false, ++command_id); | ||||||
|  | 
 | ||||||
|  |     test_app_start_run("Delay Test App", "0", PB_CommandStatus_OK, ++command_id); | ||||||
|  |     delay(100); | ||||||
|  |     test_app_get_status_lock_run(false, ++command_id); | ||||||
|  | 
 | ||||||
|  |     test_app_start_run("Delay Test App", "200", PB_CommandStatus_OK, ++command_id); | ||||||
|  |     test_app_get_status_lock_run(true, ++command_id); | ||||||
|  |     delay(100); | ||||||
|  |     test_app_get_status_lock_run(true, ++command_id); | ||||||
|  |     test_app_start_run( | ||||||
|  |         "Delay Test App", "0", PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED, ++command_id); | ||||||
|  |     delay(200); | ||||||
|  |     test_app_get_status_lock_run(false, ++command_id); | ||||||
|  | 
 | ||||||
|  |     test_app_start_run("Delay Test App", "500", PB_CommandStatus_OK, ++command_id); | ||||||
|  |     delay(100); | ||||||
|  |     test_app_get_status_lock_run(true, ++command_id); | ||||||
|  |     test_app_start_run("Infrared", "0", PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED, ++command_id); | ||||||
|  |     delay(100); | ||||||
|  |     test_app_get_status_lock_run(true, ++command_id); | ||||||
|  |     test_app_start_run( | ||||||
|  |         "2_girls_1_app", "0", PB_CommandStatus_ERROR_INVALID_PARAMETERS, ++command_id); | ||||||
|  |     delay(100); | ||||||
|  |     test_app_get_status_lock_run(true, ++command_id); | ||||||
|  |     delay(500); | ||||||
|  |     test_app_get_status_lock_run(false, ++command_id); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MU_TEST_SUITE(test_rpc_app) { | ||||||
|  |     MU_SUITE_CONFIGURE(&test_rpc_storage_setup, &test_rpc_storage_teardown); | ||||||
|  | 
 | ||||||
|  |     MU_RUN_TEST(test_app_start_and_lock_status); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int run_minunit_test_rpc() { | int run_minunit_test_rpc() { | ||||||
|     MU_RUN_SUITE(test_rpc_storage); |     MU_RUN_SUITE(test_rpc_storage); | ||||||
|     MU_RUN_SUITE(test_rpc_status); |     MU_RUN_SUITE(test_rpc_status); | ||||||
|  |     MU_RUN_SUITE(test_rpc_app); | ||||||
|     MU_REPORT(); |     MU_REPORT(); | ||||||
| 
 | 
 | ||||||
|     return MU_EXIT_CODE; |     return MU_EXIT_CODE; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | int32_t delay_test_app(void* p) { | ||||||
|  |     int timeout = atoi((const char*)p); | ||||||
|  | 
 | ||||||
|  |     if(timeout > 0) { | ||||||
|  |         delay(timeout); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,33 +1,51 @@ | |||||||
|  | #include "m-string.h" | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <furi-hal.h> | #include <furi-hal.h> | ||||||
| #include "minunit_vars.h" | #include "minunit_vars.h" | ||||||
| #include <notification/notification-messages.h> | #include <notification/notification-messages.h> | ||||||
|  | #include <cli/cli.h> | ||||||
|  | #include <loader/loader.h> | ||||||
| 
 | 
 | ||||||
| int run_minunit(); | int run_minunit(); | ||||||
| int run_minunit_test_irda_decoder_encoder(); | int run_minunit_test_irda_decoder_encoder(); | ||||||
| int run_minunit_test_rpc(); | int run_minunit_test_rpc(); | ||||||
| 
 | 
 | ||||||
| int32_t flipper_test_app(void* p) { | void unit_tests_cli(Cli* cli, string_t args, void* context) { | ||||||
|     uint32_t test_result = 0; |     uint32_t test_result = 0; | ||||||
|  |     minunit_run = 0; | ||||||
|  |     minunit_assert = 0; | ||||||
|  |     minunit_fail = 0; | ||||||
|  |     minunit_status = 0; | ||||||
|  | 
 | ||||||
|  |     Loader* loader = furi_record_open("loader"); | ||||||
|  |     furi_record_close("loader"); | ||||||
| 
 | 
 | ||||||
|     NotificationApp* notification = furi_record_open("notification"); |     NotificationApp* notification = furi_record_open("notification"); | ||||||
|  |     furi_record_close("notification"); | ||||||
| 
 | 
 | ||||||
|  |     if(loader_is_locked(loader)) { | ||||||
|  |         FURI_LOG_E("UNIT_TESTS", "RPC: stop all applications to run tests"); | ||||||
|  |         notification_message(notification, &sequence_blink_magenta_100); | ||||||
|  |     } else { | ||||||
|         notification_message_block(notification, &sequence_set_only_blue_255); |         notification_message_block(notification, &sequence_set_only_blue_255); | ||||||
| 
 | 
 | ||||||
|     //    test_result |= run_minunit();     // disabled as it fails randomly
 |         test_result |= run_minunit(); | ||||||
|         test_result |= run_minunit_test_irda_decoder_encoder(); |         test_result |= run_minunit_test_irda_decoder_encoder(); | ||||||
|         test_result |= run_minunit_test_rpc(); |         test_result |= run_minunit_test_rpc(); | ||||||
| 
 | 
 | ||||||
|         if(test_result == 0) { |         if(test_result == 0) { | ||||||
|         // test passed
 |  | ||||||
|             notification_message(notification, &sequence_success); |             notification_message(notification, &sequence_success); | ||||||
|  |             FURI_LOG_I("UNIT_TESTS", "PASSED"); | ||||||
|         } else { |         } else { | ||||||
|         // test failed
 |  | ||||||
|             notification_message(notification, &sequence_error); |             notification_message(notification, &sequence_error); | ||||||
|  |             FURI_LOG_E("UNIT_TESTS", "FAILED"); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | } | ||||||
|     furi_record_close("notification"); | 
 | ||||||
| 
 | void unit_tests_cli_init() { | ||||||
|     return 0; |     Cli* cli = furi_record_open("cli"); | ||||||
|  |     cli_add_command(cli, "unit_tests", CliCommandFlagParallelSafe, unit_tests_cli, NULL); | ||||||
|  |     furi_record_close("cli"); | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										18
									
								
								assets/compiled/application.pb.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								assets/compiled/application.pb.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | /* Automatically generated nanopb constant definitions */ | ||||||
|  | /* Generated by nanopb-0.4.5 */ | ||||||
|  | 
 | ||||||
|  | #include "application.pb.h" | ||||||
|  | #if PB_PROTO_HEADER_VERSION != 40 | ||||||
|  | #error Regenerate this file with the current version of nanopb generator. | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | PB_BIND(PB_App_Start, PB_App_Start, AUTO) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | PB_BIND(PB_App_LockStatusRequest, PB_App_LockStatusRequest, AUTO) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | PB_BIND(PB_App_LockStatusResponse, PB_App_LockStatusResponse, AUTO) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
							
								
								
									
										79
									
								
								assets/compiled/application.pb.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								assets/compiled/application.pb.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | |||||||
|  | /* Automatically generated nanopb header */ | ||||||
|  | /* Generated by nanopb-0.4.5 */ | ||||||
|  | 
 | ||||||
|  | #ifndef PB_PB_APP_APPLICATION_PB_H_INCLUDED | ||||||
|  | #define PB_PB_APP_APPLICATION_PB_H_INCLUDED | ||||||
|  | #include <pb.h> | ||||||
|  | 
 | ||||||
|  | #if PB_PROTO_HEADER_VERSION != 40 | ||||||
|  | #error Regenerate this file with the current version of nanopb generator. | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /* Struct definitions */ | ||||||
|  | typedef struct _PB_App_LockStatusRequest {  | ||||||
|  |     char dummy_field; | ||||||
|  | } PB_App_LockStatusRequest; | ||||||
|  | 
 | ||||||
|  | typedef struct _PB_App_Start {  | ||||||
|  |     char *name;  | ||||||
|  |     char *args;  | ||||||
|  | } PB_App_Start; | ||||||
|  | 
 | ||||||
|  | typedef struct _PB_App_LockStatusResponse {  | ||||||
|  |     bool locked;  | ||||||
|  | } PB_App_LockStatusResponse; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /* Initializer values for message structs */ | ||||||
|  | #define PB_App_Start_init_default                {NULL, NULL} | ||||||
|  | #define PB_App_LockStatusRequest_init_default    {0} | ||||||
|  | #define PB_App_LockStatusResponse_init_default   {0} | ||||||
|  | #define PB_App_Start_init_zero                   {NULL, NULL} | ||||||
|  | #define PB_App_LockStatusRequest_init_zero       {0} | ||||||
|  | #define PB_App_LockStatusResponse_init_zero      {0} | ||||||
|  | 
 | ||||||
|  | /* Field tags (for use in manual encoding/decoding) */ | ||||||
|  | #define PB_App_Start_name_tag                    1 | ||||||
|  | #define PB_App_Start_args_tag                    2 | ||||||
|  | #define PB_App_LockStatusResponse_locked_tag     1 | ||||||
|  | 
 | ||||||
|  | /* Struct field encoding specification for nanopb */ | ||||||
|  | #define PB_App_Start_FIELDLIST(X, a) \ | ||||||
|  | X(a, POINTER,  SINGULAR, STRING,   name,              1) \ | ||||||
|  | X(a, POINTER,  SINGULAR, STRING,   args,              2) | ||||||
|  | #define PB_App_Start_CALLBACK NULL | ||||||
|  | #define PB_App_Start_DEFAULT NULL | ||||||
|  | 
 | ||||||
|  | #define PB_App_LockStatusRequest_FIELDLIST(X, a) \ | ||||||
|  | 
 | ||||||
|  | #define PB_App_LockStatusRequest_CALLBACK NULL | ||||||
|  | #define PB_App_LockStatusRequest_DEFAULT NULL | ||||||
|  | 
 | ||||||
|  | #define PB_App_LockStatusResponse_FIELDLIST(X, a) \ | ||||||
|  | X(a, STATIC,   SINGULAR, BOOL,     locked,            1) | ||||||
|  | #define PB_App_LockStatusResponse_CALLBACK NULL | ||||||
|  | #define PB_App_LockStatusResponse_DEFAULT NULL | ||||||
|  | 
 | ||||||
|  | extern const pb_msgdesc_t PB_App_Start_msg; | ||||||
|  | extern const pb_msgdesc_t PB_App_LockStatusRequest_msg; | ||||||
|  | extern const pb_msgdesc_t PB_App_LockStatusResponse_msg; | ||||||
|  | 
 | ||||||
|  | /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ | ||||||
|  | #define PB_App_Start_fields &PB_App_Start_msg | ||||||
|  | #define PB_App_LockStatusRequest_fields &PB_App_LockStatusRequest_msg | ||||||
|  | #define PB_App_LockStatusResponse_fields &PB_App_LockStatusResponse_msg | ||||||
|  | 
 | ||||||
|  | /* Maximum encoded size of messages (where known) */ | ||||||
|  | /* PB_App_Start_size depends on runtime parameters */ | ||||||
|  | #define PB_App_LockStatusRequest_size            0 | ||||||
|  | #define PB_App_LockStatusResponse_size           2 | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } /* extern "C" */ | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
| @ -6,6 +6,7 @@ | |||||||
| #include <pb.h> | #include <pb.h> | ||||||
| #include "storage.pb.h" | #include "storage.pb.h" | ||||||
| #include "status.pb.h" | #include "status.pb.h" | ||||||
|  | #include "application.pb.h" | ||||||
| 
 | 
 | ||||||
| #if PB_PROTO_HEADER_VERSION != 40 | #if PB_PROTO_HEADER_VERSION != 40 | ||||||
| #error Regenerate this file with the current version of nanopb generator. | #error Regenerate this file with the current version of nanopb generator. | ||||||
| @ -28,7 +29,9 @@ typedef enum _PB_CommandStatus { | |||||||
|     PB_CommandStatus_ERROR_STORAGE_INVALID_NAME = 10, /* *< Invalid name/path */ |     PB_CommandStatus_ERROR_STORAGE_INVALID_NAME = 10, /* *< Invalid name/path */ | ||||||
|     PB_CommandStatus_ERROR_STORAGE_INTERNAL = 11, /* *< Internal error */ |     PB_CommandStatus_ERROR_STORAGE_INTERNAL = 11, /* *< Internal error */ | ||||||
|     PB_CommandStatus_ERROR_STORAGE_NOT_IMPLEMENTED = 12, /* *< Functon not implemented */ |     PB_CommandStatus_ERROR_STORAGE_NOT_IMPLEMENTED = 12, /* *< Functon not implemented */ | ||||||
|     PB_CommandStatus_ERROR_STORAGE_ALREADY_OPEN = 13 /* *< File/Dir already opened */ |     PB_CommandStatus_ERROR_STORAGE_ALREADY_OPEN = 13, /* *< File/Dir already opened */ | ||||||
|  |     PB_CommandStatus_ERROR_APP_CANT_START = 16, /* *< Can't start app - either wrong name, or internal error */ | ||||||
|  |     PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED = 17 /* *<  Another app is running */ | ||||||
| } PB_CommandStatus; | } PB_CommandStatus; | ||||||
| 
 | 
 | ||||||
| /* Struct definitions */ | /* Struct definitions */ | ||||||
| @ -58,14 +61,17 @@ typedef struct _PB_Main { | |||||||
|         PB_Storage_MkdirRequest storage_mkdir_request; |         PB_Storage_MkdirRequest storage_mkdir_request; | ||||||
|         PB_Storage_Md5sumRequest storage_md5sum_request; |         PB_Storage_Md5sumRequest storage_md5sum_request; | ||||||
|         PB_Storage_Md5sumResponse storage_md5sum_response; |         PB_Storage_Md5sumResponse storage_md5sum_response; | ||||||
|  |         PB_App_Start app_start; | ||||||
|  |         PB_App_LockStatusRequest app_lock_status_request; | ||||||
|  |         PB_App_LockStatusResponse app_lock_status_response; | ||||||
|     } content;  |     } content;  | ||||||
| } PB_Main; | } PB_Main; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /* Helper constants for enums */ | /* Helper constants for enums */ | ||||||
| #define _PB_CommandStatus_MIN PB_CommandStatus_OK | #define _PB_CommandStatus_MIN PB_CommandStatus_OK | ||||||
| #define _PB_CommandStatus_MAX PB_CommandStatus_ERROR_INVALID_PARAMETERS | #define _PB_CommandStatus_MAX PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED | ||||||
| #define _PB_CommandStatus_ARRAYSIZE ((PB_CommandStatus)(PB_CommandStatus_ERROR_INVALID_PARAMETERS+1)) | #define _PB_CommandStatus_ARRAYSIZE ((PB_CommandStatus)(PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED+1)) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| @ -94,6 +100,9 @@ extern "C" { | |||||||
| #define PB_Main_storage_mkdir_request_tag        13 | #define PB_Main_storage_mkdir_request_tag        13 | ||||||
| #define PB_Main_storage_md5sum_request_tag       14 | #define PB_Main_storage_md5sum_request_tag       14 | ||||||
| #define PB_Main_storage_md5sum_response_tag      15 | #define PB_Main_storage_md5sum_response_tag      15 | ||||||
|  | #define PB_Main_app_start_tag                    16 | ||||||
|  | #define PB_Main_app_lock_status_request_tag      17 | ||||||
|  | #define PB_Main_app_lock_status_response_tag     18 | ||||||
| 
 | 
 | ||||||
| /* Struct field encoding specification for nanopb */ | /* Struct field encoding specification for nanopb */ | ||||||
| #define PB_Empty_FIELDLIST(X, a) \ | #define PB_Empty_FIELDLIST(X, a) \ | ||||||
| @ -116,7 +125,10 @@ X(a, STATIC,   ONEOF,    MSG_W_CB, (content,storage_write_request,content.storag | |||||||
| X(a, STATIC,   ONEOF,    MSG_W_CB, (content,storage_delete_request,content.storage_delete_request),  12) \ | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,storage_delete_request,content.storage_delete_request),  12) \ | ||||||
| X(a, STATIC,   ONEOF,    MSG_W_CB, (content,storage_mkdir_request,content.storage_mkdir_request),  13) \ | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,storage_mkdir_request,content.storage_mkdir_request),  13) \ | ||||||
| X(a, STATIC,   ONEOF,    MSG_W_CB, (content,storage_md5sum_request,content.storage_md5sum_request),  14) \ | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,storage_md5sum_request,content.storage_md5sum_request),  14) \ | ||||||
| X(a, STATIC,   ONEOF,    MSG_W_CB, (content,storage_md5sum_response,content.storage_md5sum_response),  15) | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,storage_md5sum_response,content.storage_md5sum_response),  15) \ | ||||||
|  | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,app_start,content.app_start),  16) \ | ||||||
|  | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,app_lock_status_request,content.app_lock_status_request),  17) \ | ||||||
|  | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,app_lock_status_response,content.app_lock_status_response),  18) | ||||||
| #define PB_Main_CALLBACK NULL | #define PB_Main_CALLBACK NULL | ||||||
| #define PB_Main_DEFAULT NULL | #define PB_Main_DEFAULT NULL | ||||||
| #define PB_Main_content_empty_MSGTYPE PB_Empty | #define PB_Main_content_empty_MSGTYPE PB_Empty | ||||||
| @ -131,6 +143,9 @@ X(a, STATIC,   ONEOF,    MSG_W_CB, (content,storage_md5sum_response,content.stor | |||||||
| #define PB_Main_content_storage_mkdir_request_MSGTYPE PB_Storage_MkdirRequest | #define PB_Main_content_storage_mkdir_request_MSGTYPE PB_Storage_MkdirRequest | ||||||
| #define PB_Main_content_storage_md5sum_request_MSGTYPE PB_Storage_Md5sumRequest | #define PB_Main_content_storage_md5sum_request_MSGTYPE PB_Storage_Md5sumRequest | ||||||
| #define PB_Main_content_storage_md5sum_response_MSGTYPE PB_Storage_Md5sumResponse | #define PB_Main_content_storage_md5sum_response_MSGTYPE PB_Storage_Md5sumResponse | ||||||
|  | #define PB_Main_content_app_start_MSGTYPE PB_App_Start | ||||||
|  | #define PB_Main_content_app_lock_status_request_MSGTYPE PB_App_LockStatusRequest | ||||||
|  | #define PB_Main_content_app_lock_status_response_MSGTYPE PB_App_LockStatusResponse | ||||||
| 
 | 
 | ||||||
| extern const pb_msgdesc_t PB_Empty_msg; | extern const pb_msgdesc_t PB_Empty_msg; | ||||||
| extern const pb_msgdesc_t PB_Main_msg; | extern const pb_msgdesc_t PB_Main_msg; | ||||||
| @ -141,9 +156,9 @@ extern const pb_msgdesc_t PB_Main_msg; | |||||||
| 
 | 
 | ||||||
| /* Maximum encoded size of messages (where known) */ | /* Maximum encoded size of messages (where known) */ | ||||||
| #define PB_Empty_size                            0 | #define PB_Empty_size                            0 | ||||||
| #if defined(PB_Storage_ListRequest_size) && defined(PB_Storage_ListResponse_size) && defined(PB_Storage_ReadRequest_size) && defined(PB_Storage_ReadResponse_size) && defined(PB_Storage_WriteRequest_size) && defined(PB_Storage_DeleteRequest_size) && defined(PB_Storage_MkdirRequest_size) && defined(PB_Storage_Md5sumRequest_size) | #if defined(PB_Storage_ListRequest_size) && defined(PB_Storage_ListResponse_size) && defined(PB_Storage_ReadRequest_size) && defined(PB_Storage_ReadResponse_size) && defined(PB_Storage_WriteRequest_size) && defined(PB_Storage_DeleteRequest_size) && defined(PB_Storage_MkdirRequest_size) && defined(PB_Storage_Md5sumRequest_size) && defined(PB_App_Start_size) | ||||||
| #define PB_Main_size                             (10 + sizeof(union PB_Main_content_size_union)) | #define PB_Main_size                             (10 + sizeof(union PB_Main_content_size_union)) | ||||||
| union PB_Main_content_size_union {char f7[(6 + PB_Storage_ListRequest_size)]; char f8[(6 + PB_Storage_ListResponse_size)]; char f9[(6 + PB_Storage_ReadRequest_size)]; char f10[(6 + PB_Storage_ReadResponse_size)]; char f11[(6 + PB_Storage_WriteRequest_size)]; char f12[(6 + PB_Storage_DeleteRequest_size)]; char f13[(6 + PB_Storage_MkdirRequest_size)]; char f14[(6 + PB_Storage_Md5sumRequest_size)]; char f0[36];}; | union PB_Main_content_size_union {char f7[(6 + PB_Storage_ListRequest_size)]; char f8[(6 + PB_Storage_ListResponse_size)]; char f9[(6 + PB_Storage_ReadRequest_size)]; char f10[(6 + PB_Storage_ReadResponse_size)]; char f11[(6 + PB_Storage_WriteRequest_size)]; char f12[(6 + PB_Storage_DeleteRequest_size)]; char f13[(6 + PB_Storage_MkdirRequest_size)]; char f14[(6 + PB_Storage_Md5sumRequest_size)]; char f16[(7 + PB_App_Start_size)]; char f0[36];}; | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
|  | |||||||
| @ -1 +1 @@ | |||||||
| Subproject commit 41599b8e6a6b33a229e8f5fa58de1a2cfcc8184a | Subproject commit 8e6db414beed5aff0902f2cca2f4146a0dffb7a1 | ||||||
| @ -56,7 +56,7 @@ void subghz_protocol_came_twee_free(SubGhzProtocolCameTwee* instance) { | |||||||
| LevelDuration subghz_protocol_came_twee_add_duration_to_upload( | LevelDuration subghz_protocol_came_twee_add_duration_to_upload( | ||||||
|     SubGhzProtocolCameTwee* instance, |     SubGhzProtocolCameTwee* instance, | ||||||
|     ManchesterEncoderResult result) { |     ManchesterEncoderResult result) { | ||||||
|     LevelDuration data; |     LevelDuration data = {.duration = 0, .level = 0}; | ||||||
|     switch(result) { |     switch(result) { | ||||||
|     case ManchesterEncoderResultShortLow: |     case ManchesterEncoderResultShortLow: | ||||||
|         data.duration = instance->common.te_short; |         data.duration = instance->common.te_short; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Albert Kharisov
						Albert Kharisov