refactor(architecture): enforce component layering constraints

- move runtime policy/diag contracts to domain-owned facade and remove control_plane leapfrog usage

- enforce single declaration point for direct debug config in domain public API (constraint #6)

- harden timeout/idempotent semantics in REST/voice/printer/wifi/ble paths
This commit is contained in:
admin
2026-02-28 14:21:17 +08:00
parent 8a55ca0005
commit a549989332
23 changed files with 718 additions and 331 deletions

View File

@@ -61,26 +61,31 @@ typedef struct {
const volatile bool *cancel_flag;
} platform_direct_print_request_t;
typedef struct {
uint16_t shift_clock_high_us;
uint16_t shift_clock_low_us;
uint16_t latch_pulse_us;
bool strobe_active_high;
bool boost_active_high;
uint16_t override_strobe_on_us;
uint16_t override_strobe_interval_us;
uint16_t override_motor_step_us;
uint8_t override_steps_per_line;
} platform_direct_debug_config_t;
esp_err_t platform_direct_printer_init(void);
void platform_direct_printer_deinit(void);
esp_err_t platform_direct_printer_connect(uint32_t timeout_ms);
void platform_direct_printer_disconnect(void);
bool platform_direct_printer_is_connected(void);
esp_err_t platform_direct_printer_get_sensors(platform_printer_sensors_t *out_sensors);
esp_err_t platform_direct_printer_get_debug_config(platform_direct_debug_config_t *out_config);
esp_err_t platform_direct_printer_set_debug_config(const platform_direct_debug_config_t *config,
esp_err_t platform_direct_printer_get_debug_config(uint16_t *out_shift_clock_high_us,
uint16_t *out_shift_clock_low_us,
uint16_t *out_latch_pulse_us,
bool *out_strobe_active_high,
bool *out_boost_active_high,
uint16_t *out_override_strobe_on_us,
uint16_t *out_override_strobe_interval_us,
uint16_t *out_override_motor_step_us,
uint8_t *out_override_steps_per_line);
esp_err_t platform_direct_printer_set_debug_config(bool has_config,
uint16_t shift_clock_high_us,
uint16_t shift_clock_low_us,
uint16_t latch_pulse_us,
bool strobe_active_high,
bool boost_active_high,
uint16_t override_strobe_on_us,
uint16_t override_strobe_interval_us,
uint16_t override_motor_step_us,
uint8_t override_steps_per_line,
bool reset_defaults,
char *err,
size_t err_len);
@@ -115,11 +120,15 @@ void voice_audio_close(void);
bool voice_audio_is_open(void);
// timeout_ms applies to voice-audio mutex acquisition.
// Underlying codec driver I/O duration depends on driver/clocking state.
esp_err_t voice_audio_read_pcm(int16_t *pcm,
size_t samples,
uint32_t timeout_ms,
char *err,
size_t err_len);
// timeout_ms applies to voice-audio mutex acquisition.
// Underlying codec driver I/O duration depends on driver/clocking state.
esp_err_t voice_audio_write_pcm(const int16_t *pcm,
size_t samples,
uint32_t timeout_ms,

View File

@@ -52,6 +52,8 @@ static bool s_scanning;
static bool s_notify_ready;
static bool s_host_synced;
static bool s_initialized;
static bool s_connecting;
static uint32_t s_connect_timeout_ms = 15000;
static uint16_t uuid16(const ble_uuid_t *uuid) {
if (uuid == NULL || uuid->type != BLE_UUID_TYPE_16) {
@@ -397,12 +399,13 @@ static int gap_event_cb(struct ble_gap_event *event, void *arg) {
rc = ble_gap_connect(s_addr_type,
&s_target_addr,
30000,
s_connect_timeout_ms > 30000 ? 30000 : s_connect_timeout_ms,
&conn_params,
gap_event_cb,
NULL);
if (rc != 0) {
ESP_LOGE(TAG, "ble_gap_connect failed rc=%d", rc);
s_connecting = false;
signal_failure();
} else {
ESP_LOGI(TAG, "Connecting...");
@@ -417,6 +420,11 @@ static int gap_event_cb(struct ble_gap_event *event, void *arg) {
case BLE_GAP_EVENT_CONNECT:
if (event->connect.status == 0) {
if (!s_connecting) {
(void)ble_gap_terminate(event->connect.conn_handle, BLE_ERR_REM_USER_CONN_TERM);
return 0;
}
s_connecting = false;
s_conn_handle = event->connect.conn_handle;
xEventGroupSetBits(s_evt_group, EVT_CONNECTED);
ESP_LOGI(TAG, "Connected handle=%u", s_conn_handle);
@@ -424,6 +432,7 @@ static int gap_event_cb(struct ble_gap_event *event, void *arg) {
ble_gattc_exchange_mtu(s_conn_handle, NULL, NULL);
start_service_discovery();
} else {
s_connecting = false;
ESP_LOGE(TAG, "Connect failed status=%d", event->connect.status);
signal_failure();
}
@@ -431,6 +440,7 @@ static int gap_event_cb(struct ble_gap_event *event, void *arg) {
case BLE_GAP_EVENT_DISCONNECT:
ESP_LOGW(TAG, "Disconnected reason=%d", event->disconnect.reason);
s_connecting = false;
s_conn_handle = BLE_HS_CONN_HANDLE_NONE;
s_notify_ready = false;
reset_discovery_state();
@@ -564,6 +574,13 @@ esp_err_t ble_printer_client_connect(const char *target_name, uint32_t timeout_m
return ESP_ERR_INVALID_STATE;
}
uint32_t effective_timeout_ms = timeout_ms;
if (effective_timeout_ms < 1000) {
effective_timeout_ms = 1000;
} else if (effective_timeout_ms > 60000) {
effective_timeout_ms = 60000;
}
s_match_any_compatible = false;
if (target_name != NULL && target_name[0] != '\0') {
if (strcmp(target_name, "*") == 0) {
@@ -581,9 +598,15 @@ esp_err_t ble_printer_client_connect(const char *target_name, uint32_t timeout_m
xSemaphoreGive(s_lock);
return ESP_OK;
}
if (s_connecting) {
xSemaphoreGive(s_lock);
return ESP_ERR_INVALID_STATE;
}
xEventGroupClearBits(s_evt_group, EVT_CONNECTED | EVT_READY | EVT_FAILED);
reset_discovery_state();
s_connect_timeout_ms = effective_timeout_ms;
s_connecting = true;
int wait_sync_ms = 3000;
while (!s_host_synced && wait_sync_ms > 0) {
@@ -602,36 +625,21 @@ esp_err_t ble_printer_client_connect(const char *target_name, uint32_t timeout_m
EVT_READY | EVT_FAILED,
pdFALSE,
pdFALSE,
pdMS_TO_TICKS(timeout_ms));
pdMS_TO_TICKS(effective_timeout_ms));
if (bits & EVT_READY) {
return ESP_OK;
}
if (xSemaphoreTake(s_lock, pdMS_TO_TICKS(500)) == pdTRUE) {
stop_scan_if_running();
xSemaphoreGive(s_lock);
}
// Similar to Android app behavior: quick retry when initial attempt fails or times out.
if (xSemaphoreTake(s_lock, pdMS_TO_TICKS(1000)) == pdTRUE) {
xEventGroupClearBits(s_evt_group, EVT_CONNECTED | EVT_READY | EVT_FAILED);
stop_scan_if_running();
if (s_conn_handle != BLE_HS_CONN_HANDLE_NONE) {
(void)ble_gap_terminate(s_conn_handle, BLE_ERR_REM_USER_CONN_TERM);
}
s_connecting = false;
reset_discovery_state();
start_scan();
xSemaphoreGive(s_lock);
}
bits = xEventGroupWaitBits(s_evt_group,
EVT_READY | EVT_FAILED,
pdFALSE,
pdFALSE,
pdMS_TO_TICKS(timeout_ms));
if (bits & EVT_READY) {
return ESP_OK;
}
if (xSemaphoreTake(s_lock, pdMS_TO_TICKS(500)) == pdTRUE) {
stop_scan_if_running();
xSemaphoreGive(s_lock);
}
if (bits & EVT_FAILED) {
return ESP_FAIL;
}
@@ -653,6 +661,7 @@ void ble_printer_client_disconnect(void) {
ble_gap_terminate(s_conn_handle, BLE_ERR_REM_USER_CONN_TERM);
}
s_connecting = false;
s_conn_handle = BLE_HS_CONN_HANDLE_NONE;
reset_discovery_state();
xSemaphoreGive(s_lock);

View File

@@ -49,11 +49,23 @@ typedef struct {
adc_channel_t channel;
} adc_pin_t;
typedef struct {
uint16_t shift_clock_high_us;
uint16_t shift_clock_low_us;
uint16_t latch_pulse_us;
bool strobe_active_high;
bool boost_active_high;
uint16_t override_strobe_on_us;
uint16_t override_strobe_interval_us;
uint16_t override_motor_step_us;
uint8_t override_steps_per_line;
} direct_debug_config_t;
typedef struct {
bool initialized;
bool connected;
uint8_t motor_phase;
platform_direct_debug_config_t debug;
direct_debug_config_t debug;
SemaphoreHandle_t lock;
adc_oneshot_unit_handle_t adc1_handle;
adc_oneshot_unit_handle_t adc2_handle;
@@ -567,8 +579,8 @@ void platform_direct_printer_deinit(void) {
}
esp_err_t platform_direct_printer_connect(uint32_t timeout_ms) {
(void)timeout_ms;
#if !CONFIG_TQ_DIRECT_PRINTER_ENABLE
(void)timeout_ms;
return ESP_ERR_NOT_SUPPORTED;
#else
esp_err_t err = platform_direct_printer_init();
@@ -576,7 +588,12 @@ esp_err_t platform_direct_printer_connect(uint32_t timeout_ms) {
return err;
}
if (xSemaphoreTake(s_state.lock, pdMS_TO_TICKS(500)) != pdTRUE) {
uint32_t lock_timeout_ms = timeout_ms == 0 ? 500 : timeout_ms;
if (lock_timeout_ms > 5000) {
lock_timeout_ms = 5000;
}
if (xSemaphoreTake(s_state.lock, pdMS_TO_TICKS(lock_timeout_ms)) != pdTRUE) {
return ESP_ERR_TIMEOUT;
}
@@ -631,12 +648,31 @@ esp_err_t platform_direct_printer_get_sensors(platform_printer_sensors_t *out_se
return ESP_OK;
}
esp_err_t platform_direct_printer_get_debug_config(platform_direct_debug_config_t *out_config) {
esp_err_t platform_direct_printer_get_debug_config(uint16_t *out_shift_clock_high_us,
uint16_t *out_shift_clock_low_us,
uint16_t *out_latch_pulse_us,
bool *out_strobe_active_high,
bool *out_boost_active_high,
uint16_t *out_override_strobe_on_us,
uint16_t *out_override_strobe_interval_us,
uint16_t *out_override_motor_step_us,
uint8_t *out_override_steps_per_line) {
#if !CONFIG_TQ_DIRECT_PRINTER_ENABLE
(void)out_config;
(void)out_shift_clock_high_us;
(void)out_shift_clock_low_us;
(void)out_latch_pulse_us;
(void)out_strobe_active_high;
(void)out_boost_active_high;
(void)out_override_strobe_on_us;
(void)out_override_strobe_interval_us;
(void)out_override_motor_step_us;
(void)out_override_steps_per_line;
return ESP_ERR_NOT_SUPPORTED;
#else
if (out_config == NULL) {
if (out_shift_clock_high_us == NULL || out_shift_clock_low_us == NULL || out_latch_pulse_us == NULL ||
out_strobe_active_high == NULL || out_boost_active_high == NULL || out_override_strobe_on_us == NULL ||
out_override_strobe_interval_us == NULL || out_override_motor_step_us == NULL ||
out_override_steps_per_line == NULL) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t init_rc = platform_direct_printer_init();
@@ -646,23 +682,49 @@ esp_err_t platform_direct_printer_get_debug_config(platform_direct_debug_config_
if (xSemaphoreTake(s_state.lock, pdMS_TO_TICKS(500)) != pdTRUE) {
return ESP_ERR_TIMEOUT;
}
*out_config = s_state.debug;
*out_shift_clock_high_us = s_state.debug.shift_clock_high_us;
*out_shift_clock_low_us = s_state.debug.shift_clock_low_us;
*out_latch_pulse_us = s_state.debug.latch_pulse_us;
*out_strobe_active_high = s_state.debug.strobe_active_high;
*out_boost_active_high = s_state.debug.boost_active_high;
*out_override_strobe_on_us = s_state.debug.override_strobe_on_us;
*out_override_strobe_interval_us = s_state.debug.override_strobe_interval_us;
*out_override_motor_step_us = s_state.debug.override_motor_step_us;
*out_override_steps_per_line = s_state.debug.override_steps_per_line;
xSemaphoreGive(s_state.lock);
return ESP_OK;
#endif
}
esp_err_t platform_direct_printer_set_debug_config(const platform_direct_debug_config_t *config,
esp_err_t platform_direct_printer_set_debug_config(bool has_config,
uint16_t shift_clock_high_us,
uint16_t shift_clock_low_us,
uint16_t latch_pulse_us,
bool strobe_active_high,
bool boost_active_high,
uint16_t override_strobe_on_us,
uint16_t override_strobe_interval_us,
uint16_t override_motor_step_us,
uint8_t override_steps_per_line,
bool reset_defaults,
char *err,
size_t err_len) {
#if !CONFIG_TQ_DIRECT_PRINTER_ENABLE
(void)config;
(void)has_config;
(void)shift_clock_high_us;
(void)shift_clock_low_us;
(void)latch_pulse_us;
(void)strobe_active_high;
(void)boost_active_high;
(void)override_strobe_on_us;
(void)override_strobe_interval_us;
(void)override_motor_step_us;
(void)override_steps_per_line;
(void)reset_defaults;
write_err(err, err_len, "direct backend disabled");
return ESP_ERR_NOT_SUPPORTED;
#else
if (config == NULL && !reset_defaults) {
if (!has_config && !reset_defaults) {
write_err(err, err_len, "invalid args");
return ESP_ERR_INVALID_ARG;
}
@@ -681,45 +743,48 @@ esp_err_t platform_direct_printer_set_debug_config(const platform_direct_debug_c
set_debug_defaults_locked();
}
if (config != NULL) {
if (config->shift_clock_high_us < DEBUG_SHIFT_CLOCK_US_MIN ||
config->shift_clock_high_us > DEBUG_SHIFT_CLOCK_US_MAX ||
config->shift_clock_low_us < DEBUG_SHIFT_CLOCK_US_MIN ||
config->shift_clock_low_us > DEBUG_SHIFT_CLOCK_US_MAX) {
if (has_config) {
if (shift_clock_high_us < DEBUG_SHIFT_CLOCK_US_MIN || shift_clock_high_us > DEBUG_SHIFT_CLOCK_US_MAX ||
shift_clock_low_us < DEBUG_SHIFT_CLOCK_US_MIN || shift_clock_low_us > DEBUG_SHIFT_CLOCK_US_MAX) {
xSemaphoreGive(s_state.lock);
write_err(err, err_len, "shift clock us out of range (1..50)");
return ESP_ERR_INVALID_ARG;
}
if (config->latch_pulse_us < DEBUG_LATCH_PULSE_US_MIN ||
config->latch_pulse_us > DEBUG_LATCH_PULSE_US_MAX) {
if (latch_pulse_us < DEBUG_LATCH_PULSE_US_MIN || latch_pulse_us > DEBUG_LATCH_PULSE_US_MAX) {
xSemaphoreGive(s_state.lock);
write_err(err, err_len, "latch pulse us out of range (1..50)");
return ESP_ERR_INVALID_ARG;
}
if (config->override_strobe_on_us != 0 &&
(config->override_strobe_on_us < 100 || config->override_strobe_on_us > 10000)) {
if (override_strobe_on_us != 0 && (override_strobe_on_us < 100 || override_strobe_on_us > 10000)) {
xSemaphoreGive(s_state.lock);
write_err(err, err_len, "strobe_on_us must be 0 or 100..10000");
return ESP_ERR_INVALID_ARG;
}
if (config->override_strobe_interval_us > 10000) {
if (override_strobe_interval_us > 10000) {
xSemaphoreGive(s_state.lock);
write_err(err, err_len, "strobe_interval_us must be 0..10000");
return ESP_ERR_INVALID_ARG;
}
if (config->override_motor_step_us != 0 &&
(config->override_motor_step_us < 100 || config->override_motor_step_us > 20000)) {
if (override_motor_step_us != 0 && (override_motor_step_us < 100 || override_motor_step_us > 20000)) {
xSemaphoreGive(s_state.lock);
write_err(err, err_len, "motor_step_us must be 0 or 100..20000");
return ESP_ERR_INVALID_ARG;
}
if (config->override_steps_per_line > 8) {
if (override_steps_per_line > 8) {
xSemaphoreGive(s_state.lock);
write_err(err, err_len, "steps_per_line must be 0..8");
return ESP_ERR_INVALID_ARG;
}
s_state.debug = *config;
s_state.debug.shift_clock_high_us = shift_clock_high_us;
s_state.debug.shift_clock_low_us = shift_clock_low_us;
s_state.debug.latch_pulse_us = latch_pulse_us;
s_state.debug.strobe_active_high = strobe_active_high;
s_state.debug.boost_active_high = boost_active_high;
s_state.debug.override_strobe_on_us = override_strobe_on_us;
s_state.debug.override_strobe_interval_us = override_strobe_interval_us;
s_state.debug.override_motor_step_us = override_motor_step_us;
s_state.debug.override_steps_per_line = override_steps_per_line;
}
safe_drive_off_locked(s_state.connected);

View File

@@ -292,7 +292,9 @@ void lcd_module_deinit(void)
{
#if CONFIG_TQ_SCREEN_ENABLE
if (s_lcd.lock != NULL) {
(void)xSemaphoreTake(s_lcd.lock, portMAX_DELAY);
if (xSemaphoreTake(s_lcd.lock, pdMS_TO_TICKS(1000)) != pdTRUE) {
return;
}
}
if (s_lcd.lv_disp != NULL) {

View File

@@ -56,12 +56,18 @@ static void wifi_event_handler(void *arg,
(void)event_data;
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
if (!s_started) {
return;
}
(void)esp_wifi_connect();
return;
}
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
s_ready = false;
if (!s_started) {
return;
}
if (s_retry_num < CONFIG_TQ_WIFI_MAXIMUM_RETRY) {
(void)esp_wifi_connect();
s_retry_num++;
@@ -73,6 +79,9 @@ static void wifi_event_handler(void *arg,
}
if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
if (!s_started) {
return;
}
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
@@ -218,6 +227,7 @@ esp_err_t wifi_manager_start(void) {
runtime_diag_counter_add(RUNTIME_DIAG_COUNTER_WIFI_CONNECT_FAILED, 1);
}
runtime_diag_record_error("wifi_connect", err, "STA connect failed");
(void)wifi_manager_stop();
}
return err;
}