From acb2c986635df6c33bbd048f731f41743519566e Mon Sep 17 00:00:00 2001 From: Anna Prosvetova Date: Thu, 16 Dec 2021 15:28:42 +0300 Subject: [PATCH 1/4] CI: pass the target in a fast flash link (#907) --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e7c8546f..d440147f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -147,5 +147,5 @@ jobs: comment-id: ${{ steps.fc.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} body: | - [Click here](https://update.flipperzero.one/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.artifacts-path}}/flipper-z-${{steps.names.outputs.default-target}}-full-${{steps.names.outputs.suffix}}.dfu&channel=${{steps.names.outputs.artifacts-path}}&version=${{steps.names.outputs.short-hash}}) to flash the `${{steps.names.outputs.short-hash}}` version of this branch via WebUSB. + [Click here](https://update.flipperzero.one/?url=https://update.flipperzero.one/builds/firmware/${{steps.names.outputs.artifacts-path}}/flipper-z-${{steps.names.outputs.default-target}}-full-${{steps.names.outputs.suffix}}.dfu&channel=${{steps.names.outputs.artifacts-path}}&version=${{steps.names.outputs.short-hash}}&target=${{steps.names.outputs.default-target}}) to flash the `${{steps.names.outputs.short-hash}}` version of this branch via WebUSB. edit-mode: replace From 4013da5b595af6c77ee8e9bf0644609aa07ec157 Mon Sep 17 00:00:00 2001 From: Anna Prosvetova Date: Thu, 16 Dec 2021 18:41:55 +0300 Subject: [PATCH 2/4] Rpc: implement SystemGetDateTime, SystemSetDateTime, update GuiStartVirtualDisplay (#908) * Rpc: update protobuf sources * Rpc: implement SystemGetDateTime, SystemSetDateTime * Rpc: add first frame parameter to GuiStartVirtualDisplay --- applications/rpc/rpc_gui.c | 10 +++++ applications/rpc/rpc_system.c | 56 +++++++++++++++++++++++ assets/compiled/flipper.pb.h | 18 ++++++-- assets/compiled/gui.pb.h | 19 ++++---- assets/compiled/system.pb.c | 12 +++++ assets/compiled/system.pb.h | 83 +++++++++++++++++++++++++++++++++++ assets/protobuf | 2 +- 7 files changed, 188 insertions(+), 12 deletions(-) diff --git a/applications/rpc/rpc_gui.c b/applications/rpc/rpc_gui.c index 6e9cfb08..4b8b42d1 100644 --- a/applications/rpc/rpc_gui.c +++ b/applications/rpc/rpc_gui.c @@ -159,6 +159,16 @@ void rpc_system_gui_start_virtual_display_process(const PB_Main* request, void* // Glad they both are 1024 for now size_t buffer_size = canvas_get_buffer_size(rpc_gui->gui->canvas); rpc_gui->virtual_display_buffer = furi_alloc(buffer_size); + + if(request->content.gui_start_virtual_display_request.has_first_frame) { + size_t buffer_size = canvas_get_buffer_size(rpc_gui->gui->canvas); + memcpy( + rpc_gui->virtual_display_buffer, + request->content.gui_start_virtual_display_request.first_frame.data->bytes, + buffer_size); + rpc_gui->virtual_display_not_empty = true; + } + rpc_gui->virtual_display_view_port = view_port_alloc(); view_port_draw_callback_set( rpc_gui->virtual_display_view_port, diff --git a/applications/rpc/rpc_system.c b/applications/rpc/rpc_system.c index 01cb3145..56b626f4 100644 --- a/applications/rpc/rpc_system.c +++ b/applications/rpc/rpc_system.c @@ -98,6 +98,56 @@ void rpc_system_system_device_info_process(const PB_Main* request, void* context free(response); } +void rpc_system_system_get_datetime_process(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(request->which_content == PB_Main_system_get_datetime_request_tag); + furi_assert(context); + Rpc* rpc = context; + + FuriHalRtcDateTime datetime; + furi_hal_rtc_get_datetime(&datetime); + + PB_Main* response = furi_alloc(sizeof(PB_Main)); + response->command_id = request->command_id; + response->which_content = PB_Main_system_get_datetime_response_tag; + response->command_status = PB_CommandStatus_OK; + response->content.system_get_datetime_response.has_datetime = true; + response->content.system_get_datetime_response.datetime.hour = datetime.hour; + response->content.system_get_datetime_response.datetime.minute = datetime.minute; + response->content.system_get_datetime_response.datetime.second = datetime.second; + response->content.system_get_datetime_response.datetime.day = datetime.day; + response->content.system_get_datetime_response.datetime.month = datetime.month; + response->content.system_get_datetime_response.datetime.year = datetime.year; + response->content.system_get_datetime_response.datetime.weekday = datetime.weekday; + + rpc_send_and_release(rpc, response); +} + +void rpc_system_system_set_datetime_process(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(request->which_content == PB_Main_system_set_datetime_request_tag); + furi_assert(context); + Rpc* rpc = context; + + if(!request->content.system_set_datetime_request.has_datetime) { + rpc_send_and_release_empty( + rpc, request->command_id, PB_CommandStatus_ERROR_INVALID_PARAMETERS); + return; + } + + FuriHalRtcDateTime datetime; + datetime.hour = request->content.system_set_datetime_request.datetime.hour; + datetime.minute = request->content.system_set_datetime_request.datetime.minute; + datetime.second = request->content.system_set_datetime_request.datetime.second; + datetime.day = request->content.system_set_datetime_request.datetime.day; + datetime.month = request->content.system_set_datetime_request.datetime.month; + datetime.year = request->content.system_set_datetime_request.datetime.year; + datetime.weekday = request->content.system_set_datetime_request.datetime.weekday; + furi_hal_rtc_set_datetime(&datetime); + + rpc_send_and_release_empty(rpc, request->command_id, PB_CommandStatus_OK); +} + void rpc_system_system_factory_reset_process(const PB_Main* request, void* context) { furi_assert(request); furi_assert(request->which_content == PB_Main_system_factory_reset_request_tag); @@ -126,5 +176,11 @@ void* rpc_system_system_alloc(Rpc* rpc) { rpc_handler.message_handler = rpc_system_system_factory_reset_process; rpc_add_handler(rpc, PB_Main_system_factory_reset_request_tag, &rpc_handler); + rpc_handler.message_handler = rpc_system_system_get_datetime_process; + rpc_add_handler(rpc, PB_Main_system_get_datetime_request_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_system_set_datetime_process; + rpc_add_handler(rpc, PB_Main_system_set_datetime_request_tag, &rpc_handler); + return NULL; } diff --git a/assets/compiled/flipper.pb.h b/assets/compiled/flipper.pb.h index 5ce575fd..551b34de 100644 --- a/assets/compiled/flipper.pb.h +++ b/assets/compiled/flipper.pb.h @@ -92,6 +92,9 @@ typedef struct _PB_Main { PB_System_DeviceInfoRequest system_device_info_request; PB_System_DeviceInfoResponse system_device_info_response; PB_System_FactoryResetRequest system_factory_reset_request; + PB_System_GetDateTimeRequest system_get_datetime_request; + PB_System_GetDateTimeResponse system_get_datetime_response; + PB_System_SetDateTimeRequest system_set_datetime_request; } content; } PB_Main; @@ -149,6 +152,9 @@ extern "C" { #define PB_Main_system_device_info_request_tag 32 #define PB_Main_system_device_info_response_tag 33 #define PB_Main_system_factory_reset_request_tag 34 +#define PB_Main_system_get_datetime_request_tag 35 +#define PB_Main_system_get_datetime_response_tag 36 +#define PB_Main_system_set_datetime_request_tag 37 /* Struct field encoding specification for nanopb */ #define PB_Empty_FIELDLIST(X, a) \ @@ -195,7 +201,10 @@ X(a, STATIC, ONEOF, MSG_W_CB, (content,storage_rename_request,content.stora X(a, STATIC, ONEOF, MSG_W_CB, (content,system_reboot_request,content.system_reboot_request), 31) \ X(a, STATIC, ONEOF, MSG_W_CB, (content,system_device_info_request,content.system_device_info_request), 32) \ X(a, STATIC, ONEOF, MSG_W_CB, (content,system_device_info_response,content.system_device_info_response), 33) \ -X(a, STATIC, ONEOF, MSG_W_CB, (content,system_factory_reset_request,content.system_factory_reset_request), 34) +X(a, STATIC, ONEOF, MSG_W_CB, (content,system_factory_reset_request,content.system_factory_reset_request), 34) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,system_get_datetime_request,content.system_get_datetime_request), 35) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,system_get_datetime_response,content.system_get_datetime_response), 36) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,system_set_datetime_request,content.system_set_datetime_request), 37) #define PB_Main_CALLBACK NULL #define PB_Main_DEFAULT NULL #define PB_Main_content_empty_MSGTYPE PB_Empty @@ -229,6 +238,9 @@ X(a, STATIC, ONEOF, MSG_W_CB, (content,system_factory_reset_request,content #define PB_Main_content_system_device_info_request_MSGTYPE PB_System_DeviceInfoRequest #define PB_Main_content_system_device_info_response_MSGTYPE PB_System_DeviceInfoResponse #define PB_Main_content_system_factory_reset_request_MSGTYPE PB_System_FactoryResetRequest +#define PB_Main_content_system_get_datetime_request_MSGTYPE PB_System_GetDateTimeRequest +#define PB_Main_content_system_get_datetime_response_MSGTYPE PB_System_GetDateTimeResponse +#define PB_Main_content_system_set_datetime_request_MSGTYPE PB_System_SetDateTimeRequest extern const pb_msgdesc_t PB_Empty_msg; extern const pb_msgdesc_t PB_StopSession_msg; @@ -242,9 +254,9 @@ extern const pb_msgdesc_t PB_Main_msg; /* Maximum encoded size of messages (where known) */ #define PB_Empty_size 0 #define PB_StopSession_size 0 -#if defined(PB_System_PingRequest_size) && defined(PB_System_PingResponse_size) && 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_StartRequest_size) && defined(PB_Gui_ScreenFrame_size) && defined(PB_Storage_StatRequest_size) && defined(PB_Storage_StatResponse_size) && defined(PB_Storage_InfoRequest_size) && defined(PB_Storage_RenameRequest_size) && defined(PB_System_DeviceInfoResponse_size) +#if defined(PB_System_PingRequest_size) && defined(PB_System_PingResponse_size) && 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_StartRequest_size) && defined(PB_Gui_ScreenFrame_size) && defined(PB_Storage_StatRequest_size) && defined(PB_Storage_StatResponse_size) && defined(PB_Gui_StartVirtualDisplayRequest_size) && defined(PB_Storage_InfoRequest_size) && defined(PB_Storage_RenameRequest_size) && defined(PB_System_DeviceInfoResponse_size) #define PB_Main_size (10 + sizeof(union PB_Main_content_size_union)) -union PB_Main_content_size_union {char f5[(6 + PB_System_PingRequest_size)]; char f6[(6 + PB_System_PingResponse_size)]; 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_StartRequest_size)]; char f22[(7 + PB_Gui_ScreenFrame_size)]; char f24[(7 + PB_Storage_StatRequest_size)]; char f25[(7 + PB_Storage_StatResponse_size)]; char f28[(7 + PB_Storage_InfoRequest_size)]; char f30[(7 + PB_Storage_RenameRequest_size)]; char f33[(7 + PB_System_DeviceInfoResponse_size)]; char f0[36];}; +union PB_Main_content_size_union {char f5[(6 + PB_System_PingRequest_size)]; char f6[(6 + PB_System_PingResponse_size)]; 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_StartRequest_size)]; char f22[(7 + PB_Gui_ScreenFrame_size)]; char f24[(7 + PB_Storage_StatRequest_size)]; char f25[(7 + PB_Storage_StatResponse_size)]; char f26[(7 + PB_Gui_StartVirtualDisplayRequest_size)]; char f28[(7 + PB_Storage_InfoRequest_size)]; char f30[(7 + PB_Storage_RenameRequest_size)]; char f33[(7 + PB_System_DeviceInfoResponse_size)]; char f0[36];}; #endif #ifdef __cplusplus diff --git a/assets/compiled/gui.pb.h b/assets/compiled/gui.pb.h index 84cf1d57..7ab8a97d 100644 --- a/assets/compiled/gui.pb.h +++ b/assets/compiled/gui.pb.h @@ -36,10 +36,6 @@ typedef struct _PB_Gui_StartScreenStreamRequest { char dummy_field; } PB_Gui_StartScreenStreamRequest; -typedef struct _PB_Gui_StartVirtualDisplayRequest { - char dummy_field; -} PB_Gui_StartVirtualDisplayRequest; - typedef struct _PB_Gui_StopScreenStreamRequest { char dummy_field; } PB_Gui_StopScreenStreamRequest; @@ -53,6 +49,11 @@ typedef struct _PB_Gui_SendInputEventRequest { PB_Gui_InputType type; } PB_Gui_SendInputEventRequest; +typedef struct _PB_Gui_StartVirtualDisplayRequest { + bool has_first_frame; + PB_Gui_ScreenFrame first_frame; /* optional */ +} PB_Gui_StartVirtualDisplayRequest; + /* Helper constants for enums */ #define _PB_Gui_InputKey_MIN PB_Gui_InputKey_UP @@ -73,19 +74,20 @@ extern "C" { #define PB_Gui_StartScreenStreamRequest_init_default {0} #define PB_Gui_StopScreenStreamRequest_init_default {0} #define PB_Gui_SendInputEventRequest_init_default {_PB_Gui_InputKey_MIN, _PB_Gui_InputType_MIN} -#define PB_Gui_StartVirtualDisplayRequest_init_default {0} +#define PB_Gui_StartVirtualDisplayRequest_init_default {false, PB_Gui_ScreenFrame_init_default} #define PB_Gui_StopVirtualDisplayRequest_init_default {0} #define PB_Gui_ScreenFrame_init_zero {NULL} #define PB_Gui_StartScreenStreamRequest_init_zero {0} #define PB_Gui_StopScreenStreamRequest_init_zero {0} #define PB_Gui_SendInputEventRequest_init_zero {_PB_Gui_InputKey_MIN, _PB_Gui_InputType_MIN} -#define PB_Gui_StartVirtualDisplayRequest_init_zero {0} +#define PB_Gui_StartVirtualDisplayRequest_init_zero {false, PB_Gui_ScreenFrame_init_zero} #define PB_Gui_StopVirtualDisplayRequest_init_zero {0} /* Field tags (for use in manual encoding/decoding) */ #define PB_Gui_ScreenFrame_data_tag 1 #define PB_Gui_SendInputEventRequest_key_tag 1 #define PB_Gui_SendInputEventRequest_type_tag 2 +#define PB_Gui_StartVirtualDisplayRequest_first_frame_tag 1 /* Struct field encoding specification for nanopb */ #define PB_Gui_ScreenFrame_FIELDLIST(X, a) \ @@ -110,9 +112,10 @@ X(a, STATIC, SINGULAR, UENUM, type, 2) #define PB_Gui_SendInputEventRequest_DEFAULT NULL #define PB_Gui_StartVirtualDisplayRequest_FIELDLIST(X, a) \ - +X(a, STATIC, OPTIONAL, MESSAGE, first_frame, 1) #define PB_Gui_StartVirtualDisplayRequest_CALLBACK NULL #define PB_Gui_StartVirtualDisplayRequest_DEFAULT NULL +#define PB_Gui_StartVirtualDisplayRequest_first_frame_MSGTYPE PB_Gui_ScreenFrame #define PB_Gui_StopVirtualDisplayRequest_FIELDLIST(X, a) \ @@ -136,9 +139,9 @@ extern const pb_msgdesc_t PB_Gui_StopVirtualDisplayRequest_msg; /* Maximum encoded size of messages (where known) */ /* PB_Gui_ScreenFrame_size depends on runtime parameters */ +/* PB_Gui_StartVirtualDisplayRequest_size depends on runtime parameters */ #define PB_Gui_SendInputEventRequest_size 4 #define PB_Gui_StartScreenStreamRequest_size 0 -#define PB_Gui_StartVirtualDisplayRequest_size 0 #define PB_Gui_StopScreenStreamRequest_size 0 #define PB_Gui_StopVirtualDisplayRequest_size 0 diff --git a/assets/compiled/system.pb.c b/assets/compiled/system.pb.c index dd1bc623..89abf7cf 100644 --- a/assets/compiled/system.pb.c +++ b/assets/compiled/system.pb.c @@ -24,5 +24,17 @@ PB_BIND(PB_System_DeviceInfoResponse, PB_System_DeviceInfoResponse, AUTO) PB_BIND(PB_System_FactoryResetRequest, PB_System_FactoryResetRequest, AUTO) +PB_BIND(PB_System_GetDateTimeRequest, PB_System_GetDateTimeRequest, AUTO) + + +PB_BIND(PB_System_GetDateTimeResponse, PB_System_GetDateTimeResponse, AUTO) + + +PB_BIND(PB_System_SetDateTimeRequest, PB_System_SetDateTimeRequest, AUTO) + + +PB_BIND(PB_System_DateTime, PB_System_DateTime, AUTO) + + diff --git a/assets/compiled/system.pb.h b/assets/compiled/system.pb.h index dbb23ff7..be1e6628 100644 --- a/assets/compiled/system.pb.h +++ b/assets/compiled/system.pb.h @@ -29,6 +29,10 @@ typedef struct _PB_System_FactoryResetRequest { char dummy_field; } PB_System_FactoryResetRequest; +typedef struct _PB_System_GetDateTimeRequest { + char dummy_field; +} PB_System_GetDateTimeRequest; + typedef struct _PB_System_PingRequest { pb_bytes_array_t *data; } PB_System_PingRequest; @@ -37,10 +41,32 @@ typedef struct _PB_System_PingResponse { pb_bytes_array_t *data; } PB_System_PingResponse; +typedef struct _PB_System_DateTime { + /* Time */ + uint8_t hour; /* *< Hour in 24H format: 0-23 */ + uint8_t minute; /* *< Minute: 0-59 */ + uint8_t second; /* *< Second: 0-59 */ + /* Date */ + uint8_t day; /* *< Current day: 1-31 */ + uint8_t month; /* *< Current month: 1-12 */ + uint16_t year; /* *< Current year: 2000-2099 */ + uint8_t weekday; /* *< Current weekday: 1-7 */ +} PB_System_DateTime; + typedef struct _PB_System_RebootRequest { PB_System_RebootRequest_RebootMode mode; } PB_System_RebootRequest; +typedef struct _PB_System_GetDateTimeResponse { + bool has_datetime; + PB_System_DateTime datetime; +} PB_System_GetDateTimeResponse; + +typedef struct _PB_System_SetDateTimeRequest { + bool has_datetime; + PB_System_DateTime datetime; +} PB_System_SetDateTimeRequest; + /* Helper constants for enums */ #define _PB_System_RebootRequest_RebootMode_MIN PB_System_RebootRequest_RebootMode_OS @@ -59,19 +85,36 @@ extern "C" { #define PB_System_DeviceInfoRequest_init_default {0} #define PB_System_DeviceInfoResponse_init_default {NULL, NULL} #define PB_System_FactoryResetRequest_init_default {0} +#define PB_System_GetDateTimeRequest_init_default {0} +#define PB_System_GetDateTimeResponse_init_default {false, PB_System_DateTime_init_default} +#define PB_System_SetDateTimeRequest_init_default {false, PB_System_DateTime_init_default} +#define PB_System_DateTime_init_default {0, 0, 0, 0, 0, 0, 0} #define PB_System_PingRequest_init_zero {NULL} #define PB_System_PingResponse_init_zero {NULL} #define PB_System_RebootRequest_init_zero {_PB_System_RebootRequest_RebootMode_MIN} #define PB_System_DeviceInfoRequest_init_zero {0} #define PB_System_DeviceInfoResponse_init_zero {NULL, NULL} #define PB_System_FactoryResetRequest_init_zero {0} +#define PB_System_GetDateTimeRequest_init_zero {0} +#define PB_System_GetDateTimeResponse_init_zero {false, PB_System_DateTime_init_zero} +#define PB_System_SetDateTimeRequest_init_zero {false, PB_System_DateTime_init_zero} +#define PB_System_DateTime_init_zero {0, 0, 0, 0, 0, 0, 0} /* Field tags (for use in manual encoding/decoding) */ #define PB_System_DeviceInfoResponse_key_tag 1 #define PB_System_DeviceInfoResponse_value_tag 2 #define PB_System_PingRequest_data_tag 1 #define PB_System_PingResponse_data_tag 1 +#define PB_System_DateTime_hour_tag 1 +#define PB_System_DateTime_minute_tag 2 +#define PB_System_DateTime_second_tag 3 +#define PB_System_DateTime_day_tag 4 +#define PB_System_DateTime_month_tag 5 +#define PB_System_DateTime_year_tag 6 +#define PB_System_DateTime_weekday_tag 7 #define PB_System_RebootRequest_mode_tag 1 +#define PB_System_GetDateTimeResponse_datetime_tag 1 +#define PB_System_SetDateTimeRequest_datetime_tag 1 /* Struct field encoding specification for nanopb */ #define PB_System_PingRequest_FIELDLIST(X, a) \ @@ -105,12 +148,44 @@ X(a, POINTER, SINGULAR, STRING, value, 2) #define PB_System_FactoryResetRequest_CALLBACK NULL #define PB_System_FactoryResetRequest_DEFAULT NULL +#define PB_System_GetDateTimeRequest_FIELDLIST(X, a) \ + +#define PB_System_GetDateTimeRequest_CALLBACK NULL +#define PB_System_GetDateTimeRequest_DEFAULT NULL + +#define PB_System_GetDateTimeResponse_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, datetime, 1) +#define PB_System_GetDateTimeResponse_CALLBACK NULL +#define PB_System_GetDateTimeResponse_DEFAULT NULL +#define PB_System_GetDateTimeResponse_datetime_MSGTYPE PB_System_DateTime + +#define PB_System_SetDateTimeRequest_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, datetime, 1) +#define PB_System_SetDateTimeRequest_CALLBACK NULL +#define PB_System_SetDateTimeRequest_DEFAULT NULL +#define PB_System_SetDateTimeRequest_datetime_MSGTYPE PB_System_DateTime + +#define PB_System_DateTime_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, hour, 1) \ +X(a, STATIC, SINGULAR, UINT32, minute, 2) \ +X(a, STATIC, SINGULAR, UINT32, second, 3) \ +X(a, STATIC, SINGULAR, UINT32, day, 4) \ +X(a, STATIC, SINGULAR, UINT32, month, 5) \ +X(a, STATIC, SINGULAR, UINT32, year, 6) \ +X(a, STATIC, SINGULAR, UINT32, weekday, 7) +#define PB_System_DateTime_CALLBACK NULL +#define PB_System_DateTime_DEFAULT NULL + extern const pb_msgdesc_t PB_System_PingRequest_msg; extern const pb_msgdesc_t PB_System_PingResponse_msg; extern const pb_msgdesc_t PB_System_RebootRequest_msg; extern const pb_msgdesc_t PB_System_DeviceInfoRequest_msg; extern const pb_msgdesc_t PB_System_DeviceInfoResponse_msg; extern const pb_msgdesc_t PB_System_FactoryResetRequest_msg; +extern const pb_msgdesc_t PB_System_GetDateTimeRequest_msg; +extern const pb_msgdesc_t PB_System_GetDateTimeResponse_msg; +extern const pb_msgdesc_t PB_System_SetDateTimeRequest_msg; +extern const pb_msgdesc_t PB_System_DateTime_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define PB_System_PingRequest_fields &PB_System_PingRequest_msg @@ -119,14 +194,22 @@ extern const pb_msgdesc_t PB_System_FactoryResetRequest_msg; #define PB_System_DeviceInfoRequest_fields &PB_System_DeviceInfoRequest_msg #define PB_System_DeviceInfoResponse_fields &PB_System_DeviceInfoResponse_msg #define PB_System_FactoryResetRequest_fields &PB_System_FactoryResetRequest_msg +#define PB_System_GetDateTimeRequest_fields &PB_System_GetDateTimeRequest_msg +#define PB_System_GetDateTimeResponse_fields &PB_System_GetDateTimeResponse_msg +#define PB_System_SetDateTimeRequest_fields &PB_System_SetDateTimeRequest_msg +#define PB_System_DateTime_fields &PB_System_DateTime_msg /* Maximum encoded size of messages (where known) */ /* PB_System_PingRequest_size depends on runtime parameters */ /* PB_System_PingResponse_size depends on runtime parameters */ /* PB_System_DeviceInfoResponse_size depends on runtime parameters */ +#define PB_System_DateTime_size 22 #define PB_System_DeviceInfoRequest_size 0 #define PB_System_FactoryResetRequest_size 0 +#define PB_System_GetDateTimeRequest_size 0 +#define PB_System_GetDateTimeResponse_size 24 #define PB_System_RebootRequest_size 2 +#define PB_System_SetDateTimeRequest_size 24 #ifdef __cplusplus } /* extern "C" */ diff --git a/assets/protobuf b/assets/protobuf index f6fdc10e..f5365c36 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit f6fdc10e6d111b289188e88ac1d432698bb739cf +Subproject commit f5365c36aa458eb344280560440d6e27232a5667 From e109e2e3e86236e377651fcf03d7ba4b625b863f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Fri, 17 Dec 2021 04:28:51 +0300 Subject: [PATCH 3/4] Debug: remove lxml dependency, pickle SVD tree for faster processing. (#909) * Debug: remove lxml dependency, pickle SVD tree for faster processing. * Debug: remove unused import in svd.py --- .gitignore | 3 +- debug/PyCortexMDebug/README.md | 2 +- debug/PyCortexMDebug/cmdebug/svd.py | 61 +-- debug/PyCortexMDebug/cmdebug/svd_gdb.py | 2 + debug/PyCortexMDebug/cmdebug/x2d.py | 586 ++++++++++++++++++++++++ docker/Dockerfile | 5 +- 6 files changed, 624 insertions(+), 35 deletions(-) create mode 100644 debug/PyCortexMDebug/cmdebug/x2d.py diff --git a/.gitignore b/.gitignore index 3b032201..319cf90c 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ venv/ __pycache__/ *.py[cod] *$py.class +*.pickle .obj/ bindings/ @@ -34,4 +35,4 @@ build CMakeLists.txt # bundle output -dist \ No newline at end of file +dist diff --git a/debug/PyCortexMDebug/README.md b/debug/PyCortexMDebug/README.md index 5bf2eacd..32c76e76 100644 --- a/debug/PyCortexMDebug/README.md +++ b/debug/PyCortexMDebug/README.md @@ -16,7 +16,7 @@ My implementation so far has only tested STM32 chips but should hold for others. expect plenty of errors in the file. Like GPIOA having a register named GPIOB_OSPEEDR and lots of 16-bit registers that are listed as 32! -The implementation consists of two components -- An lxml-based parser module (pysvd) and a GDB file (gdb_svd). +The implementation consists of two components -- An xml parser module (pysvd) and a GDB file (gdb_svd). I haven't yet worked out a perfect workflow for this, though it's quite easy to use when you already tend to have a GDB initialization file for starting up OpenOCD and the like. However your workflow works, just make sure to, in GDB: diff --git a/debug/PyCortexMDebug/cmdebug/svd.py b/debug/PyCortexMDebug/cmdebug/svd.py index a25e69bf..10c9e0af 100755 --- a/debug/PyCortexMDebug/cmdebug/svd.py +++ b/debug/PyCortexMDebug/cmdebug/svd.py @@ -16,7 +16,6 @@ You should have received a copy of the GNU General Public License along with PyCortexMDebug. If not, see . """ -import lxml.objectify as objectify import sys from collections import OrderedDict import os @@ -24,6 +23,7 @@ import pickle import traceback import re import warnings +import x2d class SmartDict: @@ -126,26 +126,31 @@ class SVDFile: def __init__(self, fname): """ - Args: fname: Filename for the SVD file """ - f = objectify.parse(os.path.expanduser(fname)) - root = f.getroot() - periph = root.peripherals.getchildren() self.peripherals = SmartDict() self.base_address = 0 + xml_file_name = os.path.expanduser(fname) + pickle_file_name = xml_file_name + ".pickle" + root = None + if os.path.exists(pickle_file_name): + print("Loading pickled SVD") + root = pickle.load(open(pickle_file_name, "rb")) + else: + print("Loading XML SVD and pickling it") + root = x2d.parse(open(xml_file_name, "rb")) + pickle.dump(root, open(pickle_file_name, "wb"), pickle.HIGHEST_PROTOCOL) + print("Processing SVD tree") # XML elements - for p in periph: + for p in root["device"]["peripherals"]["peripheral"]: try: - if p.tag == "peripheral": - self.peripherals[str(p.name)] = SVDPeripheral(p, self) - else: - # This is some other tag - pass + self.peripherals[p["name"]] = SVDPeripheral(p, self) except SVDNonFatalError as e: - print(e) + # print(e) + pass + print("SVD Ready") def add_register(parent, node): @@ -265,11 +270,11 @@ class SVDPeripheral: self.parent_base_address = parent.base_address # Look for a base address, as it is required - if not hasattr(svd_elem, "baseAddress"): + if "baseAddress" not in svd_elem: raise SVDNonFatalError("Periph without base address") self.base_address = int(str(svd_elem.baseAddress), 0) - if "derivedFrom" in svd_elem.attrib: - derived_from = svd_elem.attrib["derivedFrom"] + if "@derivedFrom" in svd_elem: + derived_from = svd_elem["@derivedFrom"] try: self.name = str(svd_elem.name) except AttributeError: @@ -295,16 +300,14 @@ class SVDPeripheral: self.clusters = SmartDict() if hasattr(svd_elem, "registers"): - registers = [ - r - for r in svd_elem.registers.getchildren() - if r.tag in ["cluster", "register"] - ] - for r in registers: - if r.tag == "cluster": - add_cluster(self, r) - elif r.tag == "register": - add_register(self, r) + if "register" in svd_elem.registers: + for r in svd_elem.registers.register: + if isinstance(r, x2d.ObjectDict): + add_register(self, r) + if "cluster" in svd_elem.registers: + for c in svd_elem.registers.cluster: + if isinstance(c, x2d.ObjectDict): + add_cluster(self, c) def refactor_parent(self, parent): self.parent_base_address = parent.base_address @@ -338,11 +341,11 @@ class SVDPeripheralRegister: else: self.size = 0x20 self.fields = SmartDict() - if hasattr(svd_elem, "fields"): + if "fields" in svd_elem: # Filter fields to only consider those of tag "field" - fields = [f for f in svd_elem.fields.getchildren() if f.tag == "field"] - for f in fields: - self.fields[str(f.name)] = SVDPeripheralRegisterField(f, self) + for f in svd_elem.fields.field: + if isinstance(f, x2d.ObjectDict): + self.fields[str(f.name)] = SVDPeripheralRegisterField(f, self) def refactor_parent(self, parent): self.parent_base_address = parent.base_address diff --git a/debug/PyCortexMDebug/cmdebug/svd_gdb.py b/debug/PyCortexMDebug/cmdebug/svd_gdb.py index 2629a3ce..c7de1110 100755 --- a/debug/PyCortexMDebug/cmdebug/svd_gdb.py +++ b/debug/PyCortexMDebug/cmdebug/svd_gdb.py @@ -23,6 +23,7 @@ import sys import struct import pkg_resources import fnmatch +import traceback from .svd import SVDFile @@ -99,6 +100,7 @@ class LoadSVD(gdb.Command): try: SVD(SVDFile(f)) except Exception as e: + traceback.print_exc() raise gdb.GdbError("Could not load SVD file {} : {}...\n".format(f, e)) diff --git a/debug/PyCortexMDebug/cmdebug/x2d.py b/debug/PyCortexMDebug/cmdebug/x2d.py new file mode 100644 index 00000000..fc3f185d --- /dev/null +++ b/debug/PyCortexMDebug/cmdebug/x2d.py @@ -0,0 +1,586 @@ +#!/usr/bin/env python +"Makes working with XML feel like you are working with JSON" + +try: + from defusedexpat import pyexpat as expat +except ImportError: + from xml.parsers import expat + +from xml.sax.saxutils import XMLGenerator +from xml.sax.xmlreader import AttributesImpl + +try: # pragma no cover + from cStringIO import StringIO +except ImportError: # pragma no cover + try: + from StringIO import StringIO + except ImportError: + from io import StringIO + +from inspect import isgenerator + + +class ObjectDict(dict): + def __getattr__(self, name): + if name in self: + return self[name] + else: + raise AttributeError("No such attribute: " + name) + + +try: # pragma no cover + _basestring = basestring +except NameError: # pragma no cover + _basestring = str +try: # pragma no cover + _unicode = unicode +except NameError: # pragma no cover + _unicode = str + +__author__ = "Martin Blech" +__version__ = "0.12.0" +__license__ = "MIT" + + +class ParsingInterrupted(Exception): + pass + + +class _DictSAXHandler(object): + def __init__( + self, + item_depth=0, + item_callback=lambda *args: True, + xml_attribs=True, + attr_prefix="@", + cdata_key="#text", + force_cdata=False, + cdata_separator="", + postprocessor=None, + dict_constructor=ObjectDict, + strip_whitespace=True, + namespace_separator=":", + namespaces=None, + force_list=None, + comment_key="#comment", + ): + self.path = [] + self.stack = [] + self.data = [] + self.item = None + self.item_depth = item_depth + self.xml_attribs = xml_attribs + self.item_callback = item_callback + self.attr_prefix = attr_prefix + self.cdata_key = cdata_key + self.force_cdata = force_cdata + self.cdata_separator = cdata_separator + self.postprocessor = postprocessor + self.dict_constructor = dict_constructor + self.strip_whitespace = strip_whitespace + self.namespace_separator = namespace_separator + self.namespaces = namespaces + self.namespace_declarations = ObjectDict() + self.force_list = force_list + self.comment_key = comment_key + + def _build_name(self, full_name): + if self.namespaces is None: + return full_name + i = full_name.rfind(self.namespace_separator) + if i == -1: + return full_name + namespace, name = full_name[:i], full_name[i + 1 :] + try: + short_namespace = self.namespaces[namespace] + except KeyError: + short_namespace = namespace + if not short_namespace: + return name + else: + return self.namespace_separator.join((short_namespace, name)) + + def _attrs_to_dict(self, attrs): + if isinstance(attrs, dict): + return attrs + return self.dict_constructor(zip(attrs[0::2], attrs[1::2])) + + def startNamespaceDecl(self, prefix, uri): + self.namespace_declarations[prefix or ""] = uri + + def startElement(self, full_name, attrs): + name = self._build_name(full_name) + attrs = self._attrs_to_dict(attrs) + if attrs and self.namespace_declarations: + attrs["xmlns"] = self.namespace_declarations + self.namespace_declarations = ObjectDict() + self.path.append((name, attrs or None)) + if len(self.path) > self.item_depth: + self.stack.append((self.item, self.data)) + if self.xml_attribs: + attr_entries = [] + for key, value in attrs.items(): + key = self.attr_prefix + self._build_name(key) + if self.postprocessor: + entry = self.postprocessor(self.path, key, value) + else: + entry = (key, value) + if entry: + attr_entries.append(entry) + attrs = self.dict_constructor(attr_entries) + else: + attrs = None + self.item = attrs or None + self.data = [] + + def endElement(self, full_name): + name = self._build_name(full_name) + if len(self.path) == self.item_depth: + item = self.item + if item is None: + item = None if not self.data else self.cdata_separator.join(self.data) + + should_continue = self.item_callback(self.path, item) + if not should_continue: + raise ParsingInterrupted() + if len(self.stack): + data = None if not self.data else self.cdata_separator.join(self.data) + item = self.item + self.item, self.data = self.stack.pop() + if self.strip_whitespace and data: + data = data.strip() or None + if data and self.force_cdata and item is None: + item = self.dict_constructor() + if item is not None: + if data: + self.push_data(item, self.cdata_key, data) + self.item = self.push_data(self.item, name, item) + else: + self.item = self.push_data(self.item, name, data) + else: + self.item = None + self.data = [] + self.path.pop() + + def characters(self, data): + if not self.data: + self.data = [data] + else: + self.data.append(data) + + def comments(self, data): + if self.strip_whitespace: + data = data.strip() + self.item = self.push_data(self.item, self.comment_key, data) + + def push_data(self, item, key, data): + if self.postprocessor is not None: + result = self.postprocessor(self.path, key, data) + if result is None: + return item + key, data = result + if item is None: + item = self.dict_constructor() + try: + value = item[key] + if isinstance(value, list): + value.append(data) + else: + item[key] = [value, data] + except KeyError: + if self._should_force_list(key, data): + item[key] = [data] + else: + item[key] = data + return item + + def _should_force_list(self, key, value): + if not self.force_list: + return False + if isinstance(self.force_list, bool): + return self.force_list + try: + return key in self.force_list + except TypeError: + return self.force_list(self.path[:-1], key, value) + + +def parse( + xml_input, + encoding=None, + expat=expat, + process_namespaces=False, + namespace_separator=":", + disable_entities=True, + process_comments=False, + **kwargs +): + """Parse the given XML input and convert it into a dictionary. + + `xml_input` can either be a `string`, a file-like object, or a generator of strings. + + If `xml_attribs` is `True`, element attributes are put in the dictionary + among regular child elements, using `@` as a prefix to avoid collisions. If + set to `False`, they are just ignored. + + Simple example:: + + >>> import xmltodict + >>> doc = xmltodict.parse(\"\"\" + ... + ... 1 + ... 2 + ... + ... \"\"\") + >>> doc['a']['@prop'] + u'x' + >>> doc['a']['b'] + [u'1', u'2'] + + If `item_depth` is `0`, the function returns a dictionary for the root + element (default behavior). Otherwise, it calls `item_callback` every time + an item at the specified depth is found and returns `None` in the end + (streaming mode). + + The callback function receives two parameters: the `path` from the document + root to the item (name-attribs pairs), and the `item` (dict). If the + callback's return value is false-ish, parsing will be stopped with the + :class:`ParsingInterrupted` exception. + + Streaming example:: + + >>> def handle(path, item): + ... print('path:%s item:%s' % (path, item)) + ... return True + ... + >>> xmltodict.parse(\"\"\" + ... + ... 1 + ... 2 + ... \"\"\", item_depth=2, item_callback=handle) + path:[(u'a', {u'prop': u'x'}), (u'b', None)] item:1 + path:[(u'a', {u'prop': u'x'}), (u'b', None)] item:2 + + The optional argument `postprocessor` is a function that takes `path`, + `key` and `value` as positional arguments and returns a new `(key, value)` + pair where both `key` and `value` may have changed. Usage example:: + + >>> def postprocessor(path, key, value): + ... try: + ... return key + ':int', int(value) + ... except (ValueError, TypeError): + ... return key, value + >>> xmltodict.parse('12x', + ... postprocessor=postprocessor) + ObjectDict([(u'a', ObjectDict([(u'b:int', [1, 2]), (u'b', u'x')]))]) + + You can pass an alternate version of `expat` (such as `defusedexpat`) by + using the `expat` parameter. E.g: + + >>> import defusedexpat + >>> xmltodict.parse('hello', expat=defusedexpat.pyexpat) + ObjectDict([(u'a', u'hello')]) + + You can use the force_list argument to force lists to be created even + when there is only a single child of a given level of hierarchy. The + force_list argument is a tuple of keys. If the key for a given level + of hierarchy is in the force_list argument, that level of hierarchy + will have a list as a child (even if there is only one sub-element). + The index_keys operation takes precedence over this. This is applied + after any user-supplied postprocessor has already run. + + For example, given this input: + + + host1 + Linux + + + em0 + 10.0.0.1 + + + + + + If called with force_list=('interface',), it will produce + this dictionary: + {'servers': + {'server': + {'name': 'host1', + 'os': 'Linux'}, + 'interfaces': + {'interface': + [ {'name': 'em0', 'ip_address': '10.0.0.1' } ] } } } + + `force_list` can also be a callable that receives `path`, `key` and + `value`. This is helpful in cases where the logic that decides whether + a list should be forced is more complex. + + + If `process_comment` is `True` then comment will be added with comment_key + (default=`'#comment'`) to then tag which contains comment + + For example, given this input: + + + + + + 1 + + 2 + + + + If called with process_comment=True, it will produce + this dictionary: + 'a': { + 'b': { + '#comment': 'b comment', + 'c': { + + '#comment': 'c comment', + '#text': '1', + }, + 'd': '2', + }, + } + """ + handler = _DictSAXHandler(namespace_separator=namespace_separator, **kwargs) + if isinstance(xml_input, _unicode): + if not encoding: + encoding = "utf-8" + xml_input = xml_input.encode(encoding) + if not process_namespaces: + namespace_separator = None + parser = expat.ParserCreate(encoding, namespace_separator) + try: + parser.ordered_attributes = True + except AttributeError: + # Jython's expat does not support ordered_attributes + pass + parser.StartNamespaceDeclHandler = handler.startNamespaceDecl + parser.StartElementHandler = handler.startElement + parser.EndElementHandler = handler.endElement + parser.CharacterDataHandler = handler.characters + if process_comments: + parser.CommentHandler = handler.comments + parser.buffer_text = True + if disable_entities: + try: + # Attempt to disable DTD in Jython's expat parser (Xerces-J). + feature = "http://apache.org/xml/features/disallow-doctype-decl" + parser._reader.setFeature(feature, True) + except AttributeError: + # For CPython / expat parser. + # Anything not handled ends up here and entities aren't expanded. + parser.DefaultHandler = lambda x: None + # Expects an integer return; zero means failure -> expat.ExpatError. + parser.ExternalEntityRefHandler = lambda *x: 1 + if hasattr(xml_input, "read"): + parser.ParseFile(xml_input) + elif isgenerator(xml_input): + for chunk in xml_input: + parser.Parse(chunk, False) + parser.Parse(b"", True) + else: + parser.Parse(xml_input, True) + return handler.item + + +def _process_namespace(name, namespaces, ns_sep=":", attr_prefix="@"): + if not namespaces: + return name + try: + ns, name = name.rsplit(ns_sep, 1) + except ValueError: + pass + else: + ns_res = namespaces.get(ns.strip(attr_prefix)) + name = ( + "{}{}{}{}".format( + attr_prefix if ns.startswith(attr_prefix) else "", ns_res, ns_sep, name + ) + if ns_res + else name + ) + return name + + +def _emit( + key, + value, + content_handler, + attr_prefix="@", + cdata_key="#text", + depth=0, + preprocessor=None, + pretty=False, + newl="\n", + indent="\t", + namespace_separator=":", + namespaces=None, + full_document=True, + expand_iter=None, +): + key = _process_namespace(key, namespaces, namespace_separator, attr_prefix) + if preprocessor is not None: + result = preprocessor(key, value) + if result is None: + return + key, value = result + if ( + not hasattr(value, "__iter__") + or isinstance(value, _basestring) + or isinstance(value, dict) + ): + value = [value] + for index, v in enumerate(value): + if full_document and depth == 0 and index > 0: + raise ValueError("document with multiple roots") + if v is None: + v = ObjectDict() + elif isinstance(v, bool): + if v: + v = _unicode("true") + else: + v = _unicode("false") + elif not isinstance(v, dict): + if ( + expand_iter + and hasattr(v, "__iter__") + and not isinstance(v, _basestring) + ): + v = ObjectDict(((expand_iter, v),)) + else: + v = _unicode(v) + if isinstance(v, _basestring): + v = ObjectDict(((cdata_key, v),)) + cdata = None + attrs = ObjectDict() + children = [] + for ik, iv in v.items(): + if ik == cdata_key: + cdata = iv + continue + if ik.startswith(attr_prefix): + ik = _process_namespace( + ik, namespaces, namespace_separator, attr_prefix + ) + if ik == "@xmlns" and isinstance(iv, dict): + for k, v in iv.items(): + attr = "xmlns{}".format(":{}".format(k) if k else "") + attrs[attr] = _unicode(v) + continue + if not isinstance(iv, _unicode): + iv = _unicode(iv) + attrs[ik[len(attr_prefix) :]] = iv + continue + children.append((ik, iv)) + if pretty: + content_handler.ignorableWhitespace(depth * indent) + content_handler.startElement(key, AttributesImpl(attrs)) + if pretty and children: + content_handler.ignorableWhitespace(newl) + for child_key, child_value in children: + _emit( + child_key, + child_value, + content_handler, + attr_prefix, + cdata_key, + depth + 1, + preprocessor, + pretty, + newl, + indent, + namespaces=namespaces, + namespace_separator=namespace_separator, + expand_iter=expand_iter, + ) + if cdata is not None: + content_handler.characters(cdata) + if pretty and children: + content_handler.ignorableWhitespace(depth * indent) + content_handler.endElement(key) + if pretty and depth: + content_handler.ignorableWhitespace(newl) + + +def unparse( + input_dict, + output=None, + encoding="utf-8", + full_document=True, + short_empty_elements=False, + **kwargs +): + """Emit an XML document for the given `input_dict` (reverse of `parse`). + + The resulting XML document is returned as a string, but if `output` (a + file-like object) is specified, it is written there instead. + + Dictionary keys prefixed with `attr_prefix` (default=`'@'`) are interpreted + as XML node attributes, whereas keys equal to `cdata_key` + (default=`'#text'`) are treated as character data. + + The `pretty` parameter (default=`False`) enables pretty-printing. In this + mode, lines are terminated with `'\n'` and indented with `'\t'`, but this + can be customized with the `newl` and `indent` parameters. + + """ + if full_document and len(input_dict) != 1: + raise ValueError("Document must have exactly one root.") + must_return = False + if output is None: + output = StringIO() + must_return = True + if short_empty_elements: + content_handler = XMLGenerator(output, encoding, True) + else: + content_handler = XMLGenerator(output, encoding) + if full_document: + content_handler.startDocument() + for key, value in input_dict.items(): + _emit(key, value, content_handler, full_document=full_document, **kwargs) + if full_document: + content_handler.endDocument() + if must_return: + value = output.getvalue() + try: # pragma no cover + value = value.decode(encoding) + except AttributeError: # pragma no cover + pass + return value + + +if __name__ == "__main__": # pragma: no cover + import sys + import marshal + + try: + stdin = sys.stdin.buffer + stdout = sys.stdout.buffer + except AttributeError: + stdin = sys.stdin + stdout = sys.stdout + + (item_depth,) = sys.argv[1:] + item_depth = int(item_depth) + + def handle_item(path, item): + marshal.dump((path, item), stdout) + return True + + try: + root = parse( + stdin, + item_depth=item_depth, + item_callback=handle_item, + dict_constructor=dict, + ) + if item_depth == 0: + handle_item([], root) + except KeyboardInterrupt: + pass diff --git a/docker/Dockerfile b/docker/Dockerfile index 4d9b5fa8..ef89d7ce 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -25,9 +25,6 @@ RUN wget --progress=dot:giga "https://developer.arm.com/-/media/Files/downloads/ for file in * ; do ln -s "${PWD}/${file}" "/usr/bin/${file}" ; done && \ cd / && arm-none-eabi-gcc -v && arm-none-eabi-gdb -v -RUN wget --progress=dot:giga -O - https://bootstrap.pypa.io/pip/2.7/get-pip.py | python2 && \ - pip install --no-cache-dir lxml==4.6.3 - RUN git clone --depth 1 --branch v0.4.1 https://github.com/atomicobject/heatshrink.git && \ cd heatshrink && make && mv ./heatshrink /usr/local/bin/heatshrink @@ -35,4 +32,4 @@ COPY entrypoint.sh syntax_check.sh / RUN chmod +x /syntax_check.sh -ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file +ENTRYPOINT ["/entrypoint.sh"] From 93871f942552597846b582ab277ba0e28856a52d Mon Sep 17 00:00:00 2001 From: gornekich Date: Fri, 17 Dec 2021 16:24:37 +0300 Subject: [PATCH 4/4] BT hid navigation fix (#911) * bt: fix bt hid navigation * Cli: change datetime format to more ISO-ish, add datetime validation. Co-authored-by: Aleksandr Kutuzov --- applications/bt/bt_hid_app/bt_hid.c | 9 +-- applications/bt/bt_hid_app/bt_hid.h | 2 - applications/bt/bt_service/bt.c | 1 + applications/cli/cli_commands.c | 71 +++++++++++-------- firmware/targets/f6/furi-hal/furi-hal-bt.c | 1 + firmware/targets/f6/furi-hal/furi-hal-rtc.c | 22 ++++++ firmware/targets/f7/furi-hal/furi-hal-bt.c | 1 + firmware/targets/f7/furi-hal/furi-hal-rtc.c | 22 ++++++ .../targets/furi-hal-include/furi-hal-rtc.h | 2 + 9 files changed, 92 insertions(+), 39 deletions(-) diff --git a/applications/bt/bt_hid_app/bt_hid.c b/applications/bt/bt_hid_app/bt_hid.c index 6a93ce85..020a26b8 100755 --- a/applications/bt/bt_hid_app/bt_hid.c +++ b/applications/bt/bt_hid_app/bt_hid.c @@ -28,7 +28,7 @@ void bt_hid_dialog_callback(DialogExResult result, void* context) { // TODO switch to Submenu after Media is done view_dispatcher_stop(app->view_dispatcher); } else if(result == DialogExResultRight) { - view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); + view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewKeynote); } } @@ -56,9 +56,6 @@ void bt_hid_connection_status_changed_callback(BtStatus status, void* context) { BtHid* bt_hid_app_alloc() { BtHid* app = furi_alloc(sizeof(BtHid)); - // Load Bluetooth settings - bt_settings_load(&app->bt_settings); - // Gui app->gui = furi_record_open("gui"); @@ -156,10 +153,6 @@ int32_t bt_hid_app(void* p) { view_dispatcher_run(app->view_dispatcher); bt_set_status_changed_callback(app->bt, NULL, NULL); - // Stop advertising if bt was off - if(app->bt_settings.enabled) { - furi_hal_bt_stop_advertising(); - } // Change back profile to Serial bt_set_profile(app->bt, BtProfileSerial); diff --git a/applications/bt/bt_hid_app/bt_hid.h b/applications/bt/bt_hid_app/bt_hid.h index 4156b44b..875cac58 100644 --- a/applications/bt/bt_hid_app/bt_hid.h +++ b/applications/bt/bt_hid_app/bt_hid.h @@ -6,7 +6,6 @@ #include #include #include -#include #include #include @@ -14,7 +13,6 @@ #include "views/bt_hid_media.h" typedef struct { - BtSettings bt_settings; Bt* bt; Gui* gui; NotificationApp* notifications; diff --git a/applications/bt/bt_service/bt.c b/applications/bt/bt_service/bt.c index 4dfcdfb7..fdd43ec4 100755 --- a/applications/bt/bt_service/bt.c +++ b/applications/bt/bt_service/bt.c @@ -235,6 +235,7 @@ static void bt_statusbar_update(Bt* bt) { } static void bt_change_profile(Bt* bt, BtMessage* message) { + bt_settings_load(&bt->bt_settings); if(bt->profile == BtProfileSerial && bt->rpc_session) { FURI_LOG_I(TAG, "Close RPC connection"); osEventFlagsSet(bt->rpc_event, BT_RPC_EVENT_DISCONNECTED); diff --git a/applications/cli/cli_commands.c b/applications/cli/cli_commands.c index 7f194220..c7827d2f 100644 --- a/applications/cli/cli_commands.c +++ b/applications/cli/cli_commands.c @@ -6,6 +6,9 @@ #include #include +// Close to ISO, `date +'%Y-%m-%d %H:%M:%S %u'` +#define CLI_DATE_FORMAT "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %d" + void cli_command_device_info_callback(const char* key, const char* value, bool last, void* context) { printf("%-24s: %s\r\n", key, value); } @@ -63,51 +66,61 @@ void cli_command_date(Cli* cli, string_t args, void* context) { uint16_t hours, minutes, seconds, month, day, year, weekday; int ret = sscanf( string_get_cstr(args), - "%hu:%hu:%hu %hu-%hu-%hu %hu", + "%hu-%hu-%hu %hu:%hu:%hu %hu", + &year, + &month, + &day, &hours, &minutes, &seconds, - &month, - &day, - &year, &weekday); - if(ret == 7) { - datetime.hour = hours; - datetime.minute = minutes; - datetime.second = seconds; - datetime.weekday = weekday; - datetime.month = month; - datetime.day = day; - datetime.year = year; - furi_hal_rtc_set_datetime(&datetime); - // Verification - furi_hal_rtc_get_datetime(&datetime); + + // Some variables are going to discard upper byte + // There will be some funky behaviour which is not breaking anything + datetime.hour = hours; + datetime.minute = minutes; + datetime.second = seconds; + datetime.weekday = weekday; + datetime.month = month; + datetime.day = day; + datetime.year = year; + + if(ret != 7) { printf( - "New time is: %.2d:%.2d:%.2d %.2d-%.2d-%.2d %d", - datetime.hour, - datetime.minute, - datetime.second, - datetime.month, - datetime.day, - datetime.year, - datetime.weekday); - } else { - printf( - "Invalid time format, use `hh:mm:ss MM-DD-YYYY WD`. sscanf %d %s", + "Invalid datetime format, use `%s`. sscanf %d %s", + "%Y-%m-%d %H:%M:%S %u", ret, string_get_cstr(args)); return; } - } else { + + if(!furi_hal_rtc_validate_datetime(&datetime)) { + printf("Invalid datetime data"); + return; + } + + furi_hal_rtc_set_datetime(&datetime); + // Verification furi_hal_rtc_get_datetime(&datetime); printf( - "%.2d:%.2d:%.2d %.2d-%.2d-%.2d %d", + "New datetime is: " CLI_DATE_FORMAT, + datetime.year, + datetime.month, + datetime.day, datetime.hour, datetime.minute, datetime.second, + datetime.weekday); + } else { + furi_hal_rtc_get_datetime(&datetime); + printf( + CLI_DATE_FORMAT, + datetime.year, datetime.month, datetime.day, - datetime.year, + datetime.hour, + datetime.minute, + datetime.second, datetime.weekday); } } diff --git a/firmware/targets/f6/furi-hal/furi-hal-bt.c b/firmware/targets/f6/furi-hal/furi-hal-bt.c index bbd29754..5a457279 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-bt.c +++ b/firmware/targets/f6/furi-hal/furi-hal-bt.c @@ -160,6 +160,7 @@ bool furi_hal_bt_change_app(FuriHalBtProfile profile, BleEventCallback event_cb, gap_thread_stop(); FURI_LOG_I(TAG, "Reset SHCI"); SHCI_C2_Reinit(); + osDelay(100); ble_glue_thread_stop(); FURI_LOG_I(TAG, "Start BT initialization"); furi_hal_bt_init(); diff --git a/firmware/targets/f6/furi-hal/furi-hal-rtc.c b/firmware/targets/f6/furi-hal/furi-hal-rtc.c index 13bb17cb..20e77e13 100644 --- a/firmware/targets/f6/furi-hal/furi-hal-rtc.c +++ b/firmware/targets/f6/furi-hal/furi-hal-rtc.c @@ -120,3 +120,25 @@ void furi_hal_rtc_get_datetime(FuriHalRtcDateTime* datetime) { datetime->day = __LL_RTC_CONVERT_BCD2BIN((date >> 16) & 0xFF); datetime->weekday = __LL_RTC_CONVERT_BCD2BIN((date >> 24) & 0xFF); } + +bool furi_hal_rtc_validate_datetime(FuriHalRtcDateTime* datetime) { + bool invalid = false; + + invalid |= (datetime->second > 59); + invalid |= (datetime->minute > 59); + invalid |= (datetime->hour > 23); + + invalid |= (datetime->year < 2000); + invalid |= (datetime->year > 2099); + + invalid |= (datetime->month == 0); + invalid |= (datetime->month > 12); + + invalid |= (datetime->day == 0); + invalid |= (datetime->day > 31); + + invalid |= (datetime->weekday == 0); + invalid |= (datetime->weekday > 7); + + return !invalid; +} diff --git a/firmware/targets/f7/furi-hal/furi-hal-bt.c b/firmware/targets/f7/furi-hal/furi-hal-bt.c index bbd29754..5a457279 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-bt.c +++ b/firmware/targets/f7/furi-hal/furi-hal-bt.c @@ -160,6 +160,7 @@ bool furi_hal_bt_change_app(FuriHalBtProfile profile, BleEventCallback event_cb, gap_thread_stop(); FURI_LOG_I(TAG, "Reset SHCI"); SHCI_C2_Reinit(); + osDelay(100); ble_glue_thread_stop(); FURI_LOG_I(TAG, "Start BT initialization"); furi_hal_bt_init(); diff --git a/firmware/targets/f7/furi-hal/furi-hal-rtc.c b/firmware/targets/f7/furi-hal/furi-hal-rtc.c index 13bb17cb..20e77e13 100644 --- a/firmware/targets/f7/furi-hal/furi-hal-rtc.c +++ b/firmware/targets/f7/furi-hal/furi-hal-rtc.c @@ -120,3 +120,25 @@ void furi_hal_rtc_get_datetime(FuriHalRtcDateTime* datetime) { datetime->day = __LL_RTC_CONVERT_BCD2BIN((date >> 16) & 0xFF); datetime->weekday = __LL_RTC_CONVERT_BCD2BIN((date >> 24) & 0xFF); } + +bool furi_hal_rtc_validate_datetime(FuriHalRtcDateTime* datetime) { + bool invalid = false; + + invalid |= (datetime->second > 59); + invalid |= (datetime->minute > 59); + invalid |= (datetime->hour > 23); + + invalid |= (datetime->year < 2000); + invalid |= (datetime->year > 2099); + + invalid |= (datetime->month == 0); + invalid |= (datetime->month > 12); + + invalid |= (datetime->day == 0); + invalid |= (datetime->day > 31); + + invalid |= (datetime->weekday == 0); + invalid |= (datetime->weekday > 7); + + return !invalid; +} diff --git a/firmware/targets/furi-hal-include/furi-hal-rtc.h b/firmware/targets/furi-hal-include/furi-hal-rtc.h index 7c36aa09..cc2f56f5 100644 --- a/firmware/targets/furi-hal-include/furi-hal-rtc.h +++ b/firmware/targets/furi-hal-include/furi-hal-rtc.h @@ -47,6 +47,8 @@ void furi_hal_rtc_set_datetime(FuriHalRtcDateTime* datetime); void furi_hal_rtc_get_datetime(FuriHalRtcDateTime* datetime); +bool furi_hal_rtc_validate_datetime(FuriHalRtcDateTime* datetime); + #ifdef __cplusplus } #endif