feat(architecture): enforce lifecycle orchestration and layered component boundaries
This commit is contained in:
10
components/app_composition/CMakeLists.txt
Normal file
10
components/app_composition/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
idf_component_register(
|
||||
SRCS
|
||||
"../../main/app_composition/src/app_main.c"
|
||||
INCLUDE_DIRS
|
||||
"../../main/app_composition/include"
|
||||
PRIV_INCLUDE_DIRS
|
||||
"../../main/app_composition/internal"
|
||||
REQUIRES
|
||||
control_plane
|
||||
)
|
||||
21
components/control_plane/CMakeLists.txt
Normal file
21
components/control_plane/CMakeLists.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
idf_component_register(
|
||||
SRCS
|
||||
"../../main/control_plane/src/controller_lifecycle.c"
|
||||
"../../main/control_plane/src/rest_server.c"
|
||||
"../../main/control_plane/src/rest_server_common.c"
|
||||
"../../main/control_plane/src/rest_server_ops.c"
|
||||
"../../main/control_plane/src/rest_server_print.c"
|
||||
"../../main/control_plane/src/rest_server_print_image.c"
|
||||
"../../main/control_plane/src/rest_server_print_render.c"
|
||||
"../../main/control_plane/src/rest_server_jobs.c"
|
||||
"../../main/control_plane/src/rest_server_voice.c"
|
||||
INCLUDE_DIRS
|
||||
"../../main/control_plane/include"
|
||||
PRIV_INCLUDE_DIRS
|
||||
"../../main/control_plane/internal"
|
||||
REQUIRES
|
||||
domain
|
||||
esp_http_server
|
||||
json
|
||||
mbedtls
|
||||
)
|
||||
35
components/domain/CMakeLists.txt
Normal file
35
components/domain/CMakeLists.txt
Normal file
@@ -0,0 +1,35 @@
|
||||
idf_component_register(
|
||||
SRCS
|
||||
"../../main/domain/src/printer_protocol.c"
|
||||
"../../main/domain/src/printer_protocol_jobs.c"
|
||||
"../../main/domain/src/printer_protocol_worker.c"
|
||||
"../../main/domain/src/printer_protocol_commands.c"
|
||||
"../../main/domain/src/raster_tools.c"
|
||||
"../../main/domain/src/raster_tools_image_qr.c"
|
||||
"../../main/domain/src/image_generation.c"
|
||||
"../../main/domain/src/system_runtime.c"
|
||||
"../../main/domain/src/voice_interaction.c"
|
||||
"../../main/domain/src/voice_interaction_common.c"
|
||||
"../../main/domain/src/voice_interaction_ws.c"
|
||||
"../../main/domain/src/voice_interaction_ws_protocol.c"
|
||||
"../../main/domain/src/voice_interaction_ws_audio.c"
|
||||
"../../main/domain/src/voice_interaction_marker.c"
|
||||
"../../main/domain/src/voice_interaction_tasks.c"
|
||||
"../../main/third_party/qrcodegen.c"
|
||||
INCLUDE_DIRS
|
||||
"../../main/domain/include"
|
||||
"../../main/third_party"
|
||||
PRIV_INCLUDE_DIRS
|
||||
"../../main/domain/internal"
|
||||
REQUIRES
|
||||
platform
|
||||
esp_http_client
|
||||
esp_websocket_client
|
||||
espressif__libpng
|
||||
json
|
||||
mbedtls
|
||||
esp_coex
|
||||
EMBED_FILES
|
||||
"../../main/domain/assets/fonts/cn16_index.bin"
|
||||
"../../main/domain/assets/fonts/cn16_glyphs.bin"
|
||||
)
|
||||
24
components/platform/CMakeLists.txt
Normal file
24
components/platform/CMakeLists.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
idf_component_register(
|
||||
SRCS
|
||||
"../../main/platform/src/platform_bootstrap.c"
|
||||
"../../main/platform/src/wifi_manager.c"
|
||||
"../../main/platform/src/ble_printer_client.c"
|
||||
"../../main/platform/src/voice_audio.c"
|
||||
"../../main/platform/src/runtime_policy.c"
|
||||
"../../main/platform/src/runtime_diagnostics.c"
|
||||
INCLUDE_DIRS
|
||||
"../../main/platform/include"
|
||||
PRIV_INCLUDE_DIRS
|
||||
"../../main/platform/internal"
|
||||
REQUIRES
|
||||
bt
|
||||
esp_coex
|
||||
esp_wifi
|
||||
esp_netif
|
||||
esp_event
|
||||
nvs_flash
|
||||
mbedtls
|
||||
esp_codec_dev
|
||||
esp_audio_codec
|
||||
driver
|
||||
)
|
||||
@@ -1,62 +1,3 @@
|
||||
idf_component_register(
|
||||
SRCS
|
||||
"app_composition/src/app_main.c"
|
||||
"control_plane/src/controller_lifecycle.c"
|
||||
"control_plane/src/rest_server.c"
|
||||
"control_plane/src/rest_server_common.c"
|
||||
"control_plane/src/rest_server_ops.c"
|
||||
"control_plane/src/rest_server_print.c"
|
||||
"control_plane/src/rest_server_print_image.c"
|
||||
"control_plane/src/rest_server_print_render.c"
|
||||
"control_plane/src/rest_server_jobs.c"
|
||||
"control_plane/src/rest_server_voice.c"
|
||||
"domain/src/printer_protocol.c"
|
||||
"domain/src/printer_protocol_jobs.c"
|
||||
"domain/src/printer_protocol_worker.c"
|
||||
"domain/src/printer_protocol_commands.c"
|
||||
"domain/src/raster_tools.c"
|
||||
"domain/src/raster_tools_image_qr.c"
|
||||
"domain/src/image_generation.c"
|
||||
"domain/src/system_runtime.c"
|
||||
"domain/src/voice_interaction.c"
|
||||
"domain/src/voice_interaction_common.c"
|
||||
"domain/src/voice_interaction_ws.c"
|
||||
"domain/src/voice_interaction_ws_protocol.c"
|
||||
"domain/src/voice_interaction_ws_audio.c"
|
||||
"domain/src/voice_interaction_marker.c"
|
||||
"domain/src/voice_interaction_tasks.c"
|
||||
"platform/src/platform_bootstrap.c"
|
||||
"platform/src/wifi_manager.c"
|
||||
"platform/src/ble_printer_client.c"
|
||||
"platform/src/voice_audio.c"
|
||||
"third_party/qrcodegen.c"
|
||||
INCLUDE_DIRS
|
||||
"app_composition/include"
|
||||
"control_plane/include"
|
||||
"domain/include"
|
||||
"platform/include"
|
||||
"third_party"
|
||||
PRIV_INCLUDE_DIRS
|
||||
"app_composition/internal"
|
||||
"control_plane/internal"
|
||||
"domain/internal"
|
||||
"platform/internal"
|
||||
REQUIRES
|
||||
bt
|
||||
esp_coex
|
||||
esp_wifi
|
||||
esp_netif
|
||||
esp_event
|
||||
esp_http_server
|
||||
esp_http_client
|
||||
nvs_flash
|
||||
json
|
||||
mbedtls
|
||||
esp_websocket_client
|
||||
esp_codec_dev
|
||||
esp_audio_codec
|
||||
driver
|
||||
EMBED_FILES
|
||||
"domain/assets/fonts/cn16_index.bin"
|
||||
"domain/assets/fonts/cn16_glyphs.bin"
|
||||
)
|
||||
# Keep `main` as a lightweight component holder for project-wide Kconfig.
|
||||
# Runtime sources are split into layered components under `components/`.
|
||||
idf_component_register()
|
||||
|
||||
@@ -12,10 +12,65 @@ config TQ_WIFI_MAXIMUM_RETRY
|
||||
int "Wi-Fi Maximum Retry"
|
||||
default 10
|
||||
|
||||
config TQ_WIFI_CONNECT_TIMEOUT_MS
|
||||
int "Wi-Fi connect timeout (ms)"
|
||||
range 3000 60000
|
||||
default 15000
|
||||
|
||||
config TQ_LIFECYCLE_START_RETRY_COUNT
|
||||
int "Lifecycle start retry count"
|
||||
range 0 5
|
||||
default 2
|
||||
|
||||
config TQ_LIFECYCLE_RETRY_BACKOFF_MS
|
||||
int "Lifecycle retry base backoff (ms)"
|
||||
range 100 10000
|
||||
default 800
|
||||
|
||||
config TQ_HTTP_PORT
|
||||
int "REST HTTP Port"
|
||||
default 80
|
||||
|
||||
config TQ_REST_PRINTER_CONNECT_TIMEOUT_MS
|
||||
int "REST printer connect timeout default (ms)"
|
||||
range 1000 60000
|
||||
default 15000
|
||||
|
||||
config TQ_REST_LABEL_TIMEOUT_MS
|
||||
int "REST label/offset timeout default (ms)"
|
||||
range 500 30000
|
||||
default 5000
|
||||
|
||||
config TQ_REST_OTA_STEP_TIMEOUT_MS
|
||||
int "REST OTA step timeout default (ms)"
|
||||
range 500 60000
|
||||
default 5000
|
||||
|
||||
config TQ_PRINTER_CONTROL_LOCK_TIMEOUT_MS
|
||||
int "Printer control lane lock timeout (ms)"
|
||||
range 100 5000
|
||||
default 1000
|
||||
|
||||
config TQ_PRINTER_WORKER_QUEUE_WAIT_MS
|
||||
int "Printer worker queue poll wait (ms)"
|
||||
range 50 2000
|
||||
default 250
|
||||
|
||||
config TQ_PRINTER_QUEUE_RETRY_DELAY_MS
|
||||
int "Printer queue retry delay (ms)"
|
||||
range 5 500
|
||||
default 20
|
||||
|
||||
config TQ_PRINTER_STATUS_POLL_INTERVAL_MS
|
||||
int "Printer status poll interval (ms)"
|
||||
range 1000 30000
|
||||
default 5000
|
||||
|
||||
config TQ_PRINTER_STOP_TIMEOUT_MS
|
||||
int "Printer protocol stop timeout (ms)"
|
||||
range 1000 30000
|
||||
default 8000
|
||||
|
||||
config TQ_API_KEY
|
||||
string "REST API Key (optional, empty means disabled)"
|
||||
default ""
|
||||
|
||||
@@ -6,7 +6,11 @@
|
||||
static const char *TAG = "app_main";
|
||||
|
||||
void app_main(void) {
|
||||
ESP_ERROR_CHECK(controller_lifecycle_start());
|
||||
esp_err_t rc = controller_lifecycle_start();
|
||||
if (rc != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Controller lifecycle start failed: %s", esp_err_to_name(rc));
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "TQ controller started");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
typedef enum {
|
||||
CONTROLLER_LIFECYCLE_STATE_STOPPED = 0,
|
||||
CONTROLLER_LIFECYCLE_STATE_STARTING,
|
||||
CONTROLLER_LIFECYCLE_STATE_RUNNING,
|
||||
CONTROLLER_LIFECYCLE_STATE_DEGRADED,
|
||||
CONTROLLER_LIFECYCLE_STATE_STOPPING,
|
||||
} controller_lifecycle_state_t;
|
||||
|
||||
typedef struct {
|
||||
controller_lifecycle_state_t state;
|
||||
esp_err_t last_error;
|
||||
uint32_t start_attempt;
|
||||
int64_t last_transition_ms;
|
||||
char last_stage[32];
|
||||
} controller_lifecycle_status_t;
|
||||
|
||||
esp_err_t controller_lifecycle_start(void);
|
||||
void controller_lifecycle_stop(void);
|
||||
esp_err_t controller_lifecycle_stop(void);
|
||||
void controller_lifecycle_get_status(controller_lifecycle_status_t *out_status);
|
||||
const char *controller_lifecycle_state_str(controller_lifecycle_state_t state);
|
||||
|
||||
@@ -1,36 +1,316 @@
|
||||
#include "controller_lifecycle.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "image_generation.h"
|
||||
#include "printer_protocol.h"
|
||||
#include "rest_server.h"
|
||||
#include "runtime_diagnostics.h"
|
||||
#include "runtime_policy.h"
|
||||
#include "system_runtime.h"
|
||||
#include "voice_interaction.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
static const char *TAG = "controller_lifecycle";
|
||||
static SemaphoreHandle_t s_lock;
|
||||
static controller_lifecycle_status_t s_status = {
|
||||
.state = CONTROLLER_LIFECYCLE_STATE_STOPPED,
|
||||
.last_error = ESP_OK,
|
||||
.start_attempt = 0,
|
||||
.last_transition_ms = 0,
|
||||
.last_stage = "stopped",
|
||||
};
|
||||
|
||||
esp_err_t controller_lifecycle_start(void) {
|
||||
ESP_LOGI(TAG, "Starting system runtime");
|
||||
ESP_ERROR_CHECK(system_runtime_bootstrap());
|
||||
static void lifecycle_lock_init_if_needed(void) {
|
||||
if (s_lock == NULL) {
|
||||
s_lock = xSemaphoreCreateMutex();
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Starting printer protocol");
|
||||
ESP_ERROR_CHECK(printer_protocol_init());
|
||||
static bool lifecycle_lock_take(uint32_t timeout_ms) {
|
||||
lifecycle_lock_init_if_needed();
|
||||
return s_lock != NULL && xSemaphoreTake(s_lock, pdMS_TO_TICKS(timeout_ms)) == pdTRUE;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Starting voice interaction");
|
||||
ESP_ERROR_CHECK(voice_interaction_init());
|
||||
static void lifecycle_set_state_locked(controller_lifecycle_state_t state,
|
||||
esp_err_t last_error,
|
||||
const char *stage) {
|
||||
s_status.state = 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);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Scheduling HTTPS prewarm");
|
||||
image_generation_schedule_prewarm();
|
||||
const char *controller_lifecycle_state_str(controller_lifecycle_state_t state) {
|
||||
switch (state) {
|
||||
case CONTROLLER_LIFECYCLE_STATE_STOPPED:
|
||||
return "stopped";
|
||||
case CONTROLLER_LIFECYCLE_STATE_STARTING:
|
||||
return "starting";
|
||||
case CONTROLLER_LIFECYCLE_STATE_RUNNING:
|
||||
return "running";
|
||||
case CONTROLLER_LIFECYCLE_STATE_DEGRADED:
|
||||
return "degraded";
|
||||
case CONTROLLER_LIFECYCLE_STATE_STOPPING:
|
||||
return "stopping";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Starting REST server");
|
||||
ESP_ERROR_CHECK(rest_server_start());
|
||||
static esp_err_t lifecycle_start_system_runtime(void) {
|
||||
return system_runtime_start();
|
||||
}
|
||||
|
||||
static esp_err_t lifecycle_start_printer_protocol(void) {
|
||||
return printer_protocol_init();
|
||||
}
|
||||
|
||||
static esp_err_t lifecycle_start_voice(void) {
|
||||
return voice_interaction_init();
|
||||
}
|
||||
|
||||
static esp_err_t lifecycle_start_rest_server(void) {
|
||||
return rest_server_start();
|
||||
}
|
||||
|
||||
static esp_err_t lifecycle_stop_rest_server(void) {
|
||||
rest_server_stop();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void controller_lifecycle_stop(void) {
|
||||
rest_server_stop();
|
||||
(void)voice_interaction_stop(NULL, 0);
|
||||
printer_protocol_disconnect();
|
||||
ESP_LOGI(TAG, "Controller lifecycle stopped");
|
||||
static esp_err_t lifecycle_stop_voice(void) {
|
||||
esp_err_t rc = voice_interaction_stop(NULL, 0);
|
||||
if (rc == ESP_OK || rc == ESP_ERR_INVALID_STATE) {
|
||||
return ESP_OK;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static esp_err_t lifecycle_stop_printer_protocol(void) {
|
||||
esp_err_t rc = printer_protocol_stop(runtime_policy_printer_stop_timeout_ms());
|
||||
if (rc == ESP_OK || rc == ESP_ERR_INVALID_STATE) {
|
||||
return ESP_OK;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static esp_err_t lifecycle_stop_system_runtime(void) {
|
||||
return system_runtime_shutdown();
|
||||
}
|
||||
|
||||
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 max_attempts = retry_count + 1;
|
||||
|
||||
for (uint32_t attempt = 0; attempt < max_attempts; ++attempt) {
|
||||
esp_err_t rc = fn();
|
||||
if (rc == ESP_OK) {
|
||||
if (attempt > 0) {
|
||||
ESP_LOGW(TAG, "stage %s recovered after retries, attempt=%u", stage, (unsigned)(attempt + 1));
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool can_retry = (attempt + 1 < max_attempts) && runtime_policy_is_retryable_error(rc);
|
||||
ESP_LOGW(TAG,
|
||||
"stage %s failed, rc=0x%x, attempt=%u/%u, retry=%d",
|
||||
stage,
|
||||
(unsigned)rc,
|
||||
(unsigned)(attempt + 1),
|
||||
(unsigned)max_attempts,
|
||||
can_retry ? 1 : 0);
|
||||
|
||||
if (!can_retry) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_LIFECYCLE_START_RETRY, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(runtime_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);
|
||||
|
||||
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");
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (s_status.state == CONTROLLER_LIFECYCLE_STATE_RUNNING) {
|
||||
xSemaphoreGive(s_lock);
|
||||
return ESP_OK;
|
||||
}
|
||||
if (s_status.state == CONTROLLER_LIFECYCLE_STATE_STARTING ||
|
||||
s_status.state == CONTROLLER_LIFECYCLE_STATE_STOPPING) {
|
||||
xSemaphoreGive(s_lock);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
s_status.start_attempt++;
|
||||
lifecycle_set_state_locked(CONTROLLER_LIFECYCLE_STATE_STARTING, ESP_OK, "bootstrap");
|
||||
xSemaphoreGive(s_lock);
|
||||
|
||||
bool system_started = false;
|
||||
bool printer_started = false;
|
||||
bool voice_started = false;
|
||||
bool rest_started = false;
|
||||
const char *failed_stage = "unknown";
|
||||
|
||||
failed_stage = "system_runtime";
|
||||
esp_err_t rc = lifecycle_run_step_with_retry("system_runtime", lifecycle_start_system_runtime);
|
||||
if (rc != ESP_OK) {
|
||||
goto start_failed;
|
||||
}
|
||||
system_started = true;
|
||||
|
||||
failed_stage = "printer_protocol";
|
||||
rc = lifecycle_run_step_with_retry("printer_protocol", lifecycle_start_printer_protocol);
|
||||
if (rc != ESP_OK) {
|
||||
goto start_failed;
|
||||
}
|
||||
printer_started = true;
|
||||
|
||||
failed_stage = "voice_interaction";
|
||||
rc = lifecycle_run_step_with_retry("voice_interaction", lifecycle_start_voice);
|
||||
if (rc != ESP_OK) {
|
||||
goto start_failed;
|
||||
}
|
||||
voice_started = true;
|
||||
|
||||
failed_stage = "rest_server";
|
||||
rc = lifecycle_run_step_with_retry("rest_server", lifecycle_start_rest_server);
|
||||
if (rc != ESP_OK) {
|
||||
goto start_failed;
|
||||
}
|
||||
rest_started = true;
|
||||
|
||||
image_generation_schedule_prewarm();
|
||||
|
||||
if (lifecycle_lock_take(1000)) {
|
||||
lifecycle_set_state_locked(CONTROLLER_LIFECYCLE_STATE_RUNNING, ESP_OK, "running");
|
||||
xSemaphoreGive(s_lock);
|
||||
}
|
||||
|
||||
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_LIFECYCLE_START_SUCCESS, 1);
|
||||
ESP_LOGI(TAG, "controller lifecycle started");
|
||||
return ESP_OK;
|
||||
|
||||
start_failed:
|
||||
if (rest_started) {
|
||||
(void)lifecycle_stop_rest_server();
|
||||
}
|
||||
if (voice_started) {
|
||||
(void)lifecycle_stop_voice();
|
||||
}
|
||||
if (printer_started) {
|
||||
(void)lifecycle_stop_printer_protocol();
|
||||
}
|
||||
if (system_started) {
|
||||
(void)lifecycle_stop_system_runtime();
|
||||
}
|
||||
|
||||
if (lifecycle_lock_take(1000)) {
|
||||
lifecycle_set_state_locked(CONTROLLER_LIFECYCLE_STATE_DEGRADED, rc, failed_stage);
|
||||
xSemaphoreGive(s_lock);
|
||||
}
|
||||
|
||||
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_LIFECYCLE_START_FAILED, 1);
|
||||
runtime_diag_record_error("lifecycle_start", rc, failed_stage);
|
||||
ESP_LOGE(TAG,
|
||||
"controller lifecycle start failed at stage=%s, rc=0x%x",
|
||||
failed_stage,
|
||||
(unsigned)rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
esp_err_t controller_lifecycle_stop(void) {
|
||||
runtime_diag_counter_add(RUNTIME_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");
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (s_status.state == CONTROLLER_LIFECYCLE_STATE_STOPPED) {
|
||||
xSemaphoreGive(s_lock);
|
||||
return ESP_OK;
|
||||
}
|
||||
if (s_status.state == CONTROLLER_LIFECYCLE_STATE_STARTING ||
|
||||
s_status.state == CONTROLLER_LIFECYCLE_STATE_STOPPING) {
|
||||
xSemaphoreGive(s_lock);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
lifecycle_set_state_locked(CONTROLLER_LIFECYCLE_STATE_STOPPING, ESP_OK, "stopping");
|
||||
xSemaphoreGive(s_lock);
|
||||
|
||||
esp_err_t first_err = ESP_OK;
|
||||
|
||||
esp_err_t rc = lifecycle_stop_rest_server();
|
||||
if (rc != ESP_OK && first_err == ESP_OK) {
|
||||
first_err = rc;
|
||||
}
|
||||
|
||||
rc = lifecycle_stop_voice();
|
||||
if (rc != ESP_OK && first_err == ESP_OK) {
|
||||
first_err = rc;
|
||||
}
|
||||
|
||||
rc = lifecycle_stop_printer_protocol();
|
||||
if (rc != ESP_OK && first_err == ESP_OK) {
|
||||
first_err = rc;
|
||||
}
|
||||
|
||||
rc = lifecycle_stop_system_runtime();
|
||||
if (rc != ESP_OK && first_err == ESP_OK) {
|
||||
first_err = rc;
|
||||
}
|
||||
|
||||
if (lifecycle_lock_take(1000)) {
|
||||
if (first_err == ESP_OK) {
|
||||
lifecycle_set_state_locked(CONTROLLER_LIFECYCLE_STATE_STOPPED, ESP_OK, "stopped");
|
||||
} else {
|
||||
lifecycle_set_state_locked(CONTROLLER_LIFECYCLE_STATE_DEGRADED, first_err, "stop_failed");
|
||||
}
|
||||
xSemaphoreGive(s_lock);
|
||||
}
|
||||
|
||||
if (first_err == ESP_OK) {
|
||||
runtime_diag_counter_add(RUNTIME_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");
|
||||
ESP_LOGE(TAG, "controller lifecycle stop failed, rc=0x%x", (unsigned)first_err);
|
||||
}
|
||||
|
||||
return first_err;
|
||||
}
|
||||
|
||||
void controller_lifecycle_get_status(controller_lifecycle_status_t *out_status) {
|
||||
if (out_status == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(out_status, 0, sizeof(*out_status));
|
||||
|
||||
if (!lifecycle_lock_take(200)) {
|
||||
return;
|
||||
}
|
||||
|
||||
*out_status = s_status;
|
||||
xSemaphoreGive(s_lock);
|
||||
}
|
||||
|
||||
@@ -365,6 +365,10 @@ static esp_err_t favicon_get(httpd_req_t *req) {
|
||||
}
|
||||
|
||||
esp_err_t rest_server_start(void) {
|
||||
if (s_server != NULL) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
config.server_port = CONFIG_TQ_HTTP_PORT;
|
||||
config.uri_match_fn = httpd_uri_match_wildcard;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "esp_heap_caps.h"
|
||||
#include "mbedtls/base64.h"
|
||||
#include "runtime_diagnostics.h"
|
||||
|
||||
#define REST_SERVER_MAX_BODY_BYTES (4 * 1024 * 1024)
|
||||
|
||||
@@ -49,6 +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);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -58,6 +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");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,49 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "controller_lifecycle.h"
|
||||
#include "printer_protocol.h"
|
||||
#include "runtime_diagnostics.h"
|
||||
#include "runtime_policy.h"
|
||||
#include "system_runtime.h"
|
||||
#include "voice_interaction.h"
|
||||
|
||||
static void fill_runtime_diag_json(cJSON *root) {
|
||||
runtime_diag_snapshot_t snapshot = {0};
|
||||
runtime_diag_get_snapshot(&snapshot);
|
||||
|
||||
cJSON *diag = cJSON_AddObjectToObject(root, "diagnostics");
|
||||
if (diag == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON *counters = cJSON_AddObjectToObject(diag, "counters");
|
||||
if (counters != NULL) {
|
||||
for (int i = 0; i < RUNTIME_DIAG_COUNTER_MAX; ++i) {
|
||||
cJSON_AddNumberToObject(counters,
|
||||
runtime_diag_counter_name((runtime_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) {
|
||||
cJSON_AddNumberToObject(gauges,
|
||||
runtime_diag_gauge_name((runtime_diag_gauge_t)i),
|
||||
snapshot.gauges[i]);
|
||||
}
|
||||
}
|
||||
|
||||
cJSON *last_error = cJSON_AddObjectToObject(diag, "last_error");
|
||||
if (last_error != NULL) {
|
||||
cJSON_AddNumberToObject(last_error, "ts_ms", (double)snapshot.last_error_ms);
|
||||
cJSON_AddNumberToObject(last_error, "code", (double)snapshot.last_error_code);
|
||||
cJSON_AddStringToObject(last_error, "source", snapshot.last_error_source);
|
||||
cJSON_AddStringToObject(last_error, "message", snapshot.last_error_message);
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_runtime_json(cJSON *root) {
|
||||
char ip[32] = {0};
|
||||
system_runtime_get_ip(ip, sizeof(ip));
|
||||
@@ -44,6 +84,16 @@ static void fill_runtime_json(cJSON *root) {
|
||||
cJSON_AddNumberToObject(root, "voice_last_event_ms", (double)voice.last_event_ms);
|
||||
cJSON_AddNumberToObject(root, "voice_upstream_packets", (double)voice.upstream_packets);
|
||||
cJSON_AddNumberToObject(root, "voice_downstream_packets", (double)voice.downstream_packets);
|
||||
|
||||
controller_lifecycle_status_t lifecycle = {0};
|
||||
controller_lifecycle_get_status(&lifecycle);
|
||||
cJSON_AddStringToObject(root, "lifecycle_state", controller_lifecycle_state_str(lifecycle.state));
|
||||
cJSON_AddStringToObject(root, "lifecycle_stage", lifecycle.last_stage);
|
||||
cJSON_AddNumberToObject(root, "lifecycle_last_error", (double)lifecycle.last_error);
|
||||
cJSON_AddNumberToObject(root, "lifecycle_start_attempt", (double)lifecycle.start_attempt);
|
||||
cJSON_AddNumberToObject(root, "lifecycle_last_transition_ms", (double)lifecycle.last_transition_ms);
|
||||
|
||||
fill_runtime_diag_json(root);
|
||||
}
|
||||
|
||||
esp_err_t rest_server_health_get(httpd_req_t *req) {
|
||||
@@ -52,10 +102,13 @@ esp_err_t rest_server_health_get(httpd_req_t *req) {
|
||||
}
|
||||
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON_AddBoolToObject(root, "ok", true);
|
||||
controller_lifecycle_status_t lifecycle = {0};
|
||||
controller_lifecycle_get_status(&lifecycle);
|
||||
bool healthy = (lifecycle.state == CONTROLLER_LIFECYCLE_STATE_RUNNING);
|
||||
cJSON_AddBoolToObject(root, "ok", healthy);
|
||||
fill_runtime_json(root);
|
||||
|
||||
esp_err_t err = rest_server_send_json(req, "200 OK", root);
|
||||
esp_err_t err = rest_server_send_json(req, healthy ? "200 OK" : "503 Service Unavailable", root);
|
||||
cJSON_Delete(root);
|
||||
return err;
|
||||
}
|
||||
@@ -67,7 +120,7 @@ esp_err_t rest_server_connect_post(httpd_req_t *req) {
|
||||
|
||||
char *body = NULL;
|
||||
char name[32] = "TQPrinter";
|
||||
uint32_t timeout_ms = 15000;
|
||||
uint32_t timeout_ms = runtime_policy_rest_printer_connect_timeout_ms();
|
||||
|
||||
if (req->content_len > 0) {
|
||||
esp_err_t body_err = rest_server_read_body(req, &body);
|
||||
@@ -148,7 +201,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 = 5000;
|
||||
uint32_t timeout_ms = runtime_policy_rest_label_timeout_ms();
|
||||
if (req->content_len > 0) {
|
||||
char *body = NULL;
|
||||
esp_err_t body_err = rest_server_read_body(req, &body);
|
||||
@@ -189,7 +242,10 @@ 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, 3000, cmd_err, sizeof(cmd_err));
|
||||
esp_err_t rc = printer_protocol_get_label_offset(&offset,
|
||||
runtime_policy_rest_label_timeout_ms(),
|
||||
cmd_err,
|
||||
sizeof(cmd_err));
|
||||
if (rc != ESP_OK) {
|
||||
return rest_server_send_error(req, "409 Conflict", cmd_err[0] != '\0' ? cmd_err : "get offset failed");
|
||||
}
|
||||
@@ -230,7 +286,10 @@ esp_err_t rest_server_label_offset_post(httpd_req_t *req) {
|
||||
cJSON_Delete(json);
|
||||
|
||||
char cmd_err[128] = {0};
|
||||
esp_err_t rc = printer_protocol_set_label_offset(value, 3000, cmd_err, sizeof(cmd_err));
|
||||
esp_err_t rc = printer_protocol_set_label_offset(value,
|
||||
runtime_policy_rest_label_timeout_ms(),
|
||||
cmd_err,
|
||||
sizeof(cmd_err));
|
||||
if (rc != ESP_OK) {
|
||||
return rest_server_send_error(req, "409 Conflict", cmd_err[0] != '\0' ? cmd_err : "set offset failed");
|
||||
}
|
||||
@@ -251,7 +310,10 @@ 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, 4000, cmd_err, sizeof(cmd_err));
|
||||
esp_err_t rc = printer_protocol_ota_get_version(&version,
|
||||
runtime_policy_rest_ota_timeout_ms(),
|
||||
cmd_err,
|
||||
sizeof(cmd_err));
|
||||
if (rc != ESP_OK) {
|
||||
return rest_server_send_error(req, "409 Conflict", cmd_err[0] != '\0' ? cmd_err : "get version failed");
|
||||
}
|
||||
@@ -275,7 +337,9 @@ 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(5000, cmd_err, sizeof(cmd_err));
|
||||
esp_err_t rc = printer_protocol_ota_jump_boot(runtime_policy_rest_ota_timeout_ms(),
|
||||
cmd_err,
|
||||
sizeof(cmd_err));
|
||||
if (rc != ESP_OK) {
|
||||
return rest_server_send_error(req, "409 Conflict", cmd_err[0] != '\0' ? cmd_err : "jump boot failed");
|
||||
}
|
||||
@@ -294,7 +358,9 @@ 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(5000, cmd_err, sizeof(cmd_err));
|
||||
esp_err_t rc = printer_protocol_ota_jump_app(runtime_policy_rest_ota_timeout_ms(),
|
||||
cmd_err,
|
||||
sizeof(cmd_err));
|
||||
if (rc != ESP_OK) {
|
||||
return rest_server_send_error(req, "409 Conflict", cmd_err[0] != '\0' ? cmd_err : "jump app failed");
|
||||
}
|
||||
@@ -330,7 +396,7 @@ 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 = 5000;
|
||||
uint32_t timeout_ms = runtime_policy_rest_ota_timeout_ms();
|
||||
if (cJSON_IsNumber(jtimeout) && jtimeout->valuedouble > 0 && jtimeout->valuedouble <= 30000) {
|
||||
timeout_ms = (uint32_t)jtimeout->valuedouble;
|
||||
}
|
||||
@@ -379,7 +445,7 @@ 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 = 4000;
|
||||
uint32_t timeout_ms = runtime_policy_rest_ota_timeout_ms();
|
||||
if (cJSON_IsNumber(jtimeout) && jtimeout->valuedouble > 0 && jtimeout->valuedouble <= 30000) {
|
||||
timeout_ms = (uint32_t)jtimeout->valuedouble;
|
||||
}
|
||||
@@ -451,7 +517,7 @@ esp_err_t rest_server_ota_upgrade_post(httpd_req_t *req) {
|
||||
|
||||
uint16_t page_size = 1024;
|
||||
uint16_t packet_size = 236;
|
||||
uint32_t timeout_ms = 5000;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "image_generation.h"
|
||||
#include "printer_protocol.h"
|
||||
#include "raster_tools.h"
|
||||
#include "runtime_policy.h"
|
||||
|
||||
static const char *TAG = "rest_print";
|
||||
|
||||
@@ -229,8 +230,8 @@ static bool parse_image_generation_options(cJSON *json,
|
||||
}
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->timeout_ms = CONFIG_TQ_Z_IMAGE_TIMEOUT_MS;
|
||||
out->fetch_timeout_ms = CONFIG_TQ_Z_IMAGE_DOWNLOAD_TIMEOUT_MS;
|
||||
out->timeout_ms = runtime_policy_image_generation_timeout_default_ms();
|
||||
out->fetch_timeout_ms = runtime_policy_image_download_timeout_default_ms();
|
||||
|
||||
cJSON *jprompt = cJSON_GetObjectItemCaseSensitive(json, "prompt");
|
||||
if (!cJSON_IsString(jprompt) || jprompt->valuestring == NULL || jprompt->valuestring[0] == '\0') {
|
||||
@@ -289,9 +290,14 @@ static bool parse_image_generation_options(cJSON *json,
|
||||
|
||||
cJSON *jtimeout = cJSON_GetObjectItemCaseSensitive(json, "timeout_ms");
|
||||
if (cJSON_IsNumber(jtimeout)) {
|
||||
if (jtimeout->valuedouble < 5000 || jtimeout->valuedouble > 180000) {
|
||||
if (jtimeout->valuedouble < runtime_policy_image_generation_timeout_min_ms() ||
|
||||
jtimeout->valuedouble > runtime_policy_image_generation_timeout_max_ms()) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "timeout_ms must be 5000..180000");
|
||||
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());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -300,9 +306,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 < 2000 || jfetch_timeout->valuedouble > 120000) {
|
||||
if (jfetch_timeout->valuedouble < runtime_policy_image_download_timeout_min_ms() ||
|
||||
jfetch_timeout->valuedouble > runtime_policy_image_download_timeout_max_ms()) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "fetch_timeout_ms must be 2000..120000");
|
||||
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());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -411,7 +422,7 @@ static esp_err_t rest_server_print_image_generate(httpd_req_t *req) {
|
||||
image_generation_result_t *gen_result = NULL;
|
||||
uint8_t *raster = NULL;
|
||||
char request_id[80] = {0};
|
||||
bool status_poll_paused = false;
|
||||
printer_status_poll_pause_token_t status_poll_pause_token = 0;
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
|
||||
esp_err_t body_err = rest_server_read_body(req, &body);
|
||||
@@ -475,14 +486,12 @@ static esp_err_t rest_server_print_image_generate(httpd_req_t *req) {
|
||||
image_generation_result_reset(gen_result);
|
||||
|
||||
char model_err[160] = {0};
|
||||
printer_protocol_set_status_poll_paused(true);
|
||||
status_poll_paused = true;
|
||||
status_poll_pause_token = printer_protocol_status_poll_pause_acquire();
|
||||
esp_err_t gen_rc = image_generation_generate_png(&gen_req,
|
||||
gen_result,
|
||||
model_err,
|
||||
sizeof(model_err));
|
||||
printer_protocol_set_status_poll_paused(false);
|
||||
status_poll_paused = false;
|
||||
printer_protocol_status_poll_pause_release(&status_poll_pause_token);
|
||||
if (gen_rc != ESP_OK) {
|
||||
ESP_LOGW(TAG,
|
||||
"image generate failed, rc=0x%x, msg=%s",
|
||||
@@ -551,9 +560,7 @@ static esp_err_t rest_server_print_image_generate(httpd_req_t *req) {
|
||||
request_id);
|
||||
|
||||
cleanup:
|
||||
if (status_poll_paused) {
|
||||
printer_protocol_set_status_poll_paused(false);
|
||||
}
|
||||
printer_protocol_status_poll_pause_release(&status_poll_pause_token);
|
||||
if (body != NULL) {
|
||||
free(body);
|
||||
}
|
||||
|
||||
@@ -47,13 +47,17 @@ typedef struct {
|
||||
int64_t last_status_ms;
|
||||
} printer_runtime_status_t;
|
||||
|
||||
typedef uint32_t printer_status_poll_pause_token_t;
|
||||
|
||||
esp_err_t printer_protocol_init(void);
|
||||
esp_err_t printer_protocol_stop(uint32_t timeout_ms);
|
||||
|
||||
esp_err_t printer_protocol_connect(const char *target_name, uint32_t timeout_ms);
|
||||
void printer_protocol_disconnect(void);
|
||||
|
||||
void printer_protocol_get_runtime_status(printer_runtime_status_t *out_status);
|
||||
void printer_protocol_set_status_poll_paused(bool paused);
|
||||
printer_status_poll_pause_token_t printer_protocol_status_poll_pause_acquire(void);
|
||||
void printer_protocol_status_poll_pause_release(printer_status_poll_pause_token_t *token);
|
||||
|
||||
esp_err_t printer_protocol_submit_raster_job(const uint8_t *raster,
|
||||
size_t raster_len,
|
||||
|
||||
61
main/domain/include/runtime_diagnostics.h
Normal file
61
main/domain/include/runtime_diagnostics.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
RUNTIME_DIAG_COUNTER_LIFECYCLE_START_ATTEMPT = 0,
|
||||
RUNTIME_DIAG_COUNTER_LIFECYCLE_START_SUCCESS,
|
||||
RUNTIME_DIAG_COUNTER_LIFECYCLE_START_FAILED,
|
||||
RUNTIME_DIAG_COUNTER_LIFECYCLE_START_RETRY,
|
||||
RUNTIME_DIAG_COUNTER_LIFECYCLE_STOP_ATTEMPT,
|
||||
RUNTIME_DIAG_COUNTER_LIFECYCLE_STOP_SUCCESS,
|
||||
RUNTIME_DIAG_COUNTER_LIFECYCLE_STOP_FAILED,
|
||||
RUNTIME_DIAG_COUNTER_WIFI_CONNECT_SUCCESS,
|
||||
RUNTIME_DIAG_COUNTER_WIFI_CONNECT_FAILED,
|
||||
RUNTIME_DIAG_COUNTER_WIFI_CONNECT_TIMEOUT,
|
||||
RUNTIME_DIAG_COUNTER_PRINTER_JOB_SUBMITTED,
|
||||
RUNTIME_DIAG_COUNTER_PRINTER_JOB_SUCCESS,
|
||||
RUNTIME_DIAG_COUNTER_PRINTER_JOB_FAILED,
|
||||
RUNTIME_DIAG_COUNTER_PRINTER_JOB_CANCELED,
|
||||
RUNTIME_DIAG_COUNTER_IMAGE_GENERATE_ATTEMPT,
|
||||
RUNTIME_DIAG_COUNTER_IMAGE_GENERATE_SUCCESS,
|
||||
RUNTIME_DIAG_COUNTER_IMAGE_GENERATE_FAILED,
|
||||
RUNTIME_DIAG_COUNTER_IMAGE_GENERATE_TIMEOUT,
|
||||
RUNTIME_DIAG_COUNTER_REST_RESPONSES_TOTAL,
|
||||
RUNTIME_DIAG_COUNTER_REST_ERRORS_TOTAL,
|
||||
RUNTIME_DIAG_COUNTER_MAX,
|
||||
} runtime_diag_counter_t;
|
||||
|
||||
typedef enum {
|
||||
RUNTIME_DIAG_GAUGE_LIFECYCLE_STATE = 0,
|
||||
RUNTIME_DIAG_GAUGE_STATUS_POLL_PAUSE_DEPTH,
|
||||
RUNTIME_DIAG_GAUGE_PRINTER_QUEUE_DEPTH,
|
||||
RUNTIME_DIAG_GAUGE_MAX,
|
||||
} runtime_diag_gauge_t;
|
||||
|
||||
typedef struct {
|
||||
uint64_t counters[RUNTIME_DIAG_COUNTER_MAX];
|
||||
int32_t gauges[RUNTIME_DIAG_GAUGE_MAX];
|
||||
int64_t last_error_ms;
|
||||
esp_err_t last_error_code;
|
||||
char last_error_source[32];
|
||||
char last_error_message[96];
|
||||
} runtime_diag_snapshot_t;
|
||||
|
||||
void runtime_diag_counter_add(runtime_diag_counter_t counter, uint32_t delta);
|
||||
void runtime_diag_set_gauge(runtime_diag_gauge_t gauge, int32_t value);
|
||||
void runtime_diag_record_error(const char *source, esp_err_t code, const char *message);
|
||||
void runtime_diag_get_snapshot(runtime_diag_snapshot_t *out_snapshot);
|
||||
|
||||
const char *runtime_diag_counter_name(runtime_diag_counter_t counter);
|
||||
const char *runtime_diag_gauge_name(runtime_diag_gauge_t gauge);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
30
main/domain/include/runtime_policy.h
Normal file
30
main/domain/include/runtime_policy.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
uint32_t runtime_policy_wifi_connect_timeout_ms(void);
|
||||
|
||||
uint32_t runtime_policy_lifecycle_start_retry_count(void);
|
||||
uint32_t runtime_policy_lifecycle_retry_backoff_ms(uint32_t attempt);
|
||||
bool runtime_policy_is_retryable_error(esp_err_t err);
|
||||
|
||||
uint32_t runtime_policy_printer_control_lock_timeout_ms(void);
|
||||
uint32_t runtime_policy_printer_worker_queue_wait_ms(void);
|
||||
uint32_t runtime_policy_printer_queue_retry_delay_ms(void);
|
||||
uint32_t runtime_policy_printer_status_poll_interval_ms(void);
|
||||
uint32_t runtime_policy_printer_stop_timeout_ms(void);
|
||||
|
||||
uint32_t runtime_policy_rest_printer_connect_timeout_ms(void);
|
||||
uint32_t runtime_policy_rest_label_timeout_ms(void);
|
||||
uint32_t runtime_policy_rest_ota_timeout_ms(void);
|
||||
|
||||
uint32_t runtime_policy_image_generation_timeout_default_ms(void);
|
||||
uint32_t runtime_policy_image_generation_timeout_min_ms(void);
|
||||
uint32_t runtime_policy_image_generation_timeout_max_ms(void);
|
||||
|
||||
uint32_t runtime_policy_image_download_timeout_default_ms(void);
|
||||
uint32_t runtime_policy_image_download_timeout_min_ms(void);
|
||||
uint32_t runtime_policy_image_download_timeout_max_ms(void);
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
esp_err_t system_runtime_start(void);
|
||||
esp_err_t system_runtime_shutdown(void);
|
||||
esp_err_t system_runtime_bootstrap(void);
|
||||
|
||||
bool system_runtime_wifi_ready(void);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "freertos/event_groups.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#define PROTO_ADDR 0x01
|
||||
|
||||
@@ -28,10 +29,15 @@
|
||||
#define CMD_BOOT_GET_VERSION 0xA4
|
||||
|
||||
#define EVT_ACK BIT0
|
||||
#define EVT_SHUTDOWN BIT1
|
||||
#define EVT_WORKER_EXITED BIT2
|
||||
#define EVT_STATUS_POLL_EXITED BIT3
|
||||
|
||||
#define JOB_QUEUE_LEN 8
|
||||
#define JOB_SLOT_MAX 16
|
||||
#define MAX_RASTER_BYTES (384 * 3000 / 8)
|
||||
#define STATUS_POLL_PAUSE_SLOT_MAX 8
|
||||
#define PRINTER_JOB_SENTINEL_STOP 0u
|
||||
|
||||
#define OTA_MAX_DATA_PER_FRAME 236u
|
||||
|
||||
@@ -67,8 +73,14 @@ extern parsed_status_t s_status;
|
||||
extern uint8_t s_last_rsp_cmd;
|
||||
extern uint16_t s_last_rsp_payload_len;
|
||||
extern uint8_t s_last_rsp_payload[252];
|
||||
extern uint32_t s_status_poll_pause_next_token;
|
||||
extern uint32_t s_status_poll_pause_tokens[STATUS_POLL_PAUSE_SLOT_MAX];
|
||||
extern uint32_t s_next_job_id;
|
||||
extern job_slot_t s_jobs[JOB_SLOT_MAX];
|
||||
extern TaskHandle_t s_worker_task;
|
||||
extern TaskHandle_t s_status_poll_task_handle;
|
||||
extern bool s_protocol_initialized;
|
||||
extern bool s_protocol_stopping;
|
||||
|
||||
bool printer_protocol_acquire_control_lane(uint32_t timeout_ms, char *err, size_t err_len);
|
||||
void printer_protocol_release_control_lane(void);
|
||||
@@ -103,3 +115,4 @@ int printer_protocol_alloc_job_slot_locked(void);
|
||||
bool printer_protocol_is_terminal_state(print_job_state_t state);
|
||||
|
||||
void printer_protocol_worker_task(void *arg);
|
||||
bool printer_protocol_is_stopping(void);
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "esp_http_client.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "runtime_diagnostics.h"
|
||||
#include "runtime_policy.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
@@ -881,8 +883,11 @@ esp_err_t image_generation_generate_png(const image_generation_request_t *req,
|
||||
image_generation_result_t *out_result,
|
||||
char *err,
|
||||
size_t err_len) {
|
||||
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_IMAGE_GENERATE_ATTEMPT, 1);
|
||||
|
||||
if (req == NULL || out_result == NULL || req->prompt == NULL || req->prompt[0] == '\0') {
|
||||
image_generation_fill_err(err, err_len, "prompt is required");
|
||||
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_IMAGE_GENERATE_FAILED, 1);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
@@ -890,10 +895,10 @@ esp_err_t image_generation_generate_png(const image_generation_request_t *req,
|
||||
|
||||
image_generation_request_t actual = *req;
|
||||
if (actual.generation_timeout_ms == 0) {
|
||||
actual.generation_timeout_ms = CONFIG_TQ_Z_IMAGE_TIMEOUT_MS;
|
||||
actual.generation_timeout_ms = runtime_policy_image_generation_timeout_default_ms();
|
||||
}
|
||||
if (actual.download_timeout_ms == 0) {
|
||||
actual.download_timeout_ms = CONFIG_TQ_Z_IMAGE_DOWNLOAD_TIMEOUT_MS;
|
||||
actual.download_timeout_ms = runtime_policy_image_download_timeout_default_ms();
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG,
|
||||
@@ -912,6 +917,12 @@ esp_err_t image_generation_generate_png(const image_generation_request_t *req,
|
||||
"image generate stage failed, rc=0x%x, msg=%s",
|
||||
(unsigned)gen_rc,
|
||||
(err != NULL && err[0] != '\0') ? err : "model invoke failed");
|
||||
if (gen_rc == ESP_ERR_TIMEOUT) {
|
||||
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_IMAGE_GENERATE_TIMEOUT, 1);
|
||||
} else {
|
||||
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_IMAGE_GENERATE_FAILED, 1);
|
||||
}
|
||||
runtime_diag_record_error("image_generate", gen_rc, err != NULL ? err : "model invoke failed");
|
||||
image_generation_result_free(out_result);
|
||||
return gen_rc;
|
||||
}
|
||||
@@ -927,6 +938,12 @@ esp_err_t image_generation_generate_png(const image_generation_request_t *req,
|
||||
"image download stage failed, rc=0x%x, msg=%s",
|
||||
(unsigned)dl_rc,
|
||||
(err != NULL && err[0] != '\0') ? err : "download failed");
|
||||
if (dl_rc == ESP_ERR_TIMEOUT) {
|
||||
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_IMAGE_GENERATE_TIMEOUT, 1);
|
||||
} else {
|
||||
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_IMAGE_GENERATE_FAILED, 1);
|
||||
}
|
||||
runtime_diag_record_error("image_download", dl_rc, err != NULL ? err : "download failed");
|
||||
image_generation_result_free(out_result);
|
||||
return dl_rc;
|
||||
}
|
||||
@@ -940,6 +957,7 @@ esp_err_t image_generation_generate_png(const image_generation_request_t *req,
|
||||
(long long)(t_gen_done_ms - t0_ms),
|
||||
(long long)(t_dl_done_ms - t_gen_done_ms),
|
||||
(long long)(t_dl_done_ms - t0_ms));
|
||||
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_IMAGE_GENERATE_SUCCESS, 1);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
#include "printer_protocol_internal.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ble_printer_client.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "runtime_diagnostics.h"
|
||||
#include "runtime_policy.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
@@ -16,7 +19,8 @@ QueueHandle_t s_job_queue;
|
||||
EventGroupHandle_t s_evt;
|
||||
|
||||
uint32_t s_busy_refcnt;
|
||||
static bool s_status_poll_paused;
|
||||
uint32_t s_status_poll_pause_next_token;
|
||||
uint32_t s_status_poll_pause_tokens[STATUS_POLL_PAUSE_SLOT_MAX];
|
||||
|
||||
parsed_status_t s_status = {
|
||||
.has_paper = true,
|
||||
@@ -31,6 +35,10 @@ uint8_t s_last_rsp_payload[252];
|
||||
|
||||
uint32_t s_next_job_id = 1;
|
||||
job_slot_t s_jobs[JOB_SLOT_MAX];
|
||||
TaskHandle_t s_worker_task;
|
||||
TaskHandle_t s_status_poll_task_handle;
|
||||
bool s_protocol_initialized;
|
||||
bool s_protocol_stopping;
|
||||
|
||||
static const char *TAG = "printer_protocol";
|
||||
|
||||
@@ -40,10 +48,64 @@ static const char *TAG = "printer_protocol";
|
||||
#define PRINT_WORKER_CORE_ID 1
|
||||
#endif
|
||||
|
||||
static size_t status_poll_pause_depth_locked(void) {
|
||||
size_t depth = 0;
|
||||
for (size_t i = 0; i < STATUS_POLL_PAUSE_SLOT_MAX; ++i) {
|
||||
if (s_status_poll_pause_tokens[i] != 0) {
|
||||
++depth;
|
||||
}
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
static void status_poll_pause_slots_clear_locked(void) {
|
||||
memset(s_status_poll_pause_tokens, 0, sizeof(s_status_poll_pause_tokens));
|
||||
}
|
||||
|
||||
static void reset_jobs_locked(void) {
|
||||
for (int i = 0; i < JOB_SLOT_MAX; ++i) {
|
||||
free(s_jobs[i].data);
|
||||
memset(&s_jobs[i], 0, sizeof(s_jobs[i]));
|
||||
}
|
||||
s_next_job_id = 1;
|
||||
}
|
||||
|
||||
static void reset_runtime_state_locked(void) {
|
||||
s_busy_refcnt = 0;
|
||||
s_last_rsp_cmd = 0;
|
||||
s_last_rsp_payload_len = 0;
|
||||
memset(s_last_rsp_payload, 0, sizeof(s_last_rsp_payload));
|
||||
|
||||
status_poll_pause_slots_clear_locked();
|
||||
s_status_poll_pause_next_token = 1;
|
||||
|
||||
s_status.has_paper = true;
|
||||
s_status.battery = 100;
|
||||
s_status.temperature = 25.0f;
|
||||
s_status.updated_ms = 0;
|
||||
}
|
||||
|
||||
bool printer_protocol_is_stopping(void) {
|
||||
if (!s_protocol_initialized) {
|
||||
return false;
|
||||
}
|
||||
if (s_mutex == NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool stopping = true;
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(50)) == pdTRUE) {
|
||||
stopping = s_protocol_stopping;
|
||||
xSemaphoreGive(s_mutex);
|
||||
}
|
||||
return stopping;
|
||||
}
|
||||
|
||||
static BaseType_t printer_create_task_prefer_psram(TaskFunction_t task_fn,
|
||||
const char *name,
|
||||
uint32_t stack_size,
|
||||
UBaseType_t priority) {
|
||||
UBaseType_t priority,
|
||||
TaskHandle_t *out_task) {
|
||||
#if defined(CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY) && \
|
||||
defined(CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM) && \
|
||||
(configSUPPORT_STATIC_ALLOCATION == 1)
|
||||
@@ -52,7 +114,7 @@ static BaseType_t printer_create_task_prefer_psram(TaskFunction_t task_fn,
|
||||
(configSTACK_DEPTH_TYPE)stack_size,
|
||||
NULL,
|
||||
priority,
|
||||
NULL,
|
||||
out_task,
|
||||
PRINT_WORKER_CORE_ID,
|
||||
MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
if (rc == pdPASS) {
|
||||
@@ -64,22 +126,85 @@ static BaseType_t printer_create_task_prefer_psram(TaskFunction_t task_fn,
|
||||
(configSTACK_DEPTH_TYPE)stack_size,
|
||||
NULL,
|
||||
priority,
|
||||
NULL,
|
||||
out_task,
|
||||
PRINT_WORKER_CORE_ID);
|
||||
}
|
||||
|
||||
void printer_protocol_set_status_poll_paused(bool paused) {
|
||||
if (s_mutex == NULL) {
|
||||
printer_status_poll_pause_token_t printer_protocol_status_poll_pause_acquire(void) {
|
||||
if (!s_protocol_initialized || s_mutex == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(200)) != pdTRUE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (s_protocol_stopping) {
|
||||
xSemaphoreGive(s_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int slot = -1;
|
||||
for (int i = 0; i < STATUS_POLL_PAUSE_SLOT_MAX; ++i) {
|
||||
if (s_status_poll_pause_tokens[i] == 0) {
|
||||
slot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (slot < 0) {
|
||||
xSemaphoreGive(s_mutex);
|
||||
ESP_LOGW(TAG, "status poll pause acquire failed: no free slot");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t token = s_status_poll_pause_next_token++;
|
||||
if (token == 0) {
|
||||
token = s_status_poll_pause_next_token++;
|
||||
}
|
||||
|
||||
s_status_poll_pause_tokens[slot] = token;
|
||||
runtime_diag_set_gauge(RUNTIME_DIAG_GAUGE_STATUS_POLL_PAUSE_DEPTH,
|
||||
(int32_t)status_poll_pause_depth_locked());
|
||||
xSemaphoreGive(s_mutex);
|
||||
return token;
|
||||
}
|
||||
|
||||
void printer_protocol_status_poll_pause_release(printer_status_poll_pause_token_t *token) {
|
||||
if (token == NULL || *token == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!s_protocol_initialized || s_mutex == NULL) {
|
||||
*token = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(200)) != pdTRUE) {
|
||||
return;
|
||||
}
|
||||
s_status_poll_paused = paused;
|
||||
|
||||
for (int i = 0; i < STATUS_POLL_PAUSE_SLOT_MAX; ++i) {
|
||||
if (s_status_poll_pause_tokens[i] == *token) {
|
||||
s_status_poll_pause_tokens[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
runtime_diag_set_gauge(RUNTIME_DIAG_GAUGE_STATUS_POLL_PAUSE_DEPTH,
|
||||
(int32_t)status_poll_pause_depth_locked());
|
||||
xSemaphoreGive(s_mutex);
|
||||
*token = 0;
|
||||
}
|
||||
|
||||
bool printer_protocol_acquire_control_lane(uint32_t timeout_ms, char *err, size_t err_len) {
|
||||
if (!s_protocol_initialized || s_mutex == NULL) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "printer protocol not initialized");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(timeout_ms)) != pdTRUE) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "lock timeout");
|
||||
@@ -87,6 +212,14 @@ bool printer_protocol_acquire_control_lane(uint32_t timeout_ms, char *err, size_
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s_protocol_stopping) {
|
||||
xSemaphoreGive(s_mutex);
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "printer protocol stopping");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (s_busy_refcnt != 0) {
|
||||
xSemaphoreGive(s_mutex);
|
||||
if (err != NULL && err_len > 0) {
|
||||
@@ -101,6 +234,10 @@ bool printer_protocol_acquire_control_lane(uint32_t timeout_ms, char *err, size_
|
||||
}
|
||||
|
||||
void printer_protocol_release_control_lane(void) {
|
||||
if (s_mutex == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(1000)) != pdTRUE) {
|
||||
return;
|
||||
}
|
||||
@@ -123,6 +260,10 @@ esp_err_t printer_protocol_send_frame(uint8_t cmd,
|
||||
const uint8_t *payload,
|
||||
uint16_t payload_len,
|
||||
bool with_checksum) {
|
||||
if (!s_protocol_initialized || printer_protocol_is_stopping()) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
uint8_t frame[260];
|
||||
size_t total = 4 + payload_len + (with_checksum ? 1 : 0);
|
||||
if (total > sizeof(frame)) {
|
||||
@@ -146,7 +287,9 @@ esp_err_t printer_protocol_send_frame(uint8_t cmd,
|
||||
}
|
||||
|
||||
static void clear_ack_signal(void) {
|
||||
if (s_evt != NULL) {
|
||||
xEventGroupClearBits(s_evt, EVT_ACK);
|
||||
}
|
||||
}
|
||||
|
||||
bool printer_protocol_wait_response(uint8_t cmd,
|
||||
@@ -154,24 +297,41 @@ bool printer_protocol_wait_response(uint8_t cmd,
|
||||
size_t out_payload_cap,
|
||||
uint16_t *out_payload_len,
|
||||
uint32_t timeout_ms) {
|
||||
if (timeout_ms == 0 || s_evt == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t deadline = esp_timer_get_time() / 1000 + timeout_ms;
|
||||
|
||||
while (true) {
|
||||
if (printer_protocol_is_stopping()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t now = esp_timer_get_time() / 1000;
|
||||
if (now >= deadline) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t wait_ms = (uint32_t)(deadline - now);
|
||||
if (wait_ms > 100) {
|
||||
wait_ms = 100;
|
||||
}
|
||||
|
||||
EventBits_t bits = xEventGroupWaitBits(s_evt,
|
||||
EVT_ACK,
|
||||
pdTRUE,
|
||||
EVT_ACK | EVT_SHUTDOWN,
|
||||
pdFALSE,
|
||||
pdFALSE,
|
||||
pdMS_TO_TICKS(wait_ms));
|
||||
if (!(bits & EVT_ACK)) {
|
||||
if (bits & EVT_SHUTDOWN) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(bits & EVT_ACK)) {
|
||||
continue;
|
||||
}
|
||||
xEventGroupClearBits(s_evt, EVT_ACK);
|
||||
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(200)) != pdTRUE) {
|
||||
continue;
|
||||
}
|
||||
@@ -247,9 +407,13 @@ esp_err_t printer_protocol_send_cmd_wait_response(uint8_t cmd,
|
||||
uint16_t rsp_len = 0;
|
||||
if (!printer_protocol_wait_response(cmd, rsp, sizeof(rsp), &rsp_len, timeout_ms)) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
if (printer_protocol_is_stopping()) {
|
||||
snprintf(err, err_len, "protocol stopping");
|
||||
} else {
|
||||
snprintf(err, err_len, "response timeout");
|
||||
}
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
return printer_protocol_is_stopping() ? ESP_ERR_INVALID_STATE : ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
if (expect_ack && (rsp_len < 1 || rsp[0] != 0x01)) {
|
||||
@@ -277,22 +441,35 @@ static void status_poll_task(void *arg) {
|
||||
(void)arg;
|
||||
|
||||
while (true) {
|
||||
bool should_stop = false;
|
||||
bool can_poll = false;
|
||||
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(50)) == pdTRUE) {
|
||||
can_poll = (s_busy_refcnt == 0) && !s_status_poll_paused;
|
||||
should_stop = s_protocol_stopping;
|
||||
can_poll = (s_busy_refcnt == 0) && (status_poll_pause_depth_locked() == 0);
|
||||
xSemaphoreGive(s_mutex);
|
||||
}
|
||||
|
||||
if (should_stop) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (ble_printer_client_is_connected() && can_poll) {
|
||||
uint8_t dummy = 0x00;
|
||||
(void)printer_protocol_send_frame(CMD_GET_STATUS, &dummy, 0, true);
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
vTaskDelay(pdMS_TO_TICKS(runtime_policy_printer_status_poll_interval_ms()));
|
||||
}
|
||||
|
||||
if (s_evt != NULL) {
|
||||
xEventGroupSetBits(s_evt, EVT_STATUS_POLL_EXITED);
|
||||
}
|
||||
s_status_poll_task_handle = NULL;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void on_rx_frame(const uint8_t *data, size_t len) {
|
||||
if (data == NULL || len < 4) {
|
||||
if (data == NULL || len < 4 || s_mutex == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -336,11 +513,32 @@ static void on_rx_frame(const uint8_t *data, size_t len) {
|
||||
memcpy(s_last_rsp_payload, payload, s_last_rsp_payload_len);
|
||||
xSemaphoreGive(s_mutex);
|
||||
}
|
||||
if (s_evt != NULL) {
|
||||
xEventGroupSetBits(s_evt, EVT_ACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_runtime_objects(void) {
|
||||
if (s_job_queue != NULL) {
|
||||
vQueueDelete(s_job_queue);
|
||||
s_job_queue = NULL;
|
||||
}
|
||||
if (s_evt != NULL) {
|
||||
vEventGroupDelete(s_evt);
|
||||
s_evt = NULL;
|
||||
}
|
||||
if (s_mutex != NULL) {
|
||||
vSemaphoreDelete(s_mutex);
|
||||
s_mutex = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t printer_protocol_init(void) {
|
||||
if (s_protocol_initialized) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
s_mutex = xSemaphoreCreateMutex();
|
||||
if (s_mutex == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
@@ -348,32 +546,58 @@ esp_err_t printer_protocol_init(void) {
|
||||
|
||||
s_evt = xEventGroupCreate();
|
||||
if (s_evt == NULL) {
|
||||
destroy_runtime_objects();
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
s_job_queue = xQueueCreate(JOB_QUEUE_LEN, sizeof(uint32_t));
|
||||
if (s_job_queue == NULL) {
|
||||
destroy_runtime_objects();
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(200)) == pdTRUE) {
|
||||
reset_jobs_locked();
|
||||
reset_runtime_state_locked();
|
||||
s_protocol_stopping = false;
|
||||
xSemaphoreGive(s_mutex);
|
||||
}
|
||||
|
||||
runtime_diag_set_gauge(RUNTIME_DIAG_GAUGE_STATUS_POLL_PAUSE_DEPTH, 0);
|
||||
runtime_diag_set_gauge(RUNTIME_DIAG_GAUGE_PRINTER_QUEUE_DEPTH, 0);
|
||||
s_protocol_initialized = true;
|
||||
s_protocol_stopping = false;
|
||||
|
||||
esp_err_t err = ble_printer_client_init(on_rx_frame);
|
||||
if (err != ESP_OK) {
|
||||
s_protocol_initialized = false;
|
||||
destroy_runtime_objects();
|
||||
return err;
|
||||
}
|
||||
|
||||
BaseType_t ok = printer_create_task_prefer_psram(printer_protocol_worker_task,
|
||||
"print_worker",
|
||||
6144,
|
||||
6);
|
||||
6,
|
||||
&s_worker_task);
|
||||
if (ok != pdPASS) {
|
||||
s_protocol_initialized = false;
|
||||
destroy_runtime_objects();
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
ok = printer_create_task_prefer_psram(status_poll_task,
|
||||
"status_poll",
|
||||
4096,
|
||||
4);
|
||||
4,
|
||||
&s_status_poll_task_handle);
|
||||
if (ok != pdPASS) {
|
||||
if (s_worker_task != NULL) {
|
||||
vTaskDelete(s_worker_task);
|
||||
s_worker_task = NULL;
|
||||
}
|
||||
s_protocol_initialized = false;
|
||||
destroy_runtime_objects();
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
@@ -381,7 +605,89 @@ esp_err_t printer_protocol_init(void) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t printer_protocol_stop(uint32_t timeout_ms) {
|
||||
if (!s_protocol_initialized) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (timeout_ms == 0) {
|
||||
timeout_ms = runtime_policy_printer_stop_timeout_ms();
|
||||
}
|
||||
|
||||
if (s_mutex != NULL && xSemaphoreTake(s_mutex, pdMS_TO_TICKS(1000)) == pdTRUE) {
|
||||
s_protocol_stopping = true;
|
||||
s_busy_refcnt = 0;
|
||||
status_poll_pause_slots_clear_locked();
|
||||
xSemaphoreGive(s_mutex);
|
||||
}
|
||||
|
||||
runtime_diag_set_gauge(RUNTIME_DIAG_GAUGE_STATUS_POLL_PAUSE_DEPTH, 0);
|
||||
|
||||
if (s_evt != NULL) {
|
||||
xEventGroupSetBits(s_evt, EVT_SHUTDOWN);
|
||||
}
|
||||
|
||||
if (s_job_queue != NULL) {
|
||||
const uint32_t sentinel = PRINTER_JOB_SENTINEL_STOP;
|
||||
(void)xQueueSend(s_job_queue, &sentinel, 0);
|
||||
}
|
||||
|
||||
ble_printer_client_disconnect();
|
||||
|
||||
bool worker_done = (s_worker_task == NULL);
|
||||
bool poll_done = (s_status_poll_task_handle == NULL);
|
||||
|
||||
if (s_evt != NULL && (!worker_done || !poll_done)) {
|
||||
EventBits_t bits = xEventGroupWaitBits(s_evt,
|
||||
EVT_WORKER_EXITED | EVT_STATUS_POLL_EXITED,
|
||||
pdFALSE,
|
||||
pdTRUE,
|
||||
pdMS_TO_TICKS(timeout_ms));
|
||||
worker_done = worker_done || ((bits & EVT_WORKER_EXITED) != 0) || s_worker_task == NULL;
|
||||
poll_done = poll_done || ((bits & EVT_STATUS_POLL_EXITED) != 0) || s_status_poll_task_handle == NULL;
|
||||
}
|
||||
|
||||
if (!worker_done && s_worker_task != NULL) {
|
||||
ESP_LOGW(TAG, "worker task stop timeout, force delete");
|
||||
vTaskDelete(s_worker_task);
|
||||
s_worker_task = NULL;
|
||||
}
|
||||
if (!poll_done && s_status_poll_task_handle != NULL) {
|
||||
ESP_LOGW(TAG, "status poll task stop timeout, force delete");
|
||||
vTaskDelete(s_status_poll_task_handle);
|
||||
s_status_poll_task_handle = NULL;
|
||||
}
|
||||
|
||||
if (s_mutex != NULL && xSemaphoreTake(s_mutex, pdMS_TO_TICKS(500)) == pdTRUE) {
|
||||
reset_jobs_locked();
|
||||
reset_runtime_state_locked();
|
||||
xSemaphoreGive(s_mutex);
|
||||
}
|
||||
|
||||
destroy_runtime_objects();
|
||||
|
||||
s_busy_refcnt = 0;
|
||||
s_status_poll_pause_next_token = 1;
|
||||
memset(s_status_poll_pause_tokens, 0, sizeof(s_status_poll_pause_tokens));
|
||||
memset(s_last_rsp_payload, 0, sizeof(s_last_rsp_payload));
|
||||
s_last_rsp_cmd = 0;
|
||||
s_last_rsp_payload_len = 0;
|
||||
s_worker_task = NULL;
|
||||
s_status_poll_task_handle = NULL;
|
||||
s_protocol_initialized = false;
|
||||
s_protocol_stopping = false;
|
||||
|
||||
runtime_diag_set_gauge(RUNTIME_DIAG_GAUGE_STATUS_POLL_PAUSE_DEPTH, 0);
|
||||
runtime_diag_set_gauge(RUNTIME_DIAG_GAUGE_PRINTER_QUEUE_DEPTH, 0);
|
||||
|
||||
ESP_LOGI(TAG, "printer protocol stopped");
|
||||
return (worker_done && poll_done) ? ESP_OK : ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
esp_err_t printer_protocol_connect(const char *target_name, uint32_t timeout_ms) {
|
||||
if (!s_protocol_initialized || s_protocol_stopping) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
return ble_printer_client_connect(target_name, timeout_ms);
|
||||
}
|
||||
|
||||
@@ -403,13 +709,17 @@ void printer_protocol_get_runtime_status(printer_runtime_status_t *out_status) {
|
||||
out_status->notify_ready = link.notify_ready;
|
||||
out_status->mtu = link.mtu;
|
||||
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(200)) == pdTRUE) {
|
||||
if (s_mutex != NULL && xSemaphoreTake(s_mutex, pdMS_TO_TICKS(200)) == pdTRUE) {
|
||||
out_status->busy = s_busy_refcnt > 0;
|
||||
out_status->has_paper = s_status.has_paper;
|
||||
out_status->battery_percent = s_status.battery;
|
||||
out_status->temperature = s_status.temperature;
|
||||
out_status->last_status_ms = s_status.updated_ms;
|
||||
out_status->queue_depth = (uint32_t)uxQueueMessagesWaiting(s_job_queue);
|
||||
out_status->queue_depth = (s_job_queue != NULL)
|
||||
? (uint32_t)uxQueueMessagesWaiting(s_job_queue)
|
||||
: 0;
|
||||
xSemaphoreGive(s_mutex);
|
||||
}
|
||||
|
||||
runtime_diag_set_gauge(RUNTIME_DIAG_GAUGE_PRINTER_QUEUE_DEPTH, (int32_t)out_status->queue_depth);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "ble_printer_client.h"
|
||||
#include "runtime_policy.h"
|
||||
|
||||
esp_err_t printer_protocol_gap_move(uint32_t timeout_ms, char *err, size_t err_len) {
|
||||
if (!ble_printer_client_is_connected()) {
|
||||
@@ -14,7 +15,7 @@ esp_err_t printer_protocol_gap_move(uint32_t timeout_ms, char *err, size_t err_l
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (!printer_protocol_acquire_control_lane(1000, err, err_len)) {
|
||||
if (!printer_protocol_acquire_control_lane(runtime_policy_printer_control_lock_timeout_ms(), err, err_len)) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
@@ -49,7 +50,7 @@ esp_err_t printer_protocol_get_label_offset(uint8_t *out_offset, uint32_t timeou
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (!printer_protocol_acquire_control_lane(1000, err, err_len)) {
|
||||
if (!printer_protocol_acquire_control_lane(runtime_policy_printer_control_lock_timeout_ms(), err, err_len)) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
@@ -96,7 +97,7 @@ esp_err_t printer_protocol_set_label_offset(uint8_t offset, uint32_t timeout_ms,
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (!printer_protocol_acquire_control_lane(1000, err, err_len)) {
|
||||
if (!printer_protocol_acquire_control_lane(runtime_policy_printer_control_lock_timeout_ms(), err, err_len)) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
@@ -123,7 +124,7 @@ esp_err_t printer_protocol_ota_jump_boot(uint32_t timeout_ms, char *err, size_t
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (!printer_protocol_acquire_control_lane(1000, err, err_len)) {
|
||||
if (!printer_protocol_acquire_control_lane(runtime_policy_printer_control_lock_timeout_ms(), err, err_len)) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
@@ -150,7 +151,7 @@ esp_err_t printer_protocol_ota_jump_app(uint32_t timeout_ms, char *err, size_t e
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (!printer_protocol_acquire_control_lane(1000, err, err_len)) {
|
||||
if (!printer_protocol_acquire_control_lane(runtime_policy_printer_control_lock_timeout_ms(), err, err_len)) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
@@ -177,7 +178,7 @@ esp_err_t printer_protocol_ota_erase_page(uint16_t page_num, uint32_t timeout_ms
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (!printer_protocol_acquire_control_lane(1000, err, err_len)) {
|
||||
if (!printer_protocol_acquire_control_lane(runtime_policy_printer_control_lock_timeout_ms(), err, err_len)) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
@@ -222,7 +223,7 @@ esp_err_t printer_protocol_ota_write_frame(uint16_t packet_num,
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (!printer_protocol_acquire_control_lane(1000, err, err_len)) {
|
||||
if (!printer_protocol_acquire_control_lane(runtime_policy_printer_control_lock_timeout_ms(), err, err_len)) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
@@ -267,7 +268,7 @@ esp_err_t printer_protocol_ota_get_version(printer_ota_version_t *out_version,
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (!printer_protocol_acquire_control_lane(1000, err, err_len)) {
|
||||
if (!printer_protocol_acquire_control_lane(runtime_policy_printer_control_lock_timeout_ms(), err, err_len)) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "runtime_diagnostics.h"
|
||||
#include "runtime_policy.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
@@ -63,6 +65,13 @@ esp_err_t printer_protocol_submit_raster_job(const uint8_t *raster,
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (!s_protocol_initialized || s_protocol_stopping) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "printer protocol unavailable");
|
||||
}
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (width != 384) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "width must be 384");
|
||||
@@ -87,7 +96,7 @@ esp_err_t printer_protocol_submit_raster_job(const uint8_t *raster,
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(1000)) != pdTRUE) {
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(runtime_policy_printer_control_lock_timeout_ms())) != pdTRUE) {
|
||||
if (err != NULL && err_len > 0) {
|
||||
snprintf(err, err_len, "lock timeout");
|
||||
}
|
||||
@@ -166,6 +175,8 @@ esp_err_t printer_protocol_submit_raster_job(const uint8_t *raster,
|
||||
(unsigned)height,
|
||||
density != NULL ? density : "中等",
|
||||
(unsigned)queue_depth);
|
||||
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_PRINTER_JOB_SUBMITTED, 1);
|
||||
runtime_diag_set_gauge(RUNTIME_DIAG_GAUGE_PRINTER_QUEUE_DEPTH, (int32_t)queue_depth);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -284,6 +295,7 @@ esp_err_t printer_protocol_cancel_job(uint32_t job_id, char *err, size_t err_len
|
||||
}
|
||||
|
||||
xSemaphoreGive(s_mutex);
|
||||
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_PRINTER_JOB_CANCELED, 1);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -319,5 +331,7 @@ size_t printer_protocol_cleanup_jobs(bool include_success, bool include_failed,
|
||||
}
|
||||
|
||||
xSemaphoreGive(s_mutex);
|
||||
runtime_diag_set_gauge(RUNTIME_DIAG_GAUGE_PRINTER_QUEUE_DEPTH,
|
||||
s_job_queue != NULL ? (int32_t)uxQueueMessagesWaiting(s_job_queue) : 0);
|
||||
return removed;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "ble_printer_client.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "runtime_diagnostics.h"
|
||||
#include "runtime_policy.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
@@ -16,6 +18,10 @@ static const char *TAG = "printer_protocol";
|
||||
static bool request_status_sync(uint32_t timeout_ms) {
|
||||
int64_t old_ms;
|
||||
|
||||
if (printer_protocol_is_stopping()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(200)) != pdTRUE) {
|
||||
return false;
|
||||
}
|
||||
@@ -29,6 +35,10 @@ static bool request_status_sync(uint32_t timeout_ms) {
|
||||
|
||||
int64_t deadline = esp_timer_get_time() / 1000 + timeout_ms;
|
||||
while ((esp_timer_get_time() / 1000) < deadline) {
|
||||
if (printer_protocol_is_stopping()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(100)) != pdTRUE) {
|
||||
@@ -111,6 +121,11 @@ static bool run_print_job(job_slot_t *job) {
|
||||
(unsigned)job->height,
|
||||
job->density);
|
||||
|
||||
if (printer_protocol_is_stopping()) {
|
||||
snprintf(job->error, sizeof(job->error), "protocol stopping");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ble_printer_client_is_connected()) {
|
||||
snprintf(job->error, sizeof(job->error), "printer not connected");
|
||||
ESP_LOGW(TAG, "job %u print aborted: %s", (unsigned)job->id, job->error);
|
||||
@@ -160,6 +175,11 @@ static bool run_print_job(job_slot_t *job) {
|
||||
(unsigned)hot_time);
|
||||
|
||||
for (size_t i = 0; i < total_chunks; ++i) {
|
||||
if (printer_protocol_is_stopping()) {
|
||||
snprintf(job->error, sizeof(job->error), "protocol stopping");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (job->cancel_requested) {
|
||||
snprintf(job->error, sizeof(job->error), "job canceled");
|
||||
ESP_LOGW(TAG,
|
||||
@@ -219,11 +239,21 @@ void printer_protocol_worker_task(void *arg) {
|
||||
(void)arg;
|
||||
|
||||
while (true) {
|
||||
uint32_t job_id;
|
||||
if (xQueueReceive(s_job_queue, &job_id, portMAX_DELAY) != pdTRUE) {
|
||||
if (printer_protocol_is_stopping()) {
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t job_id = 0;
|
||||
if (xQueueReceive(s_job_queue,
|
||||
&job_id,
|
||||
pdMS_TO_TICKS(runtime_policy_printer_worker_queue_wait_ms())) != pdTRUE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (job_id == PRINTER_JOB_SENTINEL_STOP || printer_protocol_is_stopping()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(1000)) != pdTRUE) {
|
||||
continue;
|
||||
}
|
||||
@@ -241,13 +271,14 @@ void printer_protocol_worker_task(void *arg) {
|
||||
s_jobs[idx].finished_ms = esp_timer_get_time() / 1000;
|
||||
}
|
||||
xSemaphoreGive(s_mutex);
|
||||
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_PRINTER_JOB_CANCELED, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (s_busy_refcnt != 0) {
|
||||
xSemaphoreGive(s_mutex);
|
||||
xQueueSendToFront(s_job_queue, &job_id, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
(void)xQueueSendToFront(s_job_queue, &job_id, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(runtime_policy_printer_queue_retry_delay_ms()));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -287,13 +318,16 @@ void printer_protocol_worker_task(void *arg) {
|
||||
if (ok) {
|
||||
s_jobs[idx].state = PRINT_JOB_STATE_SUCCESS;
|
||||
s_jobs[idx].error[0] = '\0';
|
||||
} else if (s_jobs[idx].cancel_requested) {
|
||||
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_PRINTER_JOB_SUCCESS, 1);
|
||||
} else if (s_jobs[idx].cancel_requested || printer_protocol_is_stopping()) {
|
||||
s_jobs[idx].state = PRINT_JOB_STATE_CANCELED;
|
||||
if (s_jobs[idx].error[0] == '\0') {
|
||||
strlcpy(s_jobs[idx].error, "job canceled", sizeof(s_jobs[idx].error));
|
||||
}
|
||||
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_PRINTER_JOB_CANCELED, 1);
|
||||
} else {
|
||||
s_jobs[idx].state = PRINT_JOB_STATE_FAILED;
|
||||
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_PRINTER_JOB_FAILED, 1);
|
||||
}
|
||||
|
||||
int64_t duration_ms = 0;
|
||||
@@ -317,6 +351,14 @@ void printer_protocol_worker_task(void *arg) {
|
||||
|
||||
free(s_jobs[idx].data);
|
||||
s_jobs[idx].data = NULL;
|
||||
runtime_diag_set_gauge(RUNTIME_DIAG_GAUGE_PRINTER_QUEUE_DEPTH,
|
||||
(int32_t)uxQueueMessagesWaiting(s_job_queue));
|
||||
xSemaphoreGive(s_mutex);
|
||||
}
|
||||
|
||||
if (s_evt != NULL) {
|
||||
xEventGroupSetBits(s_evt, EVT_WORKER_EXITED);
|
||||
}
|
||||
s_worker_task = NULL;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
@@ -3,12 +3,39 @@
|
||||
#include "platform_bootstrap.h"
|
||||
#include "wifi_manager.h"
|
||||
|
||||
esp_err_t system_runtime_bootstrap(void) {
|
||||
static bool s_runtime_started;
|
||||
|
||||
esp_err_t system_runtime_start(void) {
|
||||
if (s_runtime_started) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t err = platform_bootstrap_init();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
return wifi_manager_start();
|
||||
|
||||
err = wifi_manager_start();
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
s_runtime_started = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t system_runtime_shutdown(void) {
|
||||
if (!s_runtime_started) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t err = wifi_manager_stop();
|
||||
s_runtime_started = false;
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t system_runtime_bootstrap(void) {
|
||||
return system_runtime_start();
|
||||
}
|
||||
|
||||
bool system_runtime_wifi_ready(void) {
|
||||
|
||||
@@ -331,17 +331,15 @@ static void voice_image_generation_task(void *arg) {
|
||||
image_generation_result_t result = {0};
|
||||
image_generation_result_reset(&result);
|
||||
|
||||
bool status_poll_paused = false;
|
||||
printer_status_poll_pause_token_t status_poll_pause_token = 0;
|
||||
char gen_err[160] = {0};
|
||||
ESP_LOGI(TAG,
|
||||
"[stage] marker_image_gen_start: dialog_id=%s prompt_len=%u",
|
||||
task_arg->dialog_id[0] != '\0' ? task_arg->dialog_id : "-",
|
||||
(unsigned)strlen(task_arg->prompt));
|
||||
printer_protocol_set_status_poll_paused(true);
|
||||
status_poll_paused = true;
|
||||
status_poll_pause_token = printer_protocol_status_poll_pause_acquire();
|
||||
esp_err_t rc = image_generation_generate_png(&req, &result, gen_err, sizeof(gen_err));
|
||||
printer_protocol_set_status_poll_paused(false);
|
||||
status_poll_paused = false;
|
||||
printer_protocol_status_poll_pause_release(&status_poll_pause_token);
|
||||
if (rc == ESP_OK) {
|
||||
ESP_LOGI(TAG,
|
||||
"[stage] marker_image_gen_done: dialog_id=%s request_id=%s size=%ux%u png_bytes=%u",
|
||||
@@ -375,9 +373,7 @@ static void voice_image_generation_task(void *arg) {
|
||||
gen_err[0] != '\0' ? gen_err : "image generation failed");
|
||||
}
|
||||
|
||||
if (status_poll_paused) {
|
||||
printer_protocol_set_status_poll_paused(false);
|
||||
}
|
||||
printer_protocol_status_poll_pause_release(&status_poll_pause_token);
|
||||
image_generation_result_free(&result);
|
||||
voice_marker_image_generation_release();
|
||||
image_generation_schedule_prewarm();
|
||||
|
||||
61
main/platform/include/runtime_diagnostics.h
Normal file
61
main/platform/include/runtime_diagnostics.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
RUNTIME_DIAG_COUNTER_LIFECYCLE_START_ATTEMPT = 0,
|
||||
RUNTIME_DIAG_COUNTER_LIFECYCLE_START_SUCCESS,
|
||||
RUNTIME_DIAG_COUNTER_LIFECYCLE_START_FAILED,
|
||||
RUNTIME_DIAG_COUNTER_LIFECYCLE_START_RETRY,
|
||||
RUNTIME_DIAG_COUNTER_LIFECYCLE_STOP_ATTEMPT,
|
||||
RUNTIME_DIAG_COUNTER_LIFECYCLE_STOP_SUCCESS,
|
||||
RUNTIME_DIAG_COUNTER_LIFECYCLE_STOP_FAILED,
|
||||
RUNTIME_DIAG_COUNTER_WIFI_CONNECT_SUCCESS,
|
||||
RUNTIME_DIAG_COUNTER_WIFI_CONNECT_FAILED,
|
||||
RUNTIME_DIAG_COUNTER_WIFI_CONNECT_TIMEOUT,
|
||||
RUNTIME_DIAG_COUNTER_PRINTER_JOB_SUBMITTED,
|
||||
RUNTIME_DIAG_COUNTER_PRINTER_JOB_SUCCESS,
|
||||
RUNTIME_DIAG_COUNTER_PRINTER_JOB_FAILED,
|
||||
RUNTIME_DIAG_COUNTER_PRINTER_JOB_CANCELED,
|
||||
RUNTIME_DIAG_COUNTER_IMAGE_GENERATE_ATTEMPT,
|
||||
RUNTIME_DIAG_COUNTER_IMAGE_GENERATE_SUCCESS,
|
||||
RUNTIME_DIAG_COUNTER_IMAGE_GENERATE_FAILED,
|
||||
RUNTIME_DIAG_COUNTER_IMAGE_GENERATE_TIMEOUT,
|
||||
RUNTIME_DIAG_COUNTER_REST_RESPONSES_TOTAL,
|
||||
RUNTIME_DIAG_COUNTER_REST_ERRORS_TOTAL,
|
||||
RUNTIME_DIAG_COUNTER_MAX,
|
||||
} runtime_diag_counter_t;
|
||||
|
||||
typedef enum {
|
||||
RUNTIME_DIAG_GAUGE_LIFECYCLE_STATE = 0,
|
||||
RUNTIME_DIAG_GAUGE_STATUS_POLL_PAUSE_DEPTH,
|
||||
RUNTIME_DIAG_GAUGE_PRINTER_QUEUE_DEPTH,
|
||||
RUNTIME_DIAG_GAUGE_MAX,
|
||||
} runtime_diag_gauge_t;
|
||||
|
||||
typedef struct {
|
||||
uint64_t counters[RUNTIME_DIAG_COUNTER_MAX];
|
||||
int32_t gauges[RUNTIME_DIAG_GAUGE_MAX];
|
||||
int64_t last_error_ms;
|
||||
esp_err_t last_error_code;
|
||||
char last_error_source[32];
|
||||
char last_error_message[96];
|
||||
} runtime_diag_snapshot_t;
|
||||
|
||||
void runtime_diag_counter_add(runtime_diag_counter_t counter, uint32_t delta);
|
||||
void runtime_diag_set_gauge(runtime_diag_gauge_t gauge, int32_t value);
|
||||
void runtime_diag_record_error(const char *source, esp_err_t code, const char *message);
|
||||
void runtime_diag_get_snapshot(runtime_diag_snapshot_t *out_snapshot);
|
||||
|
||||
const char *runtime_diag_counter_name(runtime_diag_counter_t counter);
|
||||
const char *runtime_diag_gauge_name(runtime_diag_gauge_t gauge);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
30
main/platform/include/runtime_policy.h
Normal file
30
main/platform/include/runtime_policy.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
uint32_t runtime_policy_wifi_connect_timeout_ms(void);
|
||||
|
||||
uint32_t runtime_policy_lifecycle_start_retry_count(void);
|
||||
uint32_t runtime_policy_lifecycle_retry_backoff_ms(uint32_t attempt);
|
||||
bool runtime_policy_is_retryable_error(esp_err_t err);
|
||||
|
||||
uint32_t runtime_policy_printer_control_lock_timeout_ms(void);
|
||||
uint32_t runtime_policy_printer_worker_queue_wait_ms(void);
|
||||
uint32_t runtime_policy_printer_queue_retry_delay_ms(void);
|
||||
uint32_t runtime_policy_printer_status_poll_interval_ms(void);
|
||||
uint32_t runtime_policy_printer_stop_timeout_ms(void);
|
||||
|
||||
uint32_t runtime_policy_rest_printer_connect_timeout_ms(void);
|
||||
uint32_t runtime_policy_rest_label_timeout_ms(void);
|
||||
uint32_t runtime_policy_rest_ota_timeout_ms(void);
|
||||
|
||||
uint32_t runtime_policy_image_generation_timeout_default_ms(void);
|
||||
uint32_t runtime_policy_image_generation_timeout_min_ms(void);
|
||||
uint32_t runtime_policy_image_generation_timeout_max_ms(void);
|
||||
|
||||
uint32_t runtime_policy_image_download_timeout_default_ms(void);
|
||||
uint32_t runtime_policy_image_download_timeout_min_ms(void);
|
||||
uint32_t runtime_policy_image_download_timeout_max_ms(void);
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "esp_err.h"
|
||||
|
||||
esp_err_t wifi_manager_start(void);
|
||||
esp_err_t wifi_manager_stop(void);
|
||||
|
||||
bool wifi_manager_is_ready(void);
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ static uint16_t s_mtu = 23;
|
||||
static bool s_scanning;
|
||||
static bool s_notify_ready;
|
||||
static bool s_host_synced;
|
||||
static bool s_initialized;
|
||||
|
||||
static uint16_t uuid16(const ble_uuid_t *uuid) {
|
||||
if (uuid == NULL || uuid->type != BLE_UUID_TYPE_16) {
|
||||
@@ -519,6 +520,11 @@ static void nimble_host_task(void *param) {
|
||||
esp_err_t ble_printer_client_init(ble_frame_rx_cb_t rx_cb) {
|
||||
s_rx_cb = rx_cb;
|
||||
|
||||
if (s_initialized) {
|
||||
reset_discovery_state();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// NimBLE emits very chatty INFO logs during each chunk write.
|
||||
// Lowering these logs reduces serial I/O overhead and improves runtime smoothness.
|
||||
esp_log_level_set("NimBLE", ESP_LOG_WARN);
|
||||
@@ -531,6 +537,8 @@ esp_err_t ble_printer_client_init(ble_frame_rx_cb_t rx_cb) {
|
||||
|
||||
s_lock = xSemaphoreCreateMutex();
|
||||
if (s_lock == NULL) {
|
||||
vEventGroupDelete(s_evt_group);
|
||||
s_evt_group = NULL;
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
@@ -547,10 +555,15 @@ esp_err_t ble_printer_client_init(ble_frame_rx_cb_t rx_cb) {
|
||||
nimble_port_freertos_init(nimble_host_task);
|
||||
|
||||
reset_discovery_state();
|
||||
s_initialized = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ble_printer_client_connect(const char *target_name, uint32_t timeout_ms) {
|
||||
if (!s_initialized || s_lock == NULL || s_evt_group == NULL) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
s_match_any_compatible = false;
|
||||
if (target_name != NULL && target_name[0] != '\0') {
|
||||
if (strcmp(target_name, "*") == 0) {
|
||||
@@ -626,6 +639,10 @@ esp_err_t ble_printer_client_connect(const char *target_name, uint32_t timeout_m
|
||||
}
|
||||
|
||||
void ble_printer_client_disconnect(void) {
|
||||
if (!s_initialized || s_lock == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(s_lock, pdMS_TO_TICKS(2000)) != pdTRUE) {
|
||||
return;
|
||||
}
|
||||
@@ -642,6 +659,9 @@ void ble_printer_client_disconnect(void) {
|
||||
}
|
||||
|
||||
bool ble_printer_client_is_connected(void) {
|
||||
if (!s_initialized) {
|
||||
return false;
|
||||
}
|
||||
return (s_conn_handle != BLE_HS_CONN_HANDLE_NONE) && s_notify_ready;
|
||||
}
|
||||
|
||||
|
||||
136
main/platform/src/runtime_diagnostics.c
Normal file
136
main/platform/src/runtime_diagnostics.c
Normal file
@@ -0,0 +1,136 @@
|
||||
#include "runtime_diagnostics.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
static SemaphoreHandle_t s_lock;
|
||||
static runtime_diag_snapshot_t s_snapshot;
|
||||
|
||||
static const char *const s_counter_names[RUNTIME_DIAG_COUNTER_MAX] = {
|
||||
"lifecycle_start_attempt",
|
||||
"lifecycle_start_success",
|
||||
"lifecycle_start_failed",
|
||||
"lifecycle_start_retry",
|
||||
"lifecycle_stop_attempt",
|
||||
"lifecycle_stop_success",
|
||||
"lifecycle_stop_failed",
|
||||
"wifi_connect_success",
|
||||
"wifi_connect_failed",
|
||||
"wifi_connect_timeout",
|
||||
"printer_job_submitted",
|
||||
"printer_job_success",
|
||||
"printer_job_failed",
|
||||
"printer_job_canceled",
|
||||
"image_generate_attempt",
|
||||
"image_generate_success",
|
||||
"image_generate_failed",
|
||||
"image_generate_timeout",
|
||||
"rest_responses_total",
|
||||
"rest_errors_total",
|
||||
};
|
||||
|
||||
static const char *const s_gauge_names[RUNTIME_DIAG_GAUGE_MAX] = {
|
||||
"lifecycle_state",
|
||||
"status_poll_pause_depth",
|
||||
"printer_queue_depth",
|
||||
};
|
||||
|
||||
static void runtime_diag_ensure_lock(void) {
|
||||
if (s_lock == NULL) {
|
||||
s_lock = xSemaphoreCreateMutex();
|
||||
}
|
||||
}
|
||||
|
||||
void runtime_diag_counter_add(runtime_diag_counter_t counter, uint32_t delta) {
|
||||
if (counter < 0 || counter >= RUNTIME_DIAG_COUNTER_MAX || delta == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
runtime_diag_ensure_lock();
|
||||
if (s_lock == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(s_lock, pdMS_TO_TICKS(50)) != pdTRUE) {
|
||||
return;
|
||||
}
|
||||
s_snapshot.counters[counter] += delta;
|
||||
xSemaphoreGive(s_lock);
|
||||
}
|
||||
|
||||
void runtime_diag_set_gauge(runtime_diag_gauge_t gauge, int32_t value) {
|
||||
if (gauge < 0 || gauge >= RUNTIME_DIAG_GAUGE_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
runtime_diag_ensure_lock();
|
||||
if (s_lock == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(s_lock, pdMS_TO_TICKS(50)) != pdTRUE) {
|
||||
return;
|
||||
}
|
||||
s_snapshot.gauges[gauge] = value;
|
||||
xSemaphoreGive(s_lock);
|
||||
}
|
||||
|
||||
void runtime_diag_record_error(const char *source, esp_err_t code, const char *message) {
|
||||
runtime_diag_ensure_lock();
|
||||
if (s_lock == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(s_lock, pdMS_TO_TICKS(100)) != pdTRUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
s_snapshot.last_error_ms = esp_timer_get_time() / 1000;
|
||||
s_snapshot.last_error_code = code;
|
||||
strlcpy(s_snapshot.last_error_source,
|
||||
source != NULL ? source : "unknown",
|
||||
sizeof(s_snapshot.last_error_source));
|
||||
strlcpy(s_snapshot.last_error_message,
|
||||
message != NULL ? message : "unknown",
|
||||
sizeof(s_snapshot.last_error_message));
|
||||
|
||||
xSemaphoreGive(s_lock);
|
||||
}
|
||||
|
||||
void runtime_diag_get_snapshot(runtime_diag_snapshot_t *out_snapshot) {
|
||||
if (out_snapshot == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(out_snapshot, 0, sizeof(*out_snapshot));
|
||||
|
||||
runtime_diag_ensure_lock();
|
||||
if (s_lock == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(s_lock, pdMS_TO_TICKS(100)) != pdTRUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
*out_snapshot = s_snapshot;
|
||||
xSemaphoreGive(s_lock);
|
||||
}
|
||||
|
||||
const char *runtime_diag_counter_name(runtime_diag_counter_t counter) {
|
||||
if (counter < 0 || counter >= RUNTIME_DIAG_COUNTER_MAX) {
|
||||
return "unknown";
|
||||
}
|
||||
return s_counter_names[counter];
|
||||
}
|
||||
|
||||
const char *runtime_diag_gauge_name(runtime_diag_gauge_t gauge) {
|
||||
if (gauge < 0 || gauge >= RUNTIME_DIAG_GAUGE_MAX) {
|
||||
return "unknown";
|
||||
}
|
||||
return s_gauge_names[gauge];
|
||||
}
|
||||
142
main/platform/src/runtime_policy.c
Normal file
142
main/platform/src/runtime_policy.c
Normal file
@@ -0,0 +1,142 @@
|
||||
#include "runtime_policy.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifndef CONFIG_TQ_WIFI_CONNECT_TIMEOUT_MS
|
||||
#define CONFIG_TQ_WIFI_CONNECT_TIMEOUT_MS 15000
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_LIFECYCLE_START_RETRY_COUNT
|
||||
#define CONFIG_TQ_LIFECYCLE_START_RETRY_COUNT 2
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_LIFECYCLE_RETRY_BACKOFF_MS
|
||||
#define CONFIG_TQ_LIFECYCLE_RETRY_BACKOFF_MS 800
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_PRINTER_CONTROL_LOCK_TIMEOUT_MS
|
||||
#define CONFIG_TQ_PRINTER_CONTROL_LOCK_TIMEOUT_MS 1000
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_PRINTER_WORKER_QUEUE_WAIT_MS
|
||||
#define CONFIG_TQ_PRINTER_WORKER_QUEUE_WAIT_MS 250
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_PRINTER_QUEUE_RETRY_DELAY_MS
|
||||
#define CONFIG_TQ_PRINTER_QUEUE_RETRY_DELAY_MS 20
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_PRINTER_STATUS_POLL_INTERVAL_MS
|
||||
#define CONFIG_TQ_PRINTER_STATUS_POLL_INTERVAL_MS 5000
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_PRINTER_STOP_TIMEOUT_MS
|
||||
#define CONFIG_TQ_PRINTER_STOP_TIMEOUT_MS 8000
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_REST_PRINTER_CONNECT_TIMEOUT_MS
|
||||
#define CONFIG_TQ_REST_PRINTER_CONNECT_TIMEOUT_MS 15000
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_REST_LABEL_TIMEOUT_MS
|
||||
#define CONFIG_TQ_REST_LABEL_TIMEOUT_MS 5000
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_REST_OTA_STEP_TIMEOUT_MS
|
||||
#define CONFIG_TQ_REST_OTA_STEP_TIMEOUT_MS 5000
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_Z_IMAGE_TIMEOUT_MS
|
||||
#define CONFIG_TQ_Z_IMAGE_TIMEOUT_MS 45000
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_Z_IMAGE_DOWNLOAD_TIMEOUT_MS
|
||||
#define CONFIG_TQ_Z_IMAGE_DOWNLOAD_TIMEOUT_MS 15000
|
||||
#endif
|
||||
|
||||
static uint32_t clamp_u32(uint32_t value, uint32_t min_value, uint32_t max_value) {
|
||||
if (value < min_value) {
|
||||
return min_value;
|
||||
}
|
||||
if (value > max_value) {
|
||||
return max_value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t runtime_policy_wifi_connect_timeout_ms(void) {
|
||||
return clamp_u32(CONFIG_TQ_WIFI_CONNECT_TIMEOUT_MS, 3000, 60000);
|
||||
}
|
||||
|
||||
uint32_t runtime_policy_lifecycle_start_retry_count(void) {
|
||||
return clamp_u32(CONFIG_TQ_LIFECYCLE_START_RETRY_COUNT, 0, 5);
|
||||
}
|
||||
|
||||
uint32_t runtime_policy_lifecycle_retry_backoff_ms(uint32_t attempt) {
|
||||
uint32_t base = clamp_u32(CONFIG_TQ_LIFECYCLE_RETRY_BACKOFF_MS, 100, 10000);
|
||||
if (attempt > 8) {
|
||||
attempt = 8;
|
||||
}
|
||||
uint32_t factor = (1u << attempt);
|
||||
return clamp_u32(base * factor, 100, 30000);
|
||||
}
|
||||
|
||||
bool runtime_policy_is_retryable_error(esp_err_t err) {
|
||||
return err == ESP_ERR_TIMEOUT || err == ESP_FAIL || err == ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
uint32_t runtime_policy_printer_control_lock_timeout_ms(void) {
|
||||
return clamp_u32(CONFIG_TQ_PRINTER_CONTROL_LOCK_TIMEOUT_MS, 100, 5000);
|
||||
}
|
||||
|
||||
uint32_t runtime_policy_printer_worker_queue_wait_ms(void) {
|
||||
return clamp_u32(CONFIG_TQ_PRINTER_WORKER_QUEUE_WAIT_MS, 50, 2000);
|
||||
}
|
||||
|
||||
uint32_t runtime_policy_printer_queue_retry_delay_ms(void) {
|
||||
return clamp_u32(CONFIG_TQ_PRINTER_QUEUE_RETRY_DELAY_MS, 5, 500);
|
||||
}
|
||||
|
||||
uint32_t runtime_policy_printer_status_poll_interval_ms(void) {
|
||||
return clamp_u32(CONFIG_TQ_PRINTER_STATUS_POLL_INTERVAL_MS, 1000, 30000);
|
||||
}
|
||||
|
||||
uint32_t runtime_policy_printer_stop_timeout_ms(void) {
|
||||
return clamp_u32(CONFIG_TQ_PRINTER_STOP_TIMEOUT_MS, 1000, 30000);
|
||||
}
|
||||
|
||||
uint32_t runtime_policy_rest_printer_connect_timeout_ms(void) {
|
||||
return clamp_u32(CONFIG_TQ_REST_PRINTER_CONNECT_TIMEOUT_MS, 1000, 60000);
|
||||
}
|
||||
|
||||
uint32_t runtime_policy_rest_label_timeout_ms(void) {
|
||||
return clamp_u32(CONFIG_TQ_REST_LABEL_TIMEOUT_MS, 500, 30000);
|
||||
}
|
||||
|
||||
uint32_t runtime_policy_rest_ota_timeout_ms(void) {
|
||||
return clamp_u32(CONFIG_TQ_REST_OTA_STEP_TIMEOUT_MS, 500, 60000);
|
||||
}
|
||||
|
||||
uint32_t runtime_policy_image_generation_timeout_default_ms(void) {
|
||||
return clamp_u32(CONFIG_TQ_Z_IMAGE_TIMEOUT_MS, 5000, 180000);
|
||||
}
|
||||
|
||||
uint32_t runtime_policy_image_generation_timeout_min_ms(void) {
|
||||
return 5000;
|
||||
}
|
||||
|
||||
uint32_t runtime_policy_image_generation_timeout_max_ms(void) {
|
||||
return 180000;
|
||||
}
|
||||
|
||||
uint32_t runtime_policy_image_download_timeout_default_ms(void) {
|
||||
return clamp_u32(CONFIG_TQ_Z_IMAGE_DOWNLOAD_TIMEOUT_MS, 2000, 120000);
|
||||
}
|
||||
|
||||
uint32_t runtime_policy_image_download_timeout_min_ms(void) {
|
||||
return 2000;
|
||||
}
|
||||
|
||||
uint32_t runtime_policy_image_download_timeout_max_ms(void) {
|
||||
return 120000;
|
||||
}
|
||||
@@ -7,6 +7,9 @@
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_wifi_default.h"
|
||||
#include "runtime_diagnostics.h"
|
||||
#include "runtime_policy.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
|
||||
@@ -18,7 +21,10 @@ static const char *TAG = "wifi_manager";
|
||||
static EventGroupHandle_t s_wifi_event_group;
|
||||
static int s_retry_num;
|
||||
static bool s_ready;
|
||||
static bool s_started;
|
||||
static esp_netif_t *s_sta_netif;
|
||||
static esp_event_handler_instance_t s_wifi_event_inst;
|
||||
static esp_event_handler_instance_t s_ip_event_inst;
|
||||
|
||||
static void wifi_manager_apply_sta_throughput_profile(void) {
|
||||
const uint8_t protocol = WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N;
|
||||
@@ -49,18 +55,20 @@ static void wifi_event_handler(void *arg,
|
||||
int32_t event_id,
|
||||
void *event_data) {
|
||||
(void)arg;
|
||||
(void)event_data;
|
||||
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||
esp_wifi_connect();
|
||||
(void)esp_wifi_connect();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||
s_ready = false;
|
||||
if (s_retry_num < CONFIG_TQ_WIFI_MAXIMUM_RETRY) {
|
||||
esp_wifi_connect();
|
||||
(void)esp_wifi_connect();
|
||||
s_retry_num++;
|
||||
ESP_LOGW(TAG, "retry to connect to the AP (%d/%d)", s_retry_num, CONFIG_TQ_WIFI_MAXIMUM_RETRY);
|
||||
} else {
|
||||
} else if (s_wifi_event_group != NULL) {
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
|
||||
}
|
||||
return;
|
||||
@@ -70,7 +78,10 @@ static void wifi_event_handler(void *arg,
|
||||
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;
|
||||
s_ready = true;
|
||||
if (s_wifi_event_group != NULL) {
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -88,9 +99,18 @@ static esp_err_t start_sta_mode(void) {
|
||||
wifi_config.sta.pmf_cfg.capable = true;
|
||||
wifi_config.sta.pmf_cfg.required = false;
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
esp_err_t rc = esp_wifi_set_mode(WIFI_MODE_STA);
|
||||
if (rc != ESP_OK) {
|
||||
return rc;
|
||||
}
|
||||
rc = esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
|
||||
if (rc != ESP_OK) {
|
||||
return rc;
|
||||
}
|
||||
rc = esp_wifi_start();
|
||||
if (rc != ESP_OK && rc != ESP_ERR_INVALID_STATE) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "wifi_init_sta finished");
|
||||
|
||||
@@ -98,7 +118,7 @@ static esp_err_t start_sta_mode(void) {
|
||||
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
|
||||
pdFALSE,
|
||||
pdFALSE,
|
||||
pdMS_TO_TICKS(15000));
|
||||
pdMS_TO_TICKS(runtime_policy_wifi_connect_timeout_ms()));
|
||||
|
||||
if (bits & WIFI_CONNECTED_BIT) {
|
||||
ESP_LOGI(TAG, "connected to AP SSID:%s", CONFIG_TQ_WIFI_SSID);
|
||||
@@ -123,35 +143,157 @@ static esp_err_t start_sta_mode(void) {
|
||||
}
|
||||
|
||||
esp_err_t wifi_manager_start(void) {
|
||||
if (s_started) {
|
||||
return s_ready ? ESP_OK : ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
s_retry_num = 0;
|
||||
s_ready = false;
|
||||
|
||||
if (s_wifi_event_group == NULL) {
|
||||
s_wifi_event_group = xEventGroupCreate();
|
||||
if (s_wifi_event_group == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
}
|
||||
xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT);
|
||||
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
esp_err_t err = esp_netif_init();
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
runtime_diag_record_error("wifi_start", err, "esp_netif_init failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = esp_event_loop_create_default();
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
runtime_diag_record_error("wifi_start", err, "event loop init failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (s_sta_netif == NULL) {
|
||||
s_sta_netif = esp_netif_create_default_wifi_sta();
|
||||
if (s_sta_netif == NULL) {
|
||||
runtime_diag_record_error("wifi_start", ESP_ERR_NO_MEM, "create sta netif failed");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
}
|
||||
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
err = esp_wifi_init(&cfg);
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
runtime_diag_record_error("wifi_start", err, "esp_wifi_init failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT,
|
||||
if (s_wifi_event_inst == NULL) {
|
||||
err = esp_event_handler_instance_register(WIFI_EVENT,
|
||||
ESP_EVENT_ANY_ID,
|
||||
&wifi_event_handler,
|
||||
NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT,
|
||||
NULL,
|
||||
&s_wifi_event_inst);
|
||||
if (err != ESP_OK) {
|
||||
runtime_diag_record_error("wifi_start", err, "register WIFI event failed");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (s_ip_event_inst == NULL) {
|
||||
err = esp_event_handler_instance_register(IP_EVENT,
|
||||
IP_EVENT_STA_GOT_IP,
|
||||
&wifi_event_handler,
|
||||
NULL));
|
||||
|
||||
esp_err_t err = start_sta_mode();
|
||||
NULL,
|
||||
&s_ip_event_inst);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "STA-only mode failed to connect (%s)", esp_err_to_name(err));
|
||||
runtime_diag_record_error("wifi_start", err, "register IP event failed");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
s_started = true;
|
||||
err = start_sta_mode();
|
||||
if (err == ESP_OK) {
|
||||
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_WIFI_CONNECT_SUCCESS, 1);
|
||||
} else {
|
||||
if (err == ESP_ERR_TIMEOUT) {
|
||||
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_WIFI_CONNECT_TIMEOUT, 1);
|
||||
} else {
|
||||
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_WIFI_CONNECT_FAILED, 1);
|
||||
}
|
||||
runtime_diag_record_error("wifi_connect", err, "STA connect failed");
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t wifi_manager_stop(void) {
|
||||
if (!s_started) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t first_err = ESP_OK;
|
||||
|
||||
s_ready = false;
|
||||
s_retry_num = 0;
|
||||
|
||||
if (s_wifi_event_inst != NULL) {
|
||||
esp_err_t err = esp_event_handler_instance_unregister(WIFI_EVENT,
|
||||
ESP_EVENT_ANY_ID,
|
||||
s_wifi_event_inst);
|
||||
if (err != ESP_OK && first_err == ESP_OK) {
|
||||
first_err = err;
|
||||
}
|
||||
s_wifi_event_inst = NULL;
|
||||
}
|
||||
|
||||
if (s_ip_event_inst != NULL) {
|
||||
esp_err_t err = esp_event_handler_instance_unregister(IP_EVENT,
|
||||
IP_EVENT_STA_GOT_IP,
|
||||
s_ip_event_inst);
|
||||
if (err != ESP_OK && first_err == ESP_OK) {
|
||||
first_err = err;
|
||||
}
|
||||
s_ip_event_inst = NULL;
|
||||
}
|
||||
|
||||
esp_err_t err = esp_wifi_disconnect();
|
||||
if (err != ESP_OK && err != ESP_ERR_WIFI_NOT_STARTED && err != ESP_ERR_WIFI_CONN) {
|
||||
if (first_err == ESP_OK) {
|
||||
first_err = err;
|
||||
}
|
||||
}
|
||||
|
||||
err = esp_wifi_stop();
|
||||
if (err != ESP_OK && err != ESP_ERR_WIFI_NOT_INIT && err != ESP_ERR_WIFI_NOT_STARTED) {
|
||||
if (first_err == ESP_OK) {
|
||||
first_err = err;
|
||||
}
|
||||
}
|
||||
|
||||
err = esp_wifi_deinit();
|
||||
if (err != ESP_OK && err != ESP_ERR_WIFI_NOT_INIT) {
|
||||
if (first_err == ESP_OK) {
|
||||
first_err = err;
|
||||
}
|
||||
}
|
||||
|
||||
if (s_sta_netif != NULL) {
|
||||
esp_netif_destroy_default_wifi(s_sta_netif);
|
||||
s_sta_netif = NULL;
|
||||
}
|
||||
|
||||
if (s_wifi_event_group != NULL) {
|
||||
vEventGroupDelete(s_wifi_event_group);
|
||||
s_wifi_event_group = NULL;
|
||||
}
|
||||
|
||||
s_started = false;
|
||||
|
||||
if (first_err != ESP_OK) {
|
||||
runtime_diag_record_error("wifi_stop", first_err, "wifi stop failed");
|
||||
}
|
||||
|
||||
return first_err;
|
||||
}
|
||||
|
||||
bool wifi_manager_is_ready(void) {
|
||||
return s_ready;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user