From 2a30d93089392b7ac6f3b37813dda59da7591dee Mon Sep 17 00:00:00 2001 From: Laurens Valk Date: Tue, 15 Oct 2024 11:34:20 +0200 Subject: [PATCH] pbio/sys/main: Expose how program was started. This gives the user a way to optionally run certain code only when it is first downloaded to the robot. Examples include one-off motor initialization or one-off wiping of user data. Fixes https://github.com/pybricks/support/issues/1496 --- CHANGELOG.md | 3 ++- lib/pbio/include/pbsys/main.h | 38 ++++++++++++++++++++++++++++---- lib/pbio/sys/command.c | 4 ++-- lib/pbio/sys/hmi.c | 4 ++-- lib/pbio/sys/main.c | 33 ++++++++++++++++++++++----- pybricks/common/pb_type_system.c | 2 ++ 6 files changed, 69 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6793282d0..b3a704eb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,8 @@ coding ([support#1661]). - Added `observe_enable` to the hub `BLE` class to selectively turn observing on and off, just like you can with broadcasting ([support#1806]). -- Added `hub.system.info()` method with hub status flags ([support#1496]). +- Added `hub.system.info()` method with hub status flags ([support#1496]) and + value representing how the program was started. ### Changed diff --git a/lib/pbio/include/pbsys/main.h b/lib/pbio/include/pbsys/main.h index 0ce129bd2..9acefc643 100644 --- a/lib/pbio/include/pbsys/main.h +++ b/lib/pbio/include/pbsys/main.h @@ -16,6 +16,30 @@ #include #include +/** + * Whether and how the program was requested to start. + * + * Values are returned to the user, so the numeric values must not change. + */ +typedef enum { + /** + * The program was not requested to start. + */ + PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_NONE = 0, + /** + * The program was requested to start automatically on boot. + */ + PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_BOOT = 1, + /** + * The program was requested to start using the hub button/display UI. + */ + PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_HUB_UI = 2, + /** + * The program was requested to start remotely, such as with an IDE. + */ + PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_REMOTE = 3, +} pbsys_main_program_start_request_type_t; + /** * Main application program data information. */ @@ -41,14 +65,16 @@ typedef struct _pbsys_main_program_t { */ pbio_pybricks_user_program_id_t id; /** - * Whether a request was made to start the program. + * Whether a request was made to start the program, and how. */ - bool start_requested; + pbsys_main_program_start_request_type_t start_request_type; } pbsys_main_program_t; #if PBSYS_CONFIG_MAIN -pbio_error_t pbsys_main_program_request_start(pbio_pybricks_user_program_id_t id); +pbsys_main_program_start_request_type_t pbsys_main_program_get_start_request_type(void); + +pbio_error_t pbsys_main_program_request_start(pbio_pybricks_user_program_id_t id, pbsys_main_program_start_request_type_t start_request_type); /** * Validates the program that is being requested to start. @@ -99,7 +125,11 @@ const char *pbsys_main_get_application_version_hash(void); #else // PBSYS_CONFIG_MAIN -static inline pbio_error_t pbsys_main_program_request_start(pbio_pybricks_user_program_id_t id) { +static inline pbsys_main_program_start_request_type_t pbsys_main_program_get_start_request_type(void) { + return PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_NONE; +} + +static inline pbio_error_t pbsys_main_program_request_start(pbio_pybricks_user_program_id_t id, pbsys_main_program_start_request_type_t start_request_type) { return PBIO_ERROR_NOT_SUPPORTED; } diff --git a/lib/pbio/sys/command.c b/lib/pbio/sys/command.c index a670dcb17..5c3b39d7f 100644 --- a/lib/pbio/sys/command.c +++ b/lib/pbio/sys/command.c @@ -49,14 +49,14 @@ pbio_pybricks_error_t pbsys_command(const uint8_t *data, uint32_t size) { } // Use payload as program ID, otherwise use active user slot. return pbio_pybricks_error_from_pbio_error( - pbsys_main_program_request_start((size == 2 ? data[1] : pbsys_hmi_get_selected_program_slot()))); + pbsys_main_program_request_start((size == 2 ? data[1] : pbsys_hmi_get_selected_program_slot()), PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_REMOTE)); } #if PBSYS_CONFIG_FEATURE_BUILTIN_USER_PROGRAM_REPL case PBIO_PYBRICKS_COMMAND_START_REPL: // Deprecated. For backwards compatibility with Pybricks // Profile < v1.4.0, make it work anyway. return pbio_pybricks_error_from_pbio_error( - pbsys_main_program_request_start(PBIO_PYBRICKS_USER_PROGRAM_ID_REPL)); + pbsys_main_program_request_start(PBIO_PYBRICKS_USER_PROGRAM_ID_REPL, PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_REMOTE)); #endif // PBSYS_CONFIG_FEATURE_BUILTIN_USER_PROGRAM_REPL case PBIO_PYBRICKS_COMMAND_WRITE_USER_PROGRAM_META: diff --git a/lib/pbio/sys/hmi.c b/lib/pbio/sys/hmi.c index fb32a52da..956bd38a8 100644 --- a/lib/pbio/sys/hmi.c +++ b/lib/pbio/sys/hmi.c @@ -56,7 +56,7 @@ static PT_THREAD(update_program_run_button_wait_state(bool button_pressed)) { // If we made it through a full press and release, without the user // program running, then start the currently selected user program. - pbsys_main_program_request_start(selected_slot); + pbsys_main_program_request_start(selected_slot, PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_HUB_UI); } PT_END(pt); @@ -152,7 +152,7 @@ static PT_THREAD(update_left_right_button_wait_state(bool left_button_pressed, b if (left_button_pressed && pbdrv_clock_get_ms() - first_press_time < 100) { selected_slot = previous_slot; pbsys_hub_light_matrix_update_program_slot(); - pbsys_main_program_request_start(PBIO_PYBRICKS_USER_PROGRAM_ID_PORT_VIEW); + pbsys_main_program_request_start(PBIO_PYBRICKS_USER_PROGRAM_ID_PORT_VIEW, PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_HUB_UI); } else { // Successful switch. And UI was already updated. previous_slot = selected_slot; diff --git a/lib/pbio/sys/main.c b/lib/pbio/sys/main.c index 09283e87b..77e9647a0 100644 --- a/lib/pbio/sys/main.c +++ b/lib/pbio/sys/main.c @@ -20,8 +20,29 @@ #include #include +// Singleton with information about the currently (or soon) active program. static pbsys_main_program_t program; +/** + * Checks if a start request has been made for the main program. + * + * @param [in] program A pointer to the main program structure. + * @returns true if a start request has been made, false otherwise. + */ +static bool pbsys_main_program_start_requested() { + return program.start_request_type != PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_NONE; +} + +/** + * Gets the type of start request for the main program. + * + * @param [in] program A pointer to the main program structure. + * @returns The type of start request. + */ +pbsys_main_program_start_request_type_t pbsys_main_program_get_start_request_type(void) { + return program.start_request_type; +} + /** * Requests to start the main user application program. * @@ -31,10 +52,10 @@ static pbsys_main_program_t program; * ::PBIO_ERROR_NOT_SUPPORTED if the program is not available. * Otherwise ::PBIO_SUCCESS. */ -pbio_error_t pbsys_main_program_request_start(pbio_pybricks_user_program_id_t id) { +pbio_error_t pbsys_main_program_request_start(pbio_pybricks_user_program_id_t id, pbsys_main_program_start_request_type_t start_request_type) { // Can't start new program if already running or new requested. - if (pbsys_status_test(PBIO_PYBRICKS_STATUS_USER_PROGRAM_RUNNING) || program.start_requested) { + if (pbsys_status_test(PBIO_PYBRICKS_STATUS_USER_PROGRAM_RUNNING) || pbsys_main_program_start_requested()) { return PBIO_ERROR_BUSY; } @@ -49,7 +70,7 @@ pbio_error_t pbsys_main_program_request_start(pbio_pybricks_user_program_id_t id return err; } - program.start_requested = true; + program.start_request_type = start_request_type; return PBIO_SUCCESS; } @@ -68,7 +89,7 @@ int main(int argc, char **argv) { while (!pbsys_status_test(PBIO_PYBRICKS_STATUS_SHUTDOWN_REQUEST)) { #if PBSYS_CONFIG_USER_PROGRAM_AUTO_START - pbsys_main_program_request_start(PBIO_PYBRICKS_USER_PROGRAM_ID_REPL); + pbsys_main_program_request_start(PBIO_PYBRICKS_USER_PROGRAM_ID_REPL, PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_BOOT); #endif // REVISIT: this can be long waiting, so we could do a more efficient @@ -76,7 +97,7 @@ int main(int argc, char **argv) { while (pbio_do_one_event()) { } - if (!program.start_requested) { + if (!pbsys_main_program_start_requested()) { continue; } @@ -98,7 +119,7 @@ int main(int argc, char **argv) { pbsys_bluetooth_rx_set_callback(NULL); pbsys_program_stop_set_buttons(PBIO_BUTTON_CENTER); pbio_stop_all(true); - program.start_requested = false; + program.start_request_type = PBSYS_MAIN_PROGRAM_START_REQUEST_TYPE_NONE; } // Stop system processes and save user data before we shutdown. diff --git a/pybricks/common/pb_type_system.c b/pybricks/common/pb_type_system.c index 065f39543..e0536298b 100644 --- a/pybricks/common/pb_type_system.c +++ b/pybricks/common/pb_type_system.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -48,6 +49,7 @@ static mp_obj_t pb_type_System_info(void) { {MP_OBJ_NEW_QSTR(MP_QSTR_reset_reason), mp_obj_new_int(pbdrv_reset_get_reason())}, #endif // PBDRV_CONFIG_RESET {MP_OBJ_NEW_QSTR(MP_QSTR_host_connected_ble), mp_obj_new_bool(pbsys_status_test(PBIO_PYBRICKS_STATUS_BLE_HOST_CONNECTED))}, + {MP_OBJ_NEW_QSTR(MP_QSTR_program_start_type), mp_obj_new_int(pbsys_main_program_get_start_request_type())}, }; mp_obj_t info_dict = mp_obj_new_dict(MP_ARRAY_SIZE(info));