From a5499893322761d096179869f2e59cbee6caf226 Mon Sep 17 00:00:00 2001 From: admin Date: Sat, 28 Feb 2026 14:21:17 +0800 Subject: [PATCH] refactor(architecture): enforce component layering constraints - move runtime policy/diag contracts to domain-owned facade and remove control_plane leapfrog usage - enforce single declaration point for direct debug config in domain public API (constraint #6) - harden timeout/idempotent semantics in REST/voice/printer/wifi/ble paths --- .../control_plane/src/controller_lifecycle.c | 36 ++-- .../control_plane/src/rest_server_common.c | 6 +- .../control_plane/src/rest_server_ops.c | 201 +++++------------- .../src/rest_server_print_image.c | 20 +- .../src/rest_server_print_render.c | 49 +++-- .../control_plane/src/rest_server_voice.c | 73 +++++-- components/domain/CMakeLists.txt | 3 +- components/domain/include/domain.h | 78 ++++++- .../internal/printer_protocol_internal.h | 5 +- .../internal/voice_interaction_internal.h | 1 + .../domain/src/domain_runtime_contracts.c | 201 ++++++++++++++++++ components/domain/src/image_generation.c | 1 + components/domain/src/printer_protocol.c | 32 ++- .../domain/src/printer_protocol_commands.c | 58 ++--- .../domain/src/printer_protocol_worker.c | 16 +- components/domain/src/screen_preview.c | 1 + components/domain/src/system_runtime.c | 1 + components/domain/src/voice_interaction.c | 50 ++++- components/platform/include/platform.h | 37 ++-- components/platform/src/ble_printer_client.c | 53 +++-- .../platform/src/direct_thermal_printer.c | 113 +++++++--- components/platform/src/display_lcd_module.c | 4 +- components/platform/src/wifi_manager.c | 10 + 23 files changed, 718 insertions(+), 331 deletions(-) create mode 100644 components/domain/src/domain_runtime_contracts.c diff --git a/components/control_plane/src/controller_lifecycle.c b/components/control_plane/src/controller_lifecycle.c index 1905d4b..25bf147 100644 --- a/components/control_plane/src/controller_lifecycle.c +++ b/components/control_plane/src/controller_lifecycle.c @@ -37,7 +37,7 @@ static void lifecycle_set_state_locked(controller_lifecycle_state_t state, s_status.last_error = last_error; s_status.last_transition_ms = esp_timer_get_time() / 1000; strlcpy(s_status.last_stage, stage != NULL ? stage : "unknown", sizeof(s_status.last_stage)); - runtime_diag_set_gauge(RUNTIME_DIAG_GAUGE_LIFECYCLE_STATE, (int32_t)state); + domain_diag_set_gauge(DOMAIN_DIAG_GAUGE_LIFECYCLE_STATE, (int32_t)state); } const char *controller_lifecycle_state_str(controller_lifecycle_state_t state) { @@ -87,7 +87,7 @@ static esp_err_t lifecycle_stop_voice(void) { } static esp_err_t lifecycle_stop_printer_protocol(void) { - esp_err_t rc = printer_protocol_stop(runtime_policy_printer_stop_timeout_ms()); + esp_err_t rc = printer_protocol_stop(domain_policy_printer_stop_timeout_ms()); if (rc == ESP_OK || rc == ESP_ERR_INVALID_STATE) { return ESP_OK; } @@ -101,7 +101,7 @@ static esp_err_t lifecycle_stop_system_runtime(void) { typedef esp_err_t (*lifecycle_step_fn_t)(void); static esp_err_t lifecycle_run_step_with_retry(const char *stage, lifecycle_step_fn_t fn) { - uint32_t retry_count = runtime_policy_lifecycle_start_retry_count(); + uint32_t retry_count = domain_policy_lifecycle_start_retry_count(); uint32_t max_attempts = retry_count + 1; for (uint32_t attempt = 0; attempt < max_attempts; ++attempt) { @@ -113,7 +113,7 @@ static esp_err_t lifecycle_run_step_with_retry(const char *stage, lifecycle_step return ESP_OK; } - bool can_retry = (attempt + 1 < max_attempts) && runtime_policy_is_retryable_error(rc); + bool can_retry = (attempt + 1 < max_attempts) && domain_policy_is_retryable_error(rc); ESP_LOGW(TAG, "stage %s failed, rc=0x%x, attempt=%u/%u, retry=%d", stage, @@ -126,19 +126,19 @@ static esp_err_t lifecycle_run_step_with_retry(const char *stage, lifecycle_step return rc; } - runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_LIFECYCLE_START_RETRY, 1); - vTaskDelay(pdMS_TO_TICKS(runtime_policy_lifecycle_retry_backoff_ms(attempt))); + domain_diag_counter_add(DOMAIN_DIAG_COUNTER_LIFECYCLE_START_RETRY, 1); + vTaskDelay(pdMS_TO_TICKS(domain_policy_lifecycle_retry_backoff_ms(attempt))); } return ESP_FAIL; } esp_err_t controller_lifecycle_start(void) { - runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_LIFECYCLE_START_ATTEMPT, 1); + domain_diag_counter_add(DOMAIN_DIAG_COUNTER_LIFECYCLE_START_ATTEMPT, 1); if (!lifecycle_lock_take(1000)) { - runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_LIFECYCLE_START_FAILED, 1); - runtime_diag_record_error("lifecycle_start", ESP_ERR_TIMEOUT, "lifecycle lock timeout"); + domain_diag_counter_add(DOMAIN_DIAG_COUNTER_LIFECYCLE_START_FAILED, 1); + domain_diag_record_error("lifecycle_start", ESP_ERR_TIMEOUT, "lifecycle lock timeout"); return ESP_ERR_TIMEOUT; } @@ -197,7 +197,7 @@ esp_err_t controller_lifecycle_start(void) { xSemaphoreGive(s_lock); } - runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_LIFECYCLE_START_SUCCESS, 1); + domain_diag_counter_add(DOMAIN_DIAG_COUNTER_LIFECYCLE_START_SUCCESS, 1); ESP_LOGI(TAG, "controller lifecycle started"); return ESP_OK; @@ -220,8 +220,8 @@ start_failed: xSemaphoreGive(s_lock); } - runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_LIFECYCLE_START_FAILED, 1); - runtime_diag_record_error("lifecycle_start", rc, failed_stage); + domain_diag_counter_add(DOMAIN_DIAG_COUNTER_LIFECYCLE_START_FAILED, 1); + domain_diag_record_error("lifecycle_start", rc, failed_stage); ESP_LOGE(TAG, "controller lifecycle start failed at stage=%s, rc=0x%x", failed_stage, @@ -230,11 +230,11 @@ start_failed: } esp_err_t controller_lifecycle_stop(void) { - runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_LIFECYCLE_STOP_ATTEMPT, 1); + domain_diag_counter_add(DOMAIN_DIAG_COUNTER_LIFECYCLE_STOP_ATTEMPT, 1); if (!lifecycle_lock_take(1000)) { - runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_LIFECYCLE_STOP_FAILED, 1); - runtime_diag_record_error("lifecycle_stop", ESP_ERR_TIMEOUT, "lifecycle lock timeout"); + domain_diag_counter_add(DOMAIN_DIAG_COUNTER_LIFECYCLE_STOP_FAILED, 1); + domain_diag_record_error("lifecycle_stop", ESP_ERR_TIMEOUT, "lifecycle lock timeout"); return ESP_ERR_TIMEOUT; } @@ -283,11 +283,11 @@ esp_err_t controller_lifecycle_stop(void) { } if (first_err == ESP_OK) { - runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_LIFECYCLE_STOP_SUCCESS, 1); + domain_diag_counter_add(DOMAIN_DIAG_COUNTER_LIFECYCLE_STOP_SUCCESS, 1); ESP_LOGI(TAG, "controller lifecycle stopped"); } else { - runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_LIFECYCLE_STOP_FAILED, 1); - runtime_diag_record_error("lifecycle_stop", first_err, "controller stop failed"); + domain_diag_counter_add(DOMAIN_DIAG_COUNTER_LIFECYCLE_STOP_FAILED, 1); + domain_diag_record_error("lifecycle_stop", first_err, "controller stop failed"); ESP_LOGE(TAG, "controller lifecycle stop failed, rc=0x%x", (unsigned)first_err); } diff --git a/components/control_plane/src/rest_server_common.c b/components/control_plane/src/rest_server_common.c index a1bbc41..05953e5 100644 --- a/components/control_plane/src/rest_server_common.c +++ b/components/control_plane/src/rest_server_common.c @@ -50,7 +50,7 @@ esp_err_t rest_server_send_json(httpd_req_t *req, const char *status, cJSON *roo httpd_resp_set_status(req, status); esp_err_t err = httpd_resp_sendstr(req, text); cJSON_free(text); - runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_REST_RESPONSES_TOTAL, 1); + domain_diag_counter_add(DOMAIN_DIAG_COUNTER_REST_RESPONSES_TOTAL, 1); return err; } @@ -60,8 +60,8 @@ esp_err_t rest_server_send_error(httpd_req_t *req, const char *status, const cha cJSON_AddStringToObject(root, "error", message != NULL ? message : "unknown"); esp_err_t err = rest_server_send_json(req, status, root); cJSON_Delete(root); - runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_REST_ERRORS_TOTAL, 1); - runtime_diag_record_error("rest_api", ESP_FAIL, message != NULL ? message : "unknown"); + domain_diag_counter_add(DOMAIN_DIAG_COUNTER_REST_ERRORS_TOTAL, 1); + domain_diag_record_error("rest_api", ESP_FAIL, message != NULL ? message : "unknown"); return err; } diff --git a/components/control_plane/src/rest_server_ops.c b/components/control_plane/src/rest_server_ops.c index 3993a36..7a2c773 100644 --- a/components/control_plane/src/rest_server_ops.c +++ b/components/control_plane/src/rest_server_ops.c @@ -39,8 +39,8 @@ static bool parse_printer_backend(const cJSON *json, printer_backend_t *out_back } static void fill_runtime_diag_json(cJSON *root) { - runtime_diag_snapshot_t snapshot = {0}; - runtime_diag_get_snapshot(&snapshot); + domain_diag_snapshot_t snapshot = {0}; + domain_diag_get_snapshot(&snapshot); cJSON *diag = cJSON_AddObjectToObject(root, "diagnostics"); if (diag == NULL) { @@ -49,18 +49,18 @@ static void fill_runtime_diag_json(cJSON *root) { cJSON *counters = cJSON_AddObjectToObject(diag, "counters"); if (counters != NULL) { - for (int i = 0; i < RUNTIME_DIAG_COUNTER_MAX; ++i) { + for (int i = 0; i < DOMAIN_DIAG_COUNTER_MAX; ++i) { cJSON_AddNumberToObject(counters, - runtime_diag_counter_name((runtime_diag_counter_t)i), + domain_diag_counter_name((domain_diag_counter_t)i), (double)snapshot.counters[i]); } } cJSON *gauges = cJSON_AddObjectToObject(diag, "gauges"); if (gauges != NULL) { - for (int i = 0; i < RUNTIME_DIAG_GAUGE_MAX; ++i) { + for (int i = 0; i < DOMAIN_DIAG_GAUGE_MAX; ++i) { cJSON_AddNumberToObject(gauges, - runtime_diag_gauge_name((runtime_diag_gauge_t)i), + domain_diag_gauge_name((domain_diag_gauge_t)i), snapshot.gauges[i]); } } @@ -139,7 +139,7 @@ esp_err_t rest_server_health_get(httpd_req_t *req) { cJSON *root = cJSON_CreateObject(); controller_lifecycle_status_t lifecycle = {0}; controller_lifecycle_get_status(&lifecycle); - bool healthy = (lifecycle.state == CONTROLLER_LIFECYCLE_STATE_RUNNING); + bool healthy = (lifecycle.state == CONTROLLER_LIFECYCLE_STATE_RUNNING) && system_runtime_wifi_ready(); cJSON_AddBoolToObject(root, "ok", healthy); fill_runtime_json(root); @@ -155,7 +155,7 @@ esp_err_t rest_server_connect_post(httpd_req_t *req) { char *body = NULL; char name[32] = "TQPrinter"; - uint32_t timeout_ms = runtime_policy_rest_printer_connect_timeout_ms(); + uint32_t timeout_ms = domain_policy_rest_printer_connect_timeout_ms(); printer_backend_t backend = printer_protocol_get_backend(); bool backend_specified = false; @@ -177,8 +177,16 @@ esp_err_t rest_server_connect_post(httpd_req_t *req) { } cJSON *jtimeout = cJSON_GetObjectItemCaseSensitive(json, "timeout_ms"); - if (cJSON_IsNumber(jtimeout) && jtimeout->valuedouble > 0) { - timeout_ms = (uint32_t)jtimeout->valuedouble; + if (cJSON_IsNumber(jtimeout)) { + uint32_t min_ms = domain_policy_rest_printer_connect_timeout_min_ms(); + uint32_t max_ms = domain_policy_rest_printer_connect_timeout_max_ms(); + if (jtimeout->valuedouble >= (double)min_ms && jtimeout->valuedouble <= (double)max_ms) { + timeout_ms = (uint32_t)jtimeout->valuedouble; + } else { + cJSON_Delete(json); + free(body); + return rest_server_send_error(req, "400 Bad Request", "timeout_ms out of range"); + } } cJSON *jbackend = cJSON_GetObjectItemCaseSensitive(json, "backend"); @@ -490,7 +498,7 @@ esp_err_t rest_server_label_gap_move_post(httpd_req_t *req) { return rest_server_send_error(req, "401 Unauthorized", "unauthorized"); } - uint32_t timeout_ms = runtime_policy_rest_label_timeout_ms(); + uint32_t timeout_ms = domain_policy_rest_label_timeout_ms(); if (req->content_len > 0) { char *body = NULL; esp_err_t body_err = rest_server_read_body(req, &body); @@ -503,7 +511,13 @@ esp_err_t rest_server_label_gap_move_post(httpd_req_t *req) { return rest_server_send_error(req, "400 Bad Request", "invalid json"); } cJSON *jtimeout = cJSON_GetObjectItemCaseSensitive(json, "timeout_ms"); - if (cJSON_IsNumber(jtimeout) && jtimeout->valuedouble > 0 && jtimeout->valuedouble <= 30000) { + if (jtimeout != NULL) { + if (!cJSON_IsNumber(jtimeout) || + jtimeout->valuedouble < (double)domain_policy_rest_label_timeout_min_ms() || + jtimeout->valuedouble > (double)domain_policy_rest_label_timeout_max_ms()) { + cJSON_Delete(json); + return rest_server_send_error(req, "400 Bad Request", "timeout_ms out of range"); + } timeout_ms = (uint32_t)jtimeout->valuedouble; } cJSON_Delete(json); @@ -532,7 +546,7 @@ esp_err_t rest_server_label_offset_get(httpd_req_t *req) { uint8_t offset = 0; char cmd_err[128] = {0}; esp_err_t rc = printer_protocol_get_label_offset(&offset, - runtime_policy_rest_label_timeout_ms(), + domain_policy_rest_label_timeout_ms(), cmd_err, sizeof(cmd_err)); if (rc != ESP_OK) { @@ -576,7 +590,7 @@ esp_err_t rest_server_label_offset_post(httpd_req_t *req) { char cmd_err[128] = {0}; esp_err_t rc = printer_protocol_set_label_offset(value, - runtime_policy_rest_label_timeout_ms(), + domain_policy_rest_label_timeout_ms(), cmd_err, sizeof(cmd_err)); if (rc != ESP_OK) { @@ -600,7 +614,7 @@ esp_err_t rest_server_ota_version_get(httpd_req_t *req) { printer_ota_version_t version = {0}; char cmd_err[128] = {0}; esp_err_t rc = printer_protocol_ota_get_version(&version, - runtime_policy_rest_ota_timeout_ms(), + domain_policy_rest_ota_timeout_ms(), cmd_err, sizeof(cmd_err)); if (rc != ESP_OK) { @@ -626,7 +640,7 @@ esp_err_t rest_server_ota_jump_boot_post(httpd_req_t *req) { } char cmd_err[128] = {0}; - esp_err_t rc = printer_protocol_ota_jump_boot(runtime_policy_rest_ota_timeout_ms(), + esp_err_t rc = printer_protocol_ota_jump_boot(domain_policy_rest_ota_timeout_ms(), cmd_err, sizeof(cmd_err)); if (rc != ESP_OK) { @@ -647,7 +661,7 @@ esp_err_t rest_server_ota_jump_app_post(httpd_req_t *req) { } char cmd_err[128] = {0}; - esp_err_t rc = printer_protocol_ota_jump_app(runtime_policy_rest_ota_timeout_ms(), + esp_err_t rc = printer_protocol_ota_jump_app(domain_policy_rest_ota_timeout_ms(), cmd_err, sizeof(cmd_err)); if (rc != ESP_OK) { @@ -685,8 +699,14 @@ esp_err_t rest_server_ota_erase_page_post(httpd_req_t *req) { return rest_server_send_error(req, "400 Bad Request", "page_num must be 0..65535"); } - uint32_t timeout_ms = runtime_policy_rest_ota_timeout_ms(); - if (cJSON_IsNumber(jtimeout) && jtimeout->valuedouble > 0 && jtimeout->valuedouble <= 30000) { + uint32_t timeout_ms = domain_policy_rest_ota_timeout_ms(); + if (jtimeout != NULL) { + if (!cJSON_IsNumber(jtimeout) || + jtimeout->valuedouble < (double)domain_policy_rest_ota_timeout_min_ms() || + jtimeout->valuedouble > (double)domain_policy_rest_ota_timeout_max_ms()) { + cJSON_Delete(json); + return rest_server_send_error(req, "400 Bad Request", "timeout_ms out of range"); + } timeout_ms = (uint32_t)jtimeout->valuedouble; } @@ -734,8 +754,14 @@ esp_err_t rest_server_ota_write_frame_post(httpd_req_t *req) { return rest_server_send_error(req, "400 Bad Request", "packet_num and data are required"); } - uint32_t timeout_ms = runtime_policy_rest_ota_timeout_ms(); - if (cJSON_IsNumber(jtimeout) && jtimeout->valuedouble > 0 && jtimeout->valuedouble <= 30000) { + uint32_t timeout_ms = domain_policy_rest_ota_timeout_ms(); + if (jtimeout != NULL) { + if (!cJSON_IsNumber(jtimeout) || + jtimeout->valuedouble < (double)domain_policy_rest_ota_timeout_min_ms() || + jtimeout->valuedouble > (double)domain_policy_rest_ota_timeout_max_ms()) { + cJSON_Delete(json); + return rest_server_send_error(req, "400 Bad Request", "timeout_ms out of range"); + } timeout_ms = (uint32_t)jtimeout->valuedouble; } bool is_last = rest_server_json_bool_with_default(jlast, false); @@ -775,134 +801,7 @@ esp_err_t rest_server_ota_upgrade_post(httpd_req_t *req) { if (!rest_server_auth_ok(req)) { return rest_server_send_error(req, "401 Unauthorized", "unauthorized"); } - - char *body = NULL; - esp_err_t body_err = rest_server_read_body(req, &body); - if (body_err != ESP_OK) { - return rest_server_send_error(req, "400 Bad Request", "invalid request body"); - } - cJSON *json = cJSON_Parse(body); - free(body); - if (json == NULL) { - return rest_server_send_error(req, "400 Bad Request", "invalid json"); - } - - cJSON *jfirmware = cJSON_GetObjectItemCaseSensitive(json, "firmware"); - cJSON *jjumpboot = cJSON_GetObjectItemCaseSensitive(json, "jump_boot"); - cJSON *jjumpapp = cJSON_GetObjectItemCaseSensitive(json, "jump_app"); - cJSON *jpagesize = cJSON_GetObjectItemCaseSensitive(json, "page_size"); - cJSON *jpacketsize = cJSON_GetObjectItemCaseSensitive(json, "packet_size"); - cJSON *jtimeout = cJSON_GetObjectItemCaseSensitive(json, "timeout_ms_per_step"); - cJSON *jreadversion = cJSON_GetObjectItemCaseSensitive(json, "read_version_after"); - - if (!cJSON_IsString(jfirmware) || jfirmware->valuestring == NULL) { - cJSON_Delete(json); - return rest_server_send_error(req, "400 Bad Request", "firmware(base64) is required"); - } - - bool jump_boot = rest_server_json_bool_with_default(jjumpboot, true); - bool jump_app = rest_server_json_bool_with_default(jjumpapp, true); - bool read_version_after = rest_server_json_bool_with_default(jreadversion, true); - - uint16_t page_size = 1024; - uint16_t packet_size = 236; - uint32_t timeout_ms = runtime_policy_rest_ota_timeout_ms(); - if (cJSON_IsNumber(jpagesize) && jpagesize->valuedouble >= 256 && jpagesize->valuedouble <= 4096) { - page_size = (uint16_t)jpagesize->valuedouble; - } - if (cJSON_IsNumber(jpacketsize) && jpacketsize->valuedouble >= 16 && jpacketsize->valuedouble <= 236) { - packet_size = (uint16_t)jpacketsize->valuedouble; - } - if (cJSON_IsNumber(jtimeout) && jtimeout->valuedouble >= 500 && jtimeout->valuedouble <= 60000) { - timeout_ms = (uint32_t)jtimeout->valuedouble; - } - - uint8_t *firmware = NULL; - size_t firmware_len = 0; - esp_err_t b64_rc = rest_server_base64_decode_alloc(jfirmware->valuestring, &firmware, &firmware_len); - cJSON_Delete(json); - if (b64_rc != ESP_OK || firmware_len == 0) { - free(firmware); - return rest_server_send_error(req, "400 Bad Request", "invalid firmware base64"); - } - - char cmd_err[128] = {0}; - if (jump_boot) { - esp_err_t rc = printer_protocol_ota_jump_boot(timeout_ms, cmd_err, sizeof(cmd_err)); - if (rc != ESP_OK) { - free(firmware); - return rest_server_send_error(req, "409 Conflict", cmd_err[0] != '\0' ? cmd_err : "jump boot failed"); - } - } - - uint32_t total_pages = (uint32_t)((firmware_len + page_size - 1) / page_size); - for (uint32_t page = 0; page < total_pages; ++page) { - esp_err_t rc = printer_protocol_ota_erase_page((uint16_t)page, timeout_ms, cmd_err, sizeof(cmd_err)); - if (rc != ESP_OK) { - free(firmware); - return rest_server_send_error(req, - "409 Conflict", - cmd_err[0] != '\0' ? cmd_err : "erase page failed"); - } - } - - uint32_t total_packets = (uint32_t)((firmware_len + packet_size - 1) / packet_size); - for (uint32_t packet = 0; packet < total_packets; ++packet) { - size_t start = (size_t)packet * packet_size; - size_t remain = firmware_len - start; - size_t len = remain > packet_size ? packet_size : remain; - bool is_last = (packet + 1) == total_packets; - - esp_err_t rc = printer_protocol_ota_write_frame((uint16_t)packet, - is_last, - firmware + start, - len, - timeout_ms, - cmd_err, - sizeof(cmd_err)); - if (rc != ESP_OK) { - free(firmware); - return rest_server_send_error(req, - "409 Conflict", - cmd_err[0] != '\0' ? cmd_err : "write frame failed"); - } - } - - if (jump_app) { - esp_err_t rc = printer_protocol_ota_jump_app(timeout_ms, cmd_err, sizeof(cmd_err)); - if (rc != ESP_OK) { - free(firmware); - return rest_server_send_error(req, "409 Conflict", cmd_err[0] != '\0' ? cmd_err : "jump app failed"); - } - } - - printer_ota_version_t version = {0}; - bool version_ok = false; - if (read_version_after) { - if (printer_protocol_ota_get_version(&version, timeout_ms, cmd_err, sizeof(cmd_err)) == ESP_OK) { - version_ok = true; - } - } - - free(firmware); - - cJSON *root = cJSON_CreateObject(); - cJSON_AddBoolToObject(root, "ok", true); - cJSON_AddNumberToObject(root, "firmware_len", (double)firmware_len); - cJSON_AddNumberToObject(root, "page_size", page_size); - cJSON_AddNumberToObject(root, "packet_size", packet_size); - cJSON_AddNumberToObject(root, "total_pages", total_pages); - cJSON_AddNumberToObject(root, "total_packets", total_packets); - cJSON_AddBoolToObject(root, "jump_boot", jump_boot); - cJSON_AddBoolToObject(root, "jump_app", jump_app); - if (version_ok) { - cJSON *v = cJSON_AddObjectToObject(root, "version"); - cJSON_AddNumberToObject(v, "major", version.major); - cJSON_AddNumberToObject(v, "minor", version.minor); - cJSON_AddNumberToObject(v, "patch", version.patch); - } - - esp_err_t err = rest_server_send_json(req, "200 OK", root); - cJSON_Delete(root); - return err; + return rest_server_send_error(req, + "409 Conflict", + "bulk ota upgrade endpoint disabled; use jump/erase/write/version step APIs"); } diff --git a/components/control_plane/src/rest_server_print_image.c b/components/control_plane/src/rest_server_print_image.c index f6f97fb..a28a6ef 100644 --- a/components/control_plane/src/rest_server_print_image.c +++ b/components/control_plane/src/rest_server_print_image.c @@ -244,8 +244,8 @@ static bool parse_image_generation_options(cJSON *json, } memset(out, 0, sizeof(*out)); - out->timeout_ms = runtime_policy_image_generation_timeout_default_ms(); - out->fetch_timeout_ms = runtime_policy_image_download_timeout_default_ms(); + out->timeout_ms = domain_policy_image_generation_timeout_default_ms(); + out->fetch_timeout_ms = domain_policy_image_download_timeout_default_ms(); cJSON *jprompt = cJSON_GetObjectItemCaseSensitive(json, "prompt"); if (!cJSON_IsString(jprompt) || jprompt->valuestring == NULL || jprompt->valuestring[0] == '\0') { @@ -304,14 +304,14 @@ static bool parse_image_generation_options(cJSON *json, cJSON *jtimeout = cJSON_GetObjectItemCaseSensitive(json, "timeout_ms"); if (cJSON_IsNumber(jtimeout)) { - if (jtimeout->valuedouble < runtime_policy_image_generation_timeout_min_ms() || - jtimeout->valuedouble > runtime_policy_image_generation_timeout_max_ms()) { + if (jtimeout->valuedouble < domain_policy_image_generation_timeout_min_ms() || + jtimeout->valuedouble > domain_policy_image_generation_timeout_max_ms()) { if (err != NULL && err_len > 0) { snprintf(err, err_len, "timeout_ms must be %u..%u", - (unsigned)runtime_policy_image_generation_timeout_min_ms(), - (unsigned)runtime_policy_image_generation_timeout_max_ms()); + (unsigned)domain_policy_image_generation_timeout_min_ms(), + (unsigned)domain_policy_image_generation_timeout_max_ms()); } return false; } @@ -320,14 +320,14 @@ static bool parse_image_generation_options(cJSON *json, cJSON *jfetch_timeout = cJSON_GetObjectItemCaseSensitive(json, "fetch_timeout_ms"); if (cJSON_IsNumber(jfetch_timeout)) { - if (jfetch_timeout->valuedouble < runtime_policy_image_download_timeout_min_ms() || - jfetch_timeout->valuedouble > runtime_policy_image_download_timeout_max_ms()) { + if (jfetch_timeout->valuedouble < domain_policy_image_download_timeout_min_ms() || + jfetch_timeout->valuedouble > domain_policy_image_download_timeout_max_ms()) { if (err != NULL && err_len > 0) { snprintf(err, err_len, "fetch_timeout_ms must be %u..%u", - (unsigned)runtime_policy_image_download_timeout_min_ms(), - (unsigned)runtime_policy_image_download_timeout_max_ms()); + (unsigned)domain_policy_image_download_timeout_min_ms(), + (unsigned)domain_policy_image_download_timeout_max_ms()); } return false; } diff --git a/components/control_plane/src/rest_server_print_render.c b/components/control_plane/src/rest_server_print_render.c index 45312b3..cab1d82 100644 --- a/components/control_plane/src/rest_server_print_render.c +++ b/components/control_plane/src/rest_server_print_render.c @@ -266,28 +266,6 @@ esp_err_t rest_server_print_label_post(httpd_req_t *req) { offset_value = (uint8_t)joffset->valuedouble; } - if (has_offset) { - char cmd_err[128] = {0}; - esp_err_t offset_rc = printer_protocol_set_label_offset(offset_value, 3000, cmd_err, sizeof(cmd_err)); - if (offset_rc != ESP_OK) { - cJSON_Delete(json); - return rest_server_send_error(req, - "409 Conflict", - cmd_err[0] != '\0' ? cmd_err : "set label offset failed"); - } - } - - if (gap_move_before) { - char cmd_err[128] = {0}; - esp_err_t gap_rc = printer_protocol_gap_move(5000, cmd_err, sizeof(cmd_err)); - if (gap_rc != ESP_OK) { - cJSON_Delete(json); - return rest_server_send_error(req, - "409 Conflict", - cmd_err[0] != '\0' ? cmd_err : "gap move failed"); - } - } - const char *density = (cJSON_IsString(jdensity) && jdensity->valuestring != NULL) ? jdensity->valuestring : "中等"; @@ -312,6 +290,33 @@ esp_err_t rest_server_print_label_post(httpd_req_t *req) { decode_err[0] != '\0' ? decode_err : "decode image failed"); } + if (has_offset) { + char cmd_err[128] = {0}; + esp_err_t offset_rc = printer_protocol_set_label_offset(offset_value, + domain_policy_rest_label_timeout_ms(), + cmd_err, + sizeof(cmd_err)); + if (offset_rc != ESP_OK) { + free(raster); + return rest_server_send_error(req, + "409 Conflict", + cmd_err[0] != '\0' ? cmd_err : "set label offset failed"); + } + } + + if (gap_move_before) { + char cmd_err[128] = {0}; + esp_err_t gap_rc = printer_protocol_gap_move(domain_policy_rest_label_timeout_ms(), + cmd_err, + sizeof(cmd_err)); + if (gap_rc != ESP_OK) { + free(raster); + return rest_server_send_error(req, + "409 Conflict", + cmd_err[0] != '\0' ? cmd_err : "gap move failed"); + } + } + esp_err_t submit_rc = rest_server_print_submit_raster_job_and_reply(req, raster, raster_len, diff --git a/components/control_plane/src/rest_server_voice.c b/components/control_plane/src/rest_server_voice.c index 4b22690..5e62b73 100644 --- a/components/control_plane/src/rest_server_voice.c +++ b/components/control_plane/src/rest_server_voice.c @@ -4,6 +4,45 @@ #include "domain.h" +#define VOICE_CONTROL_TIMEOUT_MIN_MS 500 +#define VOICE_CONTROL_TIMEOUT_MAX_MS 30000 + +static esp_err_t rest_server_parse_voice_timeout(httpd_req_t *req, uint32_t *out_timeout_ms) { + if (out_timeout_ms == NULL) { + return ESP_ERR_INVALID_ARG; + } + *out_timeout_ms = 0; + if (req->content_len <= 0) { + return ESP_OK; + } + + char *body = NULL; + esp_err_t body_err = rest_server_read_body(req, &body); + if (body_err != ESP_OK) { + return body_err; + } + + cJSON *json = cJSON_Parse(body); + free(body); + if (json == NULL) { + return ESP_ERR_INVALID_ARG; + } + + cJSON *jtimeout = cJSON_GetObjectItemCaseSensitive(json, "timeout_ms"); + if (jtimeout != NULL) { + if (!cJSON_IsNumber(jtimeout) || + jtimeout->valuedouble < VOICE_CONTROL_TIMEOUT_MIN_MS || + jtimeout->valuedouble > VOICE_CONTROL_TIMEOUT_MAX_MS) { + cJSON_Delete(json); + return ESP_ERR_INVALID_ARG; + } + *out_timeout_ms = (uint32_t)jtimeout->valuedouble; + } + + cJSON_Delete(json); + return ESP_OK; +} + static void rest_server_add_voice_status(cJSON *root) { voice_interaction_status_t st = {0}; voice_interaction_get_status(&st); @@ -48,18 +87,19 @@ esp_err_t rest_server_voice_session_start_post(httpd_req_t *req) { return rest_server_send_error(req, "401 Unauthorized", "unauthorized"); } - if (req->content_len > 0) { - char *body = NULL; - esp_err_t body_err = rest_server_read_body(req, &body); - if (body_err != ESP_OK) { - return rest_server_send_error(req, "400 Bad Request", "invalid request body"); - } - free(body); + uint32_t timeout_ms = 0; + if (rest_server_parse_voice_timeout(req, &timeout_ms) != ESP_OK) { + return rest_server_send_error(req, "400 Bad Request", "invalid request body"); } char start_err[128] = {0}; - esp_err_t rc = voice_interaction_start(start_err, sizeof(start_err)); + esp_err_t rc = voice_interaction_start_with_timeout(timeout_ms, start_err, sizeof(start_err)); if (rc != ESP_OK) { + if (rc == ESP_ERR_TIMEOUT) { + return rest_server_send_error(req, + "504 Gateway Timeout", + start_err[0] != '\0' ? start_err : "voice session start timeout"); + } return rest_server_send_error(req, "409 Conflict", start_err[0] != '\0' ? start_err : "voice session start failed"); @@ -80,18 +120,19 @@ esp_err_t rest_server_voice_session_stop_post(httpd_req_t *req) { return rest_server_send_error(req, "401 Unauthorized", "unauthorized"); } - if (req->content_len > 0) { - char *body = NULL; - esp_err_t body_err = rest_server_read_body(req, &body); - if (body_err != ESP_OK) { - return rest_server_send_error(req, "400 Bad Request", "invalid request body"); - } - free(body); + uint32_t timeout_ms = 0; + if (rest_server_parse_voice_timeout(req, &timeout_ms) != ESP_OK) { + return rest_server_send_error(req, "400 Bad Request", "invalid request body"); } char stop_err[128] = {0}; - esp_err_t rc = voice_interaction_stop(stop_err, sizeof(stop_err)); + esp_err_t rc = voice_interaction_stop_with_timeout(timeout_ms, stop_err, sizeof(stop_err)); if (rc != ESP_OK) { + if (rc == ESP_ERR_TIMEOUT) { + return rest_server_send_error(req, + "504 Gateway Timeout", + stop_err[0] != '\0' ? stop_err : "voice session stop timeout"); + } return rest_server_send_error(req, "409 Conflict", stop_err[0] != '\0' ? stop_err : "voice session stop failed"); diff --git a/components/domain/CMakeLists.txt b/components/domain/CMakeLists.txt index a7761f2..e2eafac 100644 --- a/components/domain/CMakeLists.txt +++ b/components/domain/CMakeLists.txt @@ -9,6 +9,7 @@ idf_component_register( "src/image_generation.c" "src/screen_preview.c" "src/system_runtime.c" + "src/domain_runtime_contracts.c" "src/voice_interaction.c" "src/voice_interaction_common.c" "src/voice_interaction_ws.c" @@ -19,9 +20,9 @@ idf_component_register( "third_party/qrcodegen.c" INCLUDE_DIRS "include" - "third_party" PRIV_INCLUDE_DIRS "internal" + "third_party" REQUIRES platform esp_http_client diff --git a/components/domain/include/domain.h b/components/domain/include/domain.h index 7c0bf83..98eb608 100644 --- a/components/domain/include/domain.h +++ b/components/domain/include/domain.h @@ -5,7 +5,6 @@ #include #include "esp_err.h" -#include "platform.h" #ifdef __cplusplus extern "C" { @@ -19,6 +18,81 @@ esp_err_t system_runtime_bootstrap(void); bool system_runtime_wifi_ready(void); void system_runtime_get_ip(char *buf, size_t buf_len); +// ---------- domain_policy ---------- +uint32_t domain_policy_lifecycle_start_retry_count(void); +uint32_t domain_policy_lifecycle_retry_backoff_ms(uint32_t attempt); +bool domain_policy_is_retryable_error(esp_err_t err); + +uint32_t domain_policy_printer_stop_timeout_ms(void); + +uint32_t domain_policy_rest_printer_connect_timeout_ms(void); +uint32_t domain_policy_rest_printer_connect_timeout_min_ms(void); +uint32_t domain_policy_rest_printer_connect_timeout_max_ms(void); + +uint32_t domain_policy_rest_label_timeout_ms(void); +uint32_t domain_policy_rest_label_timeout_min_ms(void); +uint32_t domain_policy_rest_label_timeout_max_ms(void); + +uint32_t domain_policy_rest_ota_timeout_ms(void); +uint32_t domain_policy_rest_ota_timeout_min_ms(void); +uint32_t domain_policy_rest_ota_timeout_max_ms(void); + +uint32_t domain_policy_image_generation_timeout_default_ms(void); +uint32_t domain_policy_image_generation_timeout_min_ms(void); +uint32_t domain_policy_image_generation_timeout_max_ms(void); + +uint32_t domain_policy_image_download_timeout_default_ms(void); +uint32_t domain_policy_image_download_timeout_min_ms(void); +uint32_t domain_policy_image_download_timeout_max_ms(void); + +// ---------- domain_diagnostics ---------- +typedef enum { + DOMAIN_DIAG_COUNTER_LIFECYCLE_START_ATTEMPT = 0, + DOMAIN_DIAG_COUNTER_LIFECYCLE_START_SUCCESS, + DOMAIN_DIAG_COUNTER_LIFECYCLE_START_FAILED, + DOMAIN_DIAG_COUNTER_LIFECYCLE_START_RETRY, + DOMAIN_DIAG_COUNTER_LIFECYCLE_STOP_ATTEMPT, + DOMAIN_DIAG_COUNTER_LIFECYCLE_STOP_SUCCESS, + DOMAIN_DIAG_COUNTER_LIFECYCLE_STOP_FAILED, + DOMAIN_DIAG_COUNTER_WIFI_CONNECT_SUCCESS, + DOMAIN_DIAG_COUNTER_WIFI_CONNECT_FAILED, + DOMAIN_DIAG_COUNTER_WIFI_CONNECT_TIMEOUT, + DOMAIN_DIAG_COUNTER_PRINTER_JOB_SUBMITTED, + DOMAIN_DIAG_COUNTER_PRINTER_JOB_SUCCESS, + DOMAIN_DIAG_COUNTER_PRINTER_JOB_FAILED, + DOMAIN_DIAG_COUNTER_PRINTER_JOB_CANCELED, + DOMAIN_DIAG_COUNTER_IMAGE_GENERATE_ATTEMPT, + DOMAIN_DIAG_COUNTER_IMAGE_GENERATE_SUCCESS, + DOMAIN_DIAG_COUNTER_IMAGE_GENERATE_FAILED, + DOMAIN_DIAG_COUNTER_IMAGE_GENERATE_TIMEOUT, + DOMAIN_DIAG_COUNTER_REST_RESPONSES_TOTAL, + DOMAIN_DIAG_COUNTER_REST_ERRORS_TOTAL, + DOMAIN_DIAG_COUNTER_MAX, +} domain_diag_counter_t; + +typedef enum { + DOMAIN_DIAG_GAUGE_LIFECYCLE_STATE = 0, + DOMAIN_DIAG_GAUGE_STATUS_POLL_PAUSE_DEPTH, + DOMAIN_DIAG_GAUGE_PRINTER_QUEUE_DEPTH, + DOMAIN_DIAG_GAUGE_MAX, +} domain_diag_gauge_t; + +typedef struct { + uint64_t counters[DOMAIN_DIAG_COUNTER_MAX]; + int32_t gauges[DOMAIN_DIAG_GAUGE_MAX]; + int64_t last_error_ms; + esp_err_t last_error_code; + char last_error_source[32]; + char last_error_message[96]; +} domain_diag_snapshot_t; + +void domain_diag_counter_add(domain_diag_counter_t counter, uint32_t delta); +void domain_diag_set_gauge(domain_diag_gauge_t gauge, int32_t value); +void domain_diag_record_error(const char *source, esp_err_t code, const char *message); +void domain_diag_get_snapshot(domain_diag_snapshot_t *out_snapshot); +const char *domain_diag_counter_name(domain_diag_counter_t counter); +const char *domain_diag_gauge_name(domain_diag_gauge_t gauge); + // ---------- raster_tools ---------- esp_err_t raster_tools_render_text_384(const char *text, uint8_t scale, @@ -286,7 +360,9 @@ typedef struct { } voice_interaction_status_t; esp_err_t voice_interaction_init(void); +esp_err_t voice_interaction_start_with_timeout(uint32_t timeout_ms, char *err, size_t err_len); esp_err_t voice_interaction_start(char *err, size_t err_len); +esp_err_t voice_interaction_stop_with_timeout(uint32_t timeout_ms, char *err, size_t err_len); esp_err_t voice_interaction_stop(char *err, size_t err_len); esp_err_t voice_interaction_tap_start(char *err, size_t err_len); diff --git a/components/domain/internal/printer_protocol_internal.h b/components/domain/internal/printer_protocol_internal.h index bb26c7d..211d765 100644 --- a/components/domain/internal/printer_protocol_internal.h +++ b/components/domain/internal/printer_protocol_internal.h @@ -6,6 +6,7 @@ #include "domain.h" #include "esp_err.h" +#include "platform.h" #include "freertos/FreeRTOS.h" #include "freertos/event_groups.h" #include "freertos/queue.h" @@ -93,6 +94,7 @@ esp_err_t printer_protocol_send_cmd_wait_response(uint8_t cmd, bool with_checksum, uint32_t timeout_ms, bool expect_ack, + bool reset_transport_on_timeout, uint8_t *out_payload, size_t out_payload_cap, uint16_t *out_payload_len, @@ -111,7 +113,8 @@ bool printer_protocol_send_cmd_with_ack(uint8_t cmd, const uint8_t *payload, uint16_t payload_len, bool with_checksum, - uint32_t timeout_ms); + uint32_t timeout_ms, + bool reset_transport_on_timeout); int printer_protocol_find_job_idx_locked(uint32_t id); int printer_protocol_alloc_job_slot_locked(void); diff --git a/components/domain/internal/voice_interaction_internal.h b/components/domain/internal/voice_interaction_internal.h index 076ca5a..7a2b478 100644 --- a/components/domain/internal/voice_interaction_internal.h +++ b/components/domain/internal/voice_interaction_internal.h @@ -11,6 +11,7 @@ #include "freertos/semphr.h" #include "freertos/task.h" #include "domain.h" +#include "platform.h" #define VOICE_STREAMING_MODE "duplex" #define VOICE_TASK_GROUP "aigc" diff --git a/components/domain/src/domain_runtime_contracts.c b/components/domain/src/domain_runtime_contracts.c new file mode 100644 index 0000000..efe268e --- /dev/null +++ b/components/domain/src/domain_runtime_contracts.c @@ -0,0 +1,201 @@ +#include "domain.h" + +#include + +#include "platform.h" + +static runtime_diag_counter_t to_platform_counter(domain_diag_counter_t counter) { + switch (counter) { + case DOMAIN_DIAG_COUNTER_LIFECYCLE_START_ATTEMPT: + return RUNTIME_DIAG_COUNTER_LIFECYCLE_START_ATTEMPT; + case DOMAIN_DIAG_COUNTER_LIFECYCLE_START_SUCCESS: + return RUNTIME_DIAG_COUNTER_LIFECYCLE_START_SUCCESS; + case DOMAIN_DIAG_COUNTER_LIFECYCLE_START_FAILED: + return RUNTIME_DIAG_COUNTER_LIFECYCLE_START_FAILED; + case DOMAIN_DIAG_COUNTER_LIFECYCLE_START_RETRY: + return RUNTIME_DIAG_COUNTER_LIFECYCLE_START_RETRY; + case DOMAIN_DIAG_COUNTER_LIFECYCLE_STOP_ATTEMPT: + return RUNTIME_DIAG_COUNTER_LIFECYCLE_STOP_ATTEMPT; + case DOMAIN_DIAG_COUNTER_LIFECYCLE_STOP_SUCCESS: + return RUNTIME_DIAG_COUNTER_LIFECYCLE_STOP_SUCCESS; + case DOMAIN_DIAG_COUNTER_LIFECYCLE_STOP_FAILED: + return RUNTIME_DIAG_COUNTER_LIFECYCLE_STOP_FAILED; + case DOMAIN_DIAG_COUNTER_WIFI_CONNECT_SUCCESS: + return RUNTIME_DIAG_COUNTER_WIFI_CONNECT_SUCCESS; + case DOMAIN_DIAG_COUNTER_WIFI_CONNECT_FAILED: + return RUNTIME_DIAG_COUNTER_WIFI_CONNECT_FAILED; + case DOMAIN_DIAG_COUNTER_WIFI_CONNECT_TIMEOUT: + return RUNTIME_DIAG_COUNTER_WIFI_CONNECT_TIMEOUT; + case DOMAIN_DIAG_COUNTER_PRINTER_JOB_SUBMITTED: + return RUNTIME_DIAG_COUNTER_PRINTER_JOB_SUBMITTED; + case DOMAIN_DIAG_COUNTER_PRINTER_JOB_SUCCESS: + return RUNTIME_DIAG_COUNTER_PRINTER_JOB_SUCCESS; + case DOMAIN_DIAG_COUNTER_PRINTER_JOB_FAILED: + return RUNTIME_DIAG_COUNTER_PRINTER_JOB_FAILED; + case DOMAIN_DIAG_COUNTER_PRINTER_JOB_CANCELED: + return RUNTIME_DIAG_COUNTER_PRINTER_JOB_CANCELED; + case DOMAIN_DIAG_COUNTER_IMAGE_GENERATE_ATTEMPT: + return RUNTIME_DIAG_COUNTER_IMAGE_GENERATE_ATTEMPT; + case DOMAIN_DIAG_COUNTER_IMAGE_GENERATE_SUCCESS: + return RUNTIME_DIAG_COUNTER_IMAGE_GENERATE_SUCCESS; + case DOMAIN_DIAG_COUNTER_IMAGE_GENERATE_FAILED: + return RUNTIME_DIAG_COUNTER_IMAGE_GENERATE_FAILED; + case DOMAIN_DIAG_COUNTER_IMAGE_GENERATE_TIMEOUT: + return RUNTIME_DIAG_COUNTER_IMAGE_GENERATE_TIMEOUT; + case DOMAIN_DIAG_COUNTER_REST_RESPONSES_TOTAL: + return RUNTIME_DIAG_COUNTER_REST_RESPONSES_TOTAL; + case DOMAIN_DIAG_COUNTER_REST_ERRORS_TOTAL: + return RUNTIME_DIAG_COUNTER_REST_ERRORS_TOTAL; + default: + return RUNTIME_DIAG_COUNTER_MAX; + } +} + +static runtime_diag_gauge_t to_platform_gauge(domain_diag_gauge_t gauge) { + switch (gauge) { + case DOMAIN_DIAG_GAUGE_LIFECYCLE_STATE: + return RUNTIME_DIAG_GAUGE_LIFECYCLE_STATE; + case DOMAIN_DIAG_GAUGE_STATUS_POLL_PAUSE_DEPTH: + return RUNTIME_DIAG_GAUGE_STATUS_POLL_PAUSE_DEPTH; + case DOMAIN_DIAG_GAUGE_PRINTER_QUEUE_DEPTH: + return RUNTIME_DIAG_GAUGE_PRINTER_QUEUE_DEPTH; + default: + return RUNTIME_DIAG_GAUGE_MAX; + } +} + +uint32_t domain_policy_lifecycle_start_retry_count(void) { + return runtime_policy_lifecycle_start_retry_count(); +} + +uint32_t domain_policy_lifecycle_retry_backoff_ms(uint32_t attempt) { + return runtime_policy_lifecycle_retry_backoff_ms(attempt); +} + +bool domain_policy_is_retryable_error(esp_err_t err) { + return runtime_policy_is_retryable_error(err); +} + +uint32_t domain_policy_printer_stop_timeout_ms(void) { + return runtime_policy_printer_stop_timeout_ms(); +} + +uint32_t domain_policy_rest_printer_connect_timeout_ms(void) { + return runtime_policy_rest_printer_connect_timeout_ms(); +} + +uint32_t domain_policy_rest_printer_connect_timeout_min_ms(void) { + return 1000; +} + +uint32_t domain_policy_rest_printer_connect_timeout_max_ms(void) { + return 60000; +} + +uint32_t domain_policy_rest_label_timeout_ms(void) { + return runtime_policy_rest_label_timeout_ms(); +} + +uint32_t domain_policy_rest_label_timeout_min_ms(void) { + return 500; +} + +uint32_t domain_policy_rest_label_timeout_max_ms(void) { + return 30000; +} + +uint32_t domain_policy_rest_ota_timeout_ms(void) { + return runtime_policy_rest_ota_timeout_ms(); +} + +uint32_t domain_policy_rest_ota_timeout_min_ms(void) { + return 500; +} + +uint32_t domain_policy_rest_ota_timeout_max_ms(void) { + return 60000; +} + +uint32_t domain_policy_image_generation_timeout_default_ms(void) { + return runtime_policy_image_generation_timeout_default_ms(); +} + +uint32_t domain_policy_image_generation_timeout_min_ms(void) { + return runtime_policy_image_generation_timeout_min_ms(); +} + +uint32_t domain_policy_image_generation_timeout_max_ms(void) { + return runtime_policy_image_generation_timeout_max_ms(); +} + +uint32_t domain_policy_image_download_timeout_default_ms(void) { + return runtime_policy_image_download_timeout_default_ms(); +} + +uint32_t domain_policy_image_download_timeout_min_ms(void) { + return runtime_policy_image_download_timeout_min_ms(); +} + +uint32_t domain_policy_image_download_timeout_max_ms(void) { + return runtime_policy_image_download_timeout_max_ms(); +} + +void domain_diag_counter_add(domain_diag_counter_t counter, uint32_t delta) { + runtime_diag_counter_t mapped = to_platform_counter(counter); + if (mapped >= RUNTIME_DIAG_COUNTER_MAX) { + return; + } + runtime_diag_counter_add(mapped, delta); +} + +void domain_diag_set_gauge(domain_diag_gauge_t gauge, int32_t value) { + runtime_diag_gauge_t mapped = to_platform_gauge(gauge); + if (mapped >= RUNTIME_DIAG_GAUGE_MAX) { + return; + } + runtime_diag_set_gauge(mapped, value); +} + +void domain_diag_record_error(const char *source, esp_err_t code, const char *message) { + runtime_diag_record_error(source, code, message); +} + +void domain_diag_get_snapshot(domain_diag_snapshot_t *out_snapshot) { + if (out_snapshot == NULL) { + return; + } + + runtime_diag_snapshot_t platform_snapshot = {0}; + runtime_diag_get_snapshot(&platform_snapshot); + + for (int i = 0; i < DOMAIN_DIAG_COUNTER_MAX && i < RUNTIME_DIAG_COUNTER_MAX; ++i) { + out_snapshot->counters[i] = platform_snapshot.counters[i]; + } + for (int i = 0; i < DOMAIN_DIAG_GAUGE_MAX && i < RUNTIME_DIAG_GAUGE_MAX; ++i) { + out_snapshot->gauges[i] = platform_snapshot.gauges[i]; + } + out_snapshot->last_error_ms = platform_snapshot.last_error_ms; + out_snapshot->last_error_code = platform_snapshot.last_error_code; + strlcpy(out_snapshot->last_error_source, + platform_snapshot.last_error_source, + sizeof(out_snapshot->last_error_source)); + strlcpy(out_snapshot->last_error_message, + platform_snapshot.last_error_message, + sizeof(out_snapshot->last_error_message)); +} + +const char *domain_diag_counter_name(domain_diag_counter_t counter) { + runtime_diag_counter_t mapped = to_platform_counter(counter); + if (mapped >= RUNTIME_DIAG_COUNTER_MAX) { + return "unknown"; + } + return runtime_diag_counter_name(mapped); +} + +const char *domain_diag_gauge_name(domain_diag_gauge_t gauge) { + runtime_diag_gauge_t mapped = to_platform_gauge(gauge); + if (mapped >= RUNTIME_DIAG_GAUGE_MAX) { + return "unknown"; + } + return runtime_diag_gauge_name(mapped); +} diff --git a/components/domain/src/image_generation.c b/components/domain/src/image_generation.c index 021cee2..449faaf 100644 --- a/components/domain/src/image_generation.c +++ b/components/domain/src/image_generation.c @@ -1,4 +1,5 @@ #include "domain.h" +#include "platform.h" #include #include diff --git a/components/domain/src/printer_protocol.c b/components/domain/src/printer_protocol.c index ca5befb..1945ceb 100644 --- a/components/domain/src/printer_protocol.c +++ b/components/domain/src/printer_protocol.c @@ -441,16 +441,30 @@ static bool wait_ack(uint8_t cmd, uint32_t timeout_ms) { return payload_len >= 1 && payload[0] == 0x01; } +static void reset_transport_after_timeout(void) { + if (printer_protocol_is_stopping()) { + return; + } + if (printer_protocol_get_backend() == PRINTER_BACKEND_BLE) { + ble_printer_client_disconnect(); + } +} + bool printer_protocol_send_cmd_with_ack(uint8_t cmd, const uint8_t *payload, uint16_t payload_len, bool with_checksum, - uint32_t timeout_ms) { + uint32_t timeout_ms, + bool reset_transport_on_timeout) { clear_ack_signal(); if (printer_protocol_send_frame(cmd, payload, payload_len, with_checksum) != ESP_OK) { return false; } - return wait_ack(cmd, timeout_ms); + bool ok = wait_ack(cmd, timeout_ms); + if (!ok && reset_transport_on_timeout) { + reset_transport_after_timeout(); + } + return ok; } esp_err_t printer_protocol_send_cmd_wait_response(uint8_t cmd, @@ -459,6 +473,7 @@ esp_err_t printer_protocol_send_cmd_wait_response(uint8_t cmd, bool with_checksum, uint32_t timeout_ms, bool expect_ack, + bool reset_transport_on_timeout, uint8_t *out_payload, size_t out_payload_cap, uint16_t *out_payload_len, @@ -475,14 +490,21 @@ esp_err_t printer_protocol_send_cmd_wait_response(uint8_t cmd, uint8_t rsp[252]; uint16_t rsp_len = 0; if (!printer_protocol_wait_response(cmd, rsp, sizeof(rsp), &rsp_len, timeout_ms)) { + bool stopping = printer_protocol_is_stopping(); + if (!stopping && reset_transport_on_timeout) { + reset_transport_after_timeout(); + } if (err != NULL && err_len > 0) { - if (printer_protocol_is_stopping()) { + if (stopping) { snprintf(err, err_len, "protocol stopping"); } else { - snprintf(err, err_len, "response timeout"); + snprintf(err, + err_len, + reset_transport_on_timeout ? "response timeout, transport reset" + : "response timeout"); } } - return printer_protocol_is_stopping() ? ESP_ERR_INVALID_STATE : ESP_ERR_TIMEOUT; + return stopping ? ESP_ERR_INVALID_STATE : ESP_ERR_TIMEOUT; } if (expect_ack && (rsp_len < 1 || rsp[0] != 0x01)) { diff --git a/components/domain/src/printer_protocol_commands.c b/components/domain/src/printer_protocol_commands.c index 54b50bc..924210c 100644 --- a/components/domain/src/printer_protocol_commands.c +++ b/components/domain/src/printer_protocol_commands.c @@ -44,6 +44,7 @@ esp_err_t printer_protocol_gap_move(uint32_t timeout_ms, char *err, size_t err_l true, timeout_ms, true, + true, NULL, 0, NULL, @@ -85,6 +86,7 @@ esp_err_t printer_protocol_get_label_offset(uint8_t *out_offset, uint32_t timeou true, timeout_ms, false, + false, rsp, sizeof(rsp), &rsp_len, @@ -133,6 +135,7 @@ esp_err_t printer_protocol_set_label_offset(uint8_t offset, uint32_t timeout_ms, true, timeout_ms, true, + true, NULL, 0, NULL, @@ -164,6 +167,7 @@ esp_err_t printer_protocol_ota_jump_boot(uint32_t timeout_ms, char *err, size_t true, timeout_ms, true, + true, NULL, 0, NULL, @@ -195,6 +199,7 @@ esp_err_t printer_protocol_ota_jump_app(uint32_t timeout_ms, char *err, size_t e true, timeout_ms, true, + true, NULL, 0, NULL, @@ -231,6 +236,7 @@ esp_err_t printer_protocol_ota_erase_page(uint16_t page_num, uint32_t timeout_ms true, timeout_ms, true, + true, NULL, 0, NULL, @@ -283,6 +289,7 @@ esp_err_t printer_protocol_ota_write_frame(uint16_t packet_num, true, timeout_ms, true, + true, NULL, 0, NULL, @@ -326,6 +333,7 @@ esp_err_t printer_protocol_ota_get_version(printer_ota_version_t *out_version, true, timeout_ms, false, + false, rsp, sizeof(rsp), &rsp_len, @@ -361,22 +369,19 @@ esp_err_t printer_protocol_get_direct_debug_config(printer_direct_debug_config_t return ESP_ERR_NOT_SUPPORTED; } - platform_direct_debug_config_t cfg = {0}; - esp_err_t rc = platform_direct_printer_get_debug_config(&cfg); + esp_err_t rc = platform_direct_printer_get_debug_config(&out_config->shift_clock_high_us, + &out_config->shift_clock_low_us, + &out_config->latch_pulse_us, + &out_config->strobe_active_high, + &out_config->boost_active_high, + &out_config->override_strobe_on_us, + &out_config->override_strobe_interval_us, + &out_config->override_motor_step_us, + &out_config->override_steps_per_line); if (rc != ESP_OK) { write_err(err, err_len, "get direct debug config failed"); return rc; } - - out_config->shift_clock_high_us = cfg.shift_clock_high_us; - out_config->shift_clock_low_us = cfg.shift_clock_low_us; - out_config->latch_pulse_us = cfg.latch_pulse_us; - out_config->strobe_active_high = cfg.strobe_active_high; - out_config->boost_active_high = cfg.boost_active_high; - out_config->override_strobe_on_us = cfg.override_strobe_on_us; - out_config->override_strobe_interval_us = cfg.override_strobe_interval_us; - out_config->override_motor_step_us = cfg.override_motor_step_us; - out_config->override_steps_per_line = cfg.override_steps_per_line; return ESP_OK; } @@ -393,22 +398,19 @@ esp_err_t printer_protocol_set_direct_debug_config(const printer_direct_debug_co return ESP_ERR_NOT_SUPPORTED; } - platform_direct_debug_config_t cfg = {0}; - platform_direct_debug_config_t *pcfg = NULL; - if (config != NULL) { - cfg.shift_clock_high_us = config->shift_clock_high_us; - cfg.shift_clock_low_us = config->shift_clock_low_us; - cfg.latch_pulse_us = config->latch_pulse_us; - cfg.strobe_active_high = config->strobe_active_high; - cfg.boost_active_high = config->boost_active_high; - cfg.override_strobe_on_us = config->override_strobe_on_us; - cfg.override_strobe_interval_us = config->override_strobe_interval_us; - cfg.override_motor_step_us = config->override_motor_step_us; - cfg.override_steps_per_line = config->override_steps_per_line; - pcfg = &cfg; - } - - return platform_direct_printer_set_debug_config(pcfg, reset_defaults, err, err_len); + return platform_direct_printer_set_debug_config(config != NULL, + config != NULL ? config->shift_clock_high_us : 0, + config != NULL ? config->shift_clock_low_us : 0, + config != NULL ? config->latch_pulse_us : 0, + config != NULL ? config->strobe_active_high : false, + config != NULL ? config->boost_active_high : false, + config != NULL ? config->override_strobe_on_us : 0, + config != NULL ? config->override_strobe_interval_us : 0, + config != NULL ? config->override_motor_step_us : 0, + config != NULL ? config->override_steps_per_line : 0, + reset_defaults, + err, + err_len); } const char *printer_protocol_job_state_str(print_job_state_t state) { diff --git a/components/domain/src/printer_protocol_worker.c b/components/domain/src/printer_protocol_worker.c index d703d41..df44d2e 100644 --- a/components/domain/src/printer_protocol_worker.c +++ b/components/domain/src/printer_protocol_worker.c @@ -164,7 +164,7 @@ static bool run_print_job_ble(job_slot_t *job) { } uint8_t power_on = 0x01; - if (!printer_protocol_send_cmd_with_ack(CMD_POWER, &power_on, 1, true, 1500)) { + if (!printer_protocol_send_cmd_with_ack(CMD_POWER, &power_on, 1, true, 1500, true)) { snprintf(job->error, sizeof(job->error), "power on failed"); ESP_LOGW(TAG, "job %u print aborted: %s", (unsigned)job->id, job->error); return false; @@ -178,7 +178,7 @@ static bool run_print_job_ble(job_slot_t *job) { (uint8_t)(hot_time & 0xFF), }; - if (!printer_protocol_send_cmd_with_ack(CMD_SET_PARAM, param, sizeof(param), true, 1500)) { + if (!printer_protocol_send_cmd_with_ack(CMD_SET_PARAM, param, sizeof(param), true, 1500, true)) { snprintf(job->error, sizeof(job->error), "set print param failed"); ESP_LOGW(TAG, "job %u print aborted: %s", (unsigned)job->id, job->error); return false; @@ -217,7 +217,8 @@ static bool run_print_job_ble(job_slot_t *job) { &job->data[start], (uint16_t)chunk_len, false, - 2500)) { + 2500, + true)) { snprintf(job->error, sizeof(job->error), "send chunk timeout at %u", (unsigned)i); ESP_LOGW(TAG, "job %u print aborted: %s", (unsigned)job->id, job->error); return false; @@ -245,10 +246,15 @@ static bool run_print_job_ble(job_slot_t *job) { ESP_LOGI(TAG, "job %u data transfer done", (unsigned)job->id); uint8_t power_off = 0x00; - (void)printer_protocol_send_cmd_with_ack(CMD_POWER, &power_off, 1, true, 1200); + (void)printer_protocol_send_cmd_with_ack(CMD_POWER, &power_off, 1, true, 1200, false); uint8_t feed_payload[3] = {0x2B, 0x00, 0x0C}; - (void)printer_protocol_send_cmd_with_ack(CMD_SET_DISTANCE, feed_payload, sizeof(feed_payload), true, 1200); + (void)printer_protocol_send_cmd_with_ack(CMD_SET_DISTANCE, + feed_payload, + sizeof(feed_payload), + true, + 1200, + false); ESP_LOGI(TAG, "job %u print command sequence done", (unsigned)job->id); return true; diff --git a/components/domain/src/screen_preview.c b/components/domain/src/screen_preview.c index b7971ae..424a8dc 100644 --- a/components/domain/src/screen_preview.c +++ b/components/domain/src/screen_preview.c @@ -1,4 +1,5 @@ #include "domain.h" +#include "platform.h" esp_err_t screen_preview_show_raster(const uint8_t *raster, size_t raster_len, diff --git a/components/domain/src/system_runtime.c b/components/domain/src/system_runtime.c index 272fea6..ad3fd64 100644 --- a/components/domain/src/system_runtime.c +++ b/components/domain/src/system_runtime.c @@ -1,4 +1,5 @@ #include "domain.h" +#include "platform.h" esp_err_t system_runtime_start(void) { esp_err_t err = platform_bootstrap_init(); diff --git a/components/domain/src/voice_interaction.c b/components/domain/src/voice_interaction.c index bad9c39..ff09f90 100644 --- a/components/domain/src/voice_interaction.c +++ b/components/domain/src/voice_interaction.c @@ -9,7 +9,18 @@ static const char *TAG = "voice_interaction"; -static void voice_wait_worker_tasks_exit(uint32_t timeout_ms) { +static uint32_t clamp_timeout_ms(uint32_t timeout_ms, uint32_t default_ms, uint32_t min_ms, uint32_t max_ms) { + uint32_t value = timeout_ms == 0 ? default_ms : timeout_ms; + if (value < min_ms) { + value = min_ms; + } + if (value > max_ms) { + value = max_ms; + } + return value; +} + +static bool voice_wait_worker_tasks_exit(uint32_t timeout_ms) { int64_t deadline_ms = voice_now_ms() + (int64_t)timeout_ms; while (voice_now_ms() < deadline_ms) { bool uplink_alive = false; @@ -21,7 +32,7 @@ static void voice_wait_worker_tasks_exit(uint32_t timeout_ms) { } if (!uplink_alive && !heartbeat_alive) { - return; + return true; } vTaskDelay(pdMS_TO_TICKS(20)); } @@ -33,6 +44,7 @@ static void voice_wait_worker_tasks_exit(uint32_t timeout_ms) { s_voice.heartbeat_task != NULL); voice_unlock(); } + return false; } const char *voice_interaction_dialog_state_str(voice_dialog_state_t state) { @@ -68,7 +80,9 @@ esp_err_t voice_interaction_init(void) { return ESP_OK; } -esp_err_t voice_interaction_start(char *err, size_t err_len) { +esp_err_t voice_interaction_start_with_timeout(uint32_t timeout_ms, char *err, size_t err_len) { + uint32_t connect_timeout_ms = + clamp_timeout_ms(timeout_ms, VOICE_START_CONNECT_TIMEOUT_MS, 1000, 30000); ESP_LOGI(TAG, "[stage] session_start_enter"); if (!s_voice.ready) { voice_fill_err(err, err_len, "voice interaction not initialized"); @@ -260,8 +274,8 @@ esp_err_t voice_interaction_start(char *err, size_t err_len) { ESP_LOGI(TAG, "[stage] heartbeat_task_create_ok"); voice_log_heap_snapshot("after_create_heartbeat"); - ESP_LOGI(TAG, "[stage] wait_ws_connected_begin: timeout_ms=%d", VOICE_START_CONNECT_TIMEOUT_MS); - int64_t deadline = voice_now_ms() + VOICE_START_CONNECT_TIMEOUT_MS; + ESP_LOGI(TAG, "[stage] wait_ws_connected_begin: timeout_ms=%u", (unsigned)connect_timeout_ms); + int64_t deadline = voice_now_ms() + connect_timeout_ms; while (voice_now_ms() < deadline) { bool connected = false; if (voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) { @@ -301,17 +315,22 @@ exit_err: return ESP_FAIL; } -esp_err_t voice_interaction_stop(char *err, size_t err_len) { - (void)err; - (void)err_len; +esp_err_t voice_interaction_start(char *err, size_t err_len) { + return voice_interaction_start_with_timeout(VOICE_START_CONNECT_TIMEOUT_MS, err, err_len); +} + +esp_err_t voice_interaction_stop_with_timeout(uint32_t timeout_ms, char *err, size_t err_len) { + uint32_t worker_exit_timeout_ms = clamp_timeout_ms(timeout_ms, 1500, 500, 10000); ESP_LOGI(TAG, "[stage] session_stop_enter"); if (!s_voice.ready) { + voice_fill_err(err, err_len, "voice interaction not initialized"); ESP_LOGW(TAG, "[stage] session_stop_abort: not initialized"); return ESP_ERR_INVALID_STATE; } if (!voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) { + voice_fill_err(err, err_len, "voice lock timeout"); ESP_LOGW(TAG, "[stage] session_stop_abort: lock timeout"); return ESP_ERR_TIMEOUT; } @@ -343,7 +362,16 @@ esp_err_t voice_interaction_stop(char *err, size_t err_len) { if (heartbeat_task != NULL) { (void)xTaskAbortDelay(heartbeat_task); } - voice_wait_worker_tasks_exit(1500); + bool workers_exited = voice_wait_worker_tasks_exit(worker_exit_timeout_ms); + if (!workers_exited) { + voice_fill_err(err, err_len, "voice worker exit timeout"); + if (voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) { + voice_set_last_error_locked("worker exit timeout"); + voice_unlock(); + } + ESP_LOGW(TAG, "[stage] session_stop_abort: workers still alive"); + return ESP_ERR_TIMEOUT; + } if (ws != NULL) { ESP_LOGI(TAG, "[stage] ws_stop_destroy_begin"); @@ -380,6 +408,10 @@ esp_err_t voice_interaction_stop(char *err, size_t err_len) { return ESP_OK; } +esp_err_t voice_interaction_stop(char *err, size_t err_len) { + return voice_interaction_stop_with_timeout(1500, err, err_len); +} + esp_err_t voice_interaction_tap_start(char *err, size_t err_len) { ESP_LOGI(TAG, "[stage] tap_start_enter"); if (!voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) { diff --git a/components/platform/include/platform.h b/components/platform/include/platform.h index b60965f..8c2b642 100644 --- a/components/platform/include/platform.h +++ b/components/platform/include/platform.h @@ -61,26 +61,31 @@ typedef struct { const volatile bool *cancel_flag; } platform_direct_print_request_t; -typedef struct { - uint16_t shift_clock_high_us; - uint16_t shift_clock_low_us; - uint16_t latch_pulse_us; - bool strobe_active_high; - bool boost_active_high; - uint16_t override_strobe_on_us; - uint16_t override_strobe_interval_us; - uint16_t override_motor_step_us; - uint8_t override_steps_per_line; -} platform_direct_debug_config_t; - esp_err_t platform_direct_printer_init(void); void platform_direct_printer_deinit(void); esp_err_t platform_direct_printer_connect(uint32_t timeout_ms); void platform_direct_printer_disconnect(void); bool platform_direct_printer_is_connected(void); esp_err_t platform_direct_printer_get_sensors(platform_printer_sensors_t *out_sensors); -esp_err_t platform_direct_printer_get_debug_config(platform_direct_debug_config_t *out_config); -esp_err_t platform_direct_printer_set_debug_config(const platform_direct_debug_config_t *config, +esp_err_t platform_direct_printer_get_debug_config(uint16_t *out_shift_clock_high_us, + uint16_t *out_shift_clock_low_us, + uint16_t *out_latch_pulse_us, + bool *out_strobe_active_high, + bool *out_boost_active_high, + uint16_t *out_override_strobe_on_us, + uint16_t *out_override_strobe_interval_us, + uint16_t *out_override_motor_step_us, + uint8_t *out_override_steps_per_line); +esp_err_t platform_direct_printer_set_debug_config(bool has_config, + uint16_t shift_clock_high_us, + uint16_t shift_clock_low_us, + uint16_t latch_pulse_us, + bool strobe_active_high, + bool boost_active_high, + uint16_t override_strobe_on_us, + uint16_t override_strobe_interval_us, + uint16_t override_motor_step_us, + uint8_t override_steps_per_line, bool reset_defaults, char *err, size_t err_len); @@ -115,11 +120,15 @@ void voice_audio_close(void); bool voice_audio_is_open(void); +// timeout_ms applies to voice-audio mutex acquisition. +// Underlying codec driver I/O duration depends on driver/clocking state. esp_err_t voice_audio_read_pcm(int16_t *pcm, size_t samples, uint32_t timeout_ms, char *err, size_t err_len); +// timeout_ms applies to voice-audio mutex acquisition. +// Underlying codec driver I/O duration depends on driver/clocking state. esp_err_t voice_audio_write_pcm(const int16_t *pcm, size_t samples, uint32_t timeout_ms, diff --git a/components/platform/src/ble_printer_client.c b/components/platform/src/ble_printer_client.c index 246e875..eb3af89 100644 --- a/components/platform/src/ble_printer_client.c +++ b/components/platform/src/ble_printer_client.c @@ -52,6 +52,8 @@ static bool s_scanning; static bool s_notify_ready; static bool s_host_synced; static bool s_initialized; +static bool s_connecting; +static uint32_t s_connect_timeout_ms = 15000; static uint16_t uuid16(const ble_uuid_t *uuid) { if (uuid == NULL || uuid->type != BLE_UUID_TYPE_16) { @@ -397,12 +399,13 @@ static int gap_event_cb(struct ble_gap_event *event, void *arg) { rc = ble_gap_connect(s_addr_type, &s_target_addr, - 30000, + s_connect_timeout_ms > 30000 ? 30000 : s_connect_timeout_ms, &conn_params, gap_event_cb, NULL); if (rc != 0) { ESP_LOGE(TAG, "ble_gap_connect failed rc=%d", rc); + s_connecting = false; signal_failure(); } else { ESP_LOGI(TAG, "Connecting..."); @@ -417,6 +420,11 @@ static int gap_event_cb(struct ble_gap_event *event, void *arg) { case BLE_GAP_EVENT_CONNECT: if (event->connect.status == 0) { + if (!s_connecting) { + (void)ble_gap_terminate(event->connect.conn_handle, BLE_ERR_REM_USER_CONN_TERM); + return 0; + } + s_connecting = false; s_conn_handle = event->connect.conn_handle; xEventGroupSetBits(s_evt_group, EVT_CONNECTED); ESP_LOGI(TAG, "Connected handle=%u", s_conn_handle); @@ -424,6 +432,7 @@ static int gap_event_cb(struct ble_gap_event *event, void *arg) { ble_gattc_exchange_mtu(s_conn_handle, NULL, NULL); start_service_discovery(); } else { + s_connecting = false; ESP_LOGE(TAG, "Connect failed status=%d", event->connect.status); signal_failure(); } @@ -431,6 +440,7 @@ static int gap_event_cb(struct ble_gap_event *event, void *arg) { case BLE_GAP_EVENT_DISCONNECT: ESP_LOGW(TAG, "Disconnected reason=%d", event->disconnect.reason); + s_connecting = false; s_conn_handle = BLE_HS_CONN_HANDLE_NONE; s_notify_ready = false; reset_discovery_state(); @@ -564,6 +574,13 @@ esp_err_t ble_printer_client_connect(const char *target_name, uint32_t timeout_m return ESP_ERR_INVALID_STATE; } + uint32_t effective_timeout_ms = timeout_ms; + if (effective_timeout_ms < 1000) { + effective_timeout_ms = 1000; + } else if (effective_timeout_ms > 60000) { + effective_timeout_ms = 60000; + } + s_match_any_compatible = false; if (target_name != NULL && target_name[0] != '\0') { if (strcmp(target_name, "*") == 0) { @@ -581,9 +598,15 @@ esp_err_t ble_printer_client_connect(const char *target_name, uint32_t timeout_m xSemaphoreGive(s_lock); return ESP_OK; } + if (s_connecting) { + xSemaphoreGive(s_lock); + return ESP_ERR_INVALID_STATE; + } xEventGroupClearBits(s_evt_group, EVT_CONNECTED | EVT_READY | EVT_FAILED); reset_discovery_state(); + s_connect_timeout_ms = effective_timeout_ms; + s_connecting = true; int wait_sync_ms = 3000; while (!s_host_synced && wait_sync_ms > 0) { @@ -602,36 +625,21 @@ esp_err_t ble_printer_client_connect(const char *target_name, uint32_t timeout_m EVT_READY | EVT_FAILED, pdFALSE, pdFALSE, - pdMS_TO_TICKS(timeout_ms)); + pdMS_TO_TICKS(effective_timeout_ms)); if (bits & EVT_READY) { return ESP_OK; } - if (xSemaphoreTake(s_lock, pdMS_TO_TICKS(500)) == pdTRUE) { - stop_scan_if_running(); - xSemaphoreGive(s_lock); - } - - // Similar to Android app behavior: quick retry when initial attempt fails or times out. if (xSemaphoreTake(s_lock, pdMS_TO_TICKS(1000)) == pdTRUE) { - xEventGroupClearBits(s_evt_group, EVT_CONNECTED | EVT_READY | EVT_FAILED); + stop_scan_if_running(); + if (s_conn_handle != BLE_HS_CONN_HANDLE_NONE) { + (void)ble_gap_terminate(s_conn_handle, BLE_ERR_REM_USER_CONN_TERM); + } + s_connecting = false; reset_discovery_state(); - start_scan(); xSemaphoreGive(s_lock); } - bits = xEventGroupWaitBits(s_evt_group, - EVT_READY | EVT_FAILED, - pdFALSE, - pdFALSE, - pdMS_TO_TICKS(timeout_ms)); - if (bits & EVT_READY) { - return ESP_OK; - } - if (xSemaphoreTake(s_lock, pdMS_TO_TICKS(500)) == pdTRUE) { - stop_scan_if_running(); - xSemaphoreGive(s_lock); - } if (bits & EVT_FAILED) { return ESP_FAIL; } @@ -653,6 +661,7 @@ void ble_printer_client_disconnect(void) { ble_gap_terminate(s_conn_handle, BLE_ERR_REM_USER_CONN_TERM); } + s_connecting = false; s_conn_handle = BLE_HS_CONN_HANDLE_NONE; reset_discovery_state(); xSemaphoreGive(s_lock); diff --git a/components/platform/src/direct_thermal_printer.c b/components/platform/src/direct_thermal_printer.c index 06f161d..ad65b50 100644 --- a/components/platform/src/direct_thermal_printer.c +++ b/components/platform/src/direct_thermal_printer.c @@ -49,11 +49,23 @@ typedef struct { adc_channel_t channel; } adc_pin_t; +typedef struct { + uint16_t shift_clock_high_us; + uint16_t shift_clock_low_us; + uint16_t latch_pulse_us; + bool strobe_active_high; + bool boost_active_high; + uint16_t override_strobe_on_us; + uint16_t override_strobe_interval_us; + uint16_t override_motor_step_us; + uint8_t override_steps_per_line; +} direct_debug_config_t; + typedef struct { bool initialized; bool connected; uint8_t motor_phase; - platform_direct_debug_config_t debug; + direct_debug_config_t debug; SemaphoreHandle_t lock; adc_oneshot_unit_handle_t adc1_handle; adc_oneshot_unit_handle_t adc2_handle; @@ -567,8 +579,8 @@ void platform_direct_printer_deinit(void) { } esp_err_t platform_direct_printer_connect(uint32_t timeout_ms) { - (void)timeout_ms; #if !CONFIG_TQ_DIRECT_PRINTER_ENABLE + (void)timeout_ms; return ESP_ERR_NOT_SUPPORTED; #else esp_err_t err = platform_direct_printer_init(); @@ -576,7 +588,12 @@ esp_err_t platform_direct_printer_connect(uint32_t timeout_ms) { return err; } - if (xSemaphoreTake(s_state.lock, pdMS_TO_TICKS(500)) != pdTRUE) { + uint32_t lock_timeout_ms = timeout_ms == 0 ? 500 : timeout_ms; + if (lock_timeout_ms > 5000) { + lock_timeout_ms = 5000; + } + + if (xSemaphoreTake(s_state.lock, pdMS_TO_TICKS(lock_timeout_ms)) != pdTRUE) { return ESP_ERR_TIMEOUT; } @@ -631,12 +648,31 @@ esp_err_t platform_direct_printer_get_sensors(platform_printer_sensors_t *out_se return ESP_OK; } -esp_err_t platform_direct_printer_get_debug_config(platform_direct_debug_config_t *out_config) { +esp_err_t platform_direct_printer_get_debug_config(uint16_t *out_shift_clock_high_us, + uint16_t *out_shift_clock_low_us, + uint16_t *out_latch_pulse_us, + bool *out_strobe_active_high, + bool *out_boost_active_high, + uint16_t *out_override_strobe_on_us, + uint16_t *out_override_strobe_interval_us, + uint16_t *out_override_motor_step_us, + uint8_t *out_override_steps_per_line) { #if !CONFIG_TQ_DIRECT_PRINTER_ENABLE - (void)out_config; + (void)out_shift_clock_high_us; + (void)out_shift_clock_low_us; + (void)out_latch_pulse_us; + (void)out_strobe_active_high; + (void)out_boost_active_high; + (void)out_override_strobe_on_us; + (void)out_override_strobe_interval_us; + (void)out_override_motor_step_us; + (void)out_override_steps_per_line; return ESP_ERR_NOT_SUPPORTED; #else - if (out_config == NULL) { + if (out_shift_clock_high_us == NULL || out_shift_clock_low_us == NULL || out_latch_pulse_us == NULL || + out_strobe_active_high == NULL || out_boost_active_high == NULL || out_override_strobe_on_us == NULL || + out_override_strobe_interval_us == NULL || out_override_motor_step_us == NULL || + out_override_steps_per_line == NULL) { return ESP_ERR_INVALID_ARG; } esp_err_t init_rc = platform_direct_printer_init(); @@ -646,23 +682,49 @@ esp_err_t platform_direct_printer_get_debug_config(platform_direct_debug_config_ if (xSemaphoreTake(s_state.lock, pdMS_TO_TICKS(500)) != pdTRUE) { return ESP_ERR_TIMEOUT; } - *out_config = s_state.debug; + *out_shift_clock_high_us = s_state.debug.shift_clock_high_us; + *out_shift_clock_low_us = s_state.debug.shift_clock_low_us; + *out_latch_pulse_us = s_state.debug.latch_pulse_us; + *out_strobe_active_high = s_state.debug.strobe_active_high; + *out_boost_active_high = s_state.debug.boost_active_high; + *out_override_strobe_on_us = s_state.debug.override_strobe_on_us; + *out_override_strobe_interval_us = s_state.debug.override_strobe_interval_us; + *out_override_motor_step_us = s_state.debug.override_motor_step_us; + *out_override_steps_per_line = s_state.debug.override_steps_per_line; xSemaphoreGive(s_state.lock); return ESP_OK; #endif } -esp_err_t platform_direct_printer_set_debug_config(const platform_direct_debug_config_t *config, +esp_err_t platform_direct_printer_set_debug_config(bool has_config, + uint16_t shift_clock_high_us, + uint16_t shift_clock_low_us, + uint16_t latch_pulse_us, + bool strobe_active_high, + bool boost_active_high, + uint16_t override_strobe_on_us, + uint16_t override_strobe_interval_us, + uint16_t override_motor_step_us, + uint8_t override_steps_per_line, bool reset_defaults, char *err, size_t err_len) { #if !CONFIG_TQ_DIRECT_PRINTER_ENABLE - (void)config; + (void)has_config; + (void)shift_clock_high_us; + (void)shift_clock_low_us; + (void)latch_pulse_us; + (void)strobe_active_high; + (void)boost_active_high; + (void)override_strobe_on_us; + (void)override_strobe_interval_us; + (void)override_motor_step_us; + (void)override_steps_per_line; (void)reset_defaults; write_err(err, err_len, "direct backend disabled"); return ESP_ERR_NOT_SUPPORTED; #else - if (config == NULL && !reset_defaults) { + if (!has_config && !reset_defaults) { write_err(err, err_len, "invalid args"); return ESP_ERR_INVALID_ARG; } @@ -681,45 +743,48 @@ esp_err_t platform_direct_printer_set_debug_config(const platform_direct_debug_c set_debug_defaults_locked(); } - if (config != NULL) { - if (config->shift_clock_high_us < DEBUG_SHIFT_CLOCK_US_MIN || - config->shift_clock_high_us > DEBUG_SHIFT_CLOCK_US_MAX || - config->shift_clock_low_us < DEBUG_SHIFT_CLOCK_US_MIN || - config->shift_clock_low_us > DEBUG_SHIFT_CLOCK_US_MAX) { + if (has_config) { + if (shift_clock_high_us < DEBUG_SHIFT_CLOCK_US_MIN || shift_clock_high_us > DEBUG_SHIFT_CLOCK_US_MAX || + shift_clock_low_us < DEBUG_SHIFT_CLOCK_US_MIN || shift_clock_low_us > DEBUG_SHIFT_CLOCK_US_MAX) { xSemaphoreGive(s_state.lock); write_err(err, err_len, "shift clock us out of range (1..50)"); return ESP_ERR_INVALID_ARG; } - if (config->latch_pulse_us < DEBUG_LATCH_PULSE_US_MIN || - config->latch_pulse_us > DEBUG_LATCH_PULSE_US_MAX) { + if (latch_pulse_us < DEBUG_LATCH_PULSE_US_MIN || latch_pulse_us > DEBUG_LATCH_PULSE_US_MAX) { xSemaphoreGive(s_state.lock); write_err(err, err_len, "latch pulse us out of range (1..50)"); return ESP_ERR_INVALID_ARG; } - if (config->override_strobe_on_us != 0 && - (config->override_strobe_on_us < 100 || config->override_strobe_on_us > 10000)) { + if (override_strobe_on_us != 0 && (override_strobe_on_us < 100 || override_strobe_on_us > 10000)) { xSemaphoreGive(s_state.lock); write_err(err, err_len, "strobe_on_us must be 0 or 100..10000"); return ESP_ERR_INVALID_ARG; } - if (config->override_strobe_interval_us > 10000) { + if (override_strobe_interval_us > 10000) { xSemaphoreGive(s_state.lock); write_err(err, err_len, "strobe_interval_us must be 0..10000"); return ESP_ERR_INVALID_ARG; } - if (config->override_motor_step_us != 0 && - (config->override_motor_step_us < 100 || config->override_motor_step_us > 20000)) { + if (override_motor_step_us != 0 && (override_motor_step_us < 100 || override_motor_step_us > 20000)) { xSemaphoreGive(s_state.lock); write_err(err, err_len, "motor_step_us must be 0 or 100..20000"); return ESP_ERR_INVALID_ARG; } - if (config->override_steps_per_line > 8) { + if (override_steps_per_line > 8) { xSemaphoreGive(s_state.lock); write_err(err, err_len, "steps_per_line must be 0..8"); return ESP_ERR_INVALID_ARG; } - s_state.debug = *config; + s_state.debug.shift_clock_high_us = shift_clock_high_us; + s_state.debug.shift_clock_low_us = shift_clock_low_us; + s_state.debug.latch_pulse_us = latch_pulse_us; + s_state.debug.strobe_active_high = strobe_active_high; + s_state.debug.boost_active_high = boost_active_high; + s_state.debug.override_strobe_on_us = override_strobe_on_us; + s_state.debug.override_strobe_interval_us = override_strobe_interval_us; + s_state.debug.override_motor_step_us = override_motor_step_us; + s_state.debug.override_steps_per_line = override_steps_per_line; } safe_drive_off_locked(s_state.connected); diff --git a/components/platform/src/display_lcd_module.c b/components/platform/src/display_lcd_module.c index 1207b31..c45f097 100644 --- a/components/platform/src/display_lcd_module.c +++ b/components/platform/src/display_lcd_module.c @@ -292,7 +292,9 @@ void lcd_module_deinit(void) { #if CONFIG_TQ_SCREEN_ENABLE if (s_lcd.lock != NULL) { - (void)xSemaphoreTake(s_lcd.lock, portMAX_DELAY); + if (xSemaphoreTake(s_lcd.lock, pdMS_TO_TICKS(1000)) != pdTRUE) { + return; + } } if (s_lcd.lv_disp != NULL) { diff --git a/components/platform/src/wifi_manager.c b/components/platform/src/wifi_manager.c index 089c56f..24f986a 100644 --- a/components/platform/src/wifi_manager.c +++ b/components/platform/src/wifi_manager.c @@ -56,12 +56,18 @@ static void wifi_event_handler(void *arg, (void)event_data; if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + if (!s_started) { + return; + } (void)esp_wifi_connect(); return; } if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { s_ready = false; + if (!s_started) { + return; + } if (s_retry_num < CONFIG_TQ_WIFI_MAXIMUM_RETRY) { (void)esp_wifi_connect(); s_retry_num++; @@ -73,6 +79,9 @@ static void wifi_event_handler(void *arg, } if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + if (!s_started) { + return; + } ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); s_retry_num = 0; @@ -218,6 +227,7 @@ esp_err_t wifi_manager_start(void) { runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_WIFI_CONNECT_FAILED, 1); } runtime_diag_record_error("wifi_connect", err, "STA connect failed"); + (void)wifi_manager_stop(); } return err; }