修复了WS连接稳定性问题
This commit is contained in:
@@ -66,6 +66,8 @@ typedef void (*control_plane_multimodal_downstream_audio_callback_t)(const uint8
|
||||
|
||||
typedef struct {
|
||||
bool connected;
|
||||
bool starting;
|
||||
bool stopping;
|
||||
bool has_active_task;
|
||||
bool server_listening;
|
||||
bool remote_responding;
|
||||
@@ -86,6 +88,7 @@ typedef enum {
|
||||
CONTROL_PLANE_TRANSPORT_LOSS_SEND_BINARY_FAILURE,
|
||||
CONTROL_PLANE_TRANSPORT_LOSS_LISTENING_TIMEOUT,
|
||||
CONTROL_PLANE_TRANSPORT_LOSS_NO_RESPONSE_TIMEOUT,
|
||||
CONTROL_PLANE_TRANSPORT_LOSS_PROTOCOL_ERROR,
|
||||
} control_plane_transport_loss_reason_t;
|
||||
|
||||
esp_err_t control_plane_multimodal_start(void);
|
||||
|
||||
+145
-6
@@ -119,6 +119,8 @@
|
||||
static const char *TAG = "cp_mm_session";
|
||||
|
||||
static bool s_started;
|
||||
static bool s_starting;
|
||||
static bool s_stopping;
|
||||
|
||||
static control_plane_multimodal_auth_info_t s_auth;
|
||||
static char s_task_id[40];
|
||||
@@ -167,6 +169,7 @@ static StaticSemaphore_t s_state_mutex_buf;
|
||||
typedef enum {
|
||||
LOCAL_RESPONDING_EVENT_STARTED = 0,
|
||||
LOCAL_RESPONDING_EVENT_ENDED,
|
||||
LOCAL_RESPONDING_EVENT_QUIT,
|
||||
} local_responding_event_type_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -418,6 +421,8 @@ static const char *transport_loss_reason_name(control_plane_transport_loss_reaso
|
||||
return "listening_timeout";
|
||||
case CONTROL_PLANE_TRANSPORT_LOSS_NO_RESPONSE_TIMEOUT:
|
||||
return "no_response_timeout";
|
||||
case CONTROL_PLANE_TRANSPORT_LOSS_PROTOCOL_ERROR:
|
||||
return "protocol_error";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
@@ -444,6 +449,8 @@ void control_plane_multimodal_mark_transport_unavailable(
|
||||
|
||||
if (lock_state()) {
|
||||
s_started = false;
|
||||
s_starting = false;
|
||||
s_stopping = false;
|
||||
s_transport_available = false;
|
||||
memset(&s_auth, 0, sizeof(s_auth));
|
||||
reset_session_state_locked();
|
||||
@@ -451,6 +458,19 @@ void control_plane_multimodal_mark_transport_unavailable(
|
||||
}
|
||||
|
||||
control_plane_multimodal_stop_uplink();
|
||||
|
||||
bool close_transport = reason == CONTROL_PLANE_TRANSPORT_LOSS_PROTOCOL_ERROR ||
|
||||
reason == CONTROL_PLANE_TRANSPORT_LOSS_SEND_TEXT_FAILURE ||
|
||||
reason == CONTROL_PLANE_TRANSPORT_LOSS_LISTENING_TIMEOUT ||
|
||||
reason == CONTROL_PLANE_TRANSPORT_LOSS_NO_RESPONSE_TIMEOUT;
|
||||
if (close_transport) {
|
||||
esp_err_t stop_err = domain_runtime_multimodal_transport_stop(0);
|
||||
if (stop_err != ESP_OK) {
|
||||
ESP_LOGW(TAG,
|
||||
"Async transport stop after unavailable ended with %s",
|
||||
esp_err_to_name(stop_err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void reset_session_state(void)
|
||||
@@ -523,7 +543,19 @@ static void local_responding_event_worker(void *arg)
|
||||
idle_ticks = 1;
|
||||
}
|
||||
if (xQueueReceive(s_local_responding_event_queue, &msg, idle_ticks) != pdTRUE) {
|
||||
s_local_responding_event_task = NULL;
|
||||
if (lock_state()) {
|
||||
s_local_responding_event_task = NULL;
|
||||
unlock_state();
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.type == LOCAL_RESPONDING_EVENT_QUIT) {
|
||||
if (lock_state()) {
|
||||
s_local_responding_event_task = NULL;
|
||||
unlock_state();
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
return;
|
||||
}
|
||||
@@ -594,6 +626,50 @@ static esp_err_t ensure_local_responding_event_worker(void)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void stop_local_responding_event_worker(void)
|
||||
{
|
||||
QueueHandle_t queue = NULL;
|
||||
TaskHandle_t task = NULL;
|
||||
if (lock_state()) {
|
||||
queue = s_local_responding_event_queue;
|
||||
task = s_local_responding_event_task;
|
||||
s_local_responding_started_send_pending = false;
|
||||
s_local_responding_ended_send_pending = false;
|
||||
unlock_state();
|
||||
}
|
||||
|
||||
if (queue != NULL && task != NULL) {
|
||||
local_responding_event_msg_t quit = {
|
||||
.type = LOCAL_RESPONDING_EVENT_QUIT,
|
||||
};
|
||||
(void)xQueueSend(queue, &quit, 0);
|
||||
TickType_t start_tick = xTaskGetTickCount();
|
||||
TickType_t wait_ticks = pdMS_TO_TICKS(300);
|
||||
if (wait_ticks == 0) {
|
||||
wait_ticks = 1;
|
||||
}
|
||||
while ((xTaskGetTickCount() - start_tick) < wait_ticks) {
|
||||
bool running = false;
|
||||
if (lock_state()) {
|
||||
running = s_local_responding_event_task != NULL;
|
||||
unlock_state();
|
||||
}
|
||||
if (!running) {
|
||||
break;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
}
|
||||
}
|
||||
|
||||
if (lock_state()) {
|
||||
s_local_responding_event_task = NULL;
|
||||
if (s_local_responding_event_queue != NULL) {
|
||||
xQueueReset(s_local_responding_event_queue);
|
||||
}
|
||||
unlock_state();
|
||||
}
|
||||
}
|
||||
|
||||
static void queue_local_responding_event(local_responding_event_type_t type)
|
||||
{
|
||||
char task_id[sizeof(s_task_id)] = {0};
|
||||
@@ -1198,7 +1274,12 @@ static void process_text_json(const char *json_text)
|
||||
request_id,
|
||||
dialog_id[0] ? dialog_id : "-");
|
||||
|
||||
if (cJSON_IsString(failed_task_id) &&
|
||||
bool protocol_error = strcmp(error_code, "InvalidParameter") == 0 ||
|
||||
strcmp(error_name, "InvalidParameter") == 0 ||
|
||||
strstr(error_message, "Invalid action") != NULL;
|
||||
|
||||
if (!protocol_error &&
|
||||
cJSON_IsString(failed_task_id) &&
|
||||
failed_task_id->valuestring != NULL &&
|
||||
failed_task_id->valuestring[0] != '\0') {
|
||||
bool matches_active_task = false;
|
||||
@@ -1218,7 +1299,13 @@ static void process_text_json(const char *json_text)
|
||||
}
|
||||
|
||||
stop_local_downstream_playback();
|
||||
reset_session_state_if_task_matches(task_id);
|
||||
if (protocol_error) {
|
||||
control_plane_multimodal_mark_transport_unavailable(
|
||||
CONTROL_PLANE_TRANSPORT_LOSS_PROTOCOL_ERROR,
|
||||
__func__);
|
||||
} else {
|
||||
reset_session_state_if_task_matches(task_id);
|
||||
}
|
||||
notify_event(CONTROL_PLANE_MULTIMODAL_EVENT_TASK_FAILED);
|
||||
} else if (strcmp(event->valuestring, "task-started") == 0) {
|
||||
notify_event(CONTROL_PLANE_MULTIMODAL_EVENT_TASK_STARTED);
|
||||
@@ -1378,8 +1465,10 @@ static void process_text_json(const char *json_text)
|
||||
cJSON *error_message = cJSON_GetObjectItemCaseSensitive(output, "error_message");
|
||||
cJSON *llm_request_id = cJSON_GetObjectItemCaseSensitive(output, "llm_request_id");
|
||||
char dialog_id[sizeof(s_dialog_id)] = {0};
|
||||
char task_id[sizeof(s_task_id)] = {0};
|
||||
if (lock_state()) {
|
||||
s_remote_responding = false;
|
||||
strlcpy(task_id, s_task_id, sizeof(task_id));
|
||||
strlcpy(dialog_id, s_dialog_id, sizeof(dialog_id));
|
||||
unlock_state();
|
||||
}
|
||||
@@ -1390,6 +1479,11 @@ static void process_text_json(const char *json_text)
|
||||
cJSON_IsString(error_message) && error_message->valuestring ? error_message->valuestring : "unknown",
|
||||
cJSON_IsString(llm_request_id) && llm_request_id->valuestring ? llm_request_id->valuestring : "-",
|
||||
dialog_id[0] ? dialog_id : "-");
|
||||
control_plane_multimodal_stop_uplink();
|
||||
stop_local_downstream_playback();
|
||||
control_plane_multimodal_mark_transport_unavailable(
|
||||
CONTROL_PLANE_TRANSPORT_LOSS_PROTOCOL_ERROR,
|
||||
__func__);
|
||||
notify_event(CONTROL_PLANE_MULTIMODAL_EVENT_OUTPUT_ERROR);
|
||||
}
|
||||
}
|
||||
@@ -1409,6 +1503,9 @@ static void transport_event_bridge(const domain_multimodal_transport_event_t *ev
|
||||
switch (event->type) {
|
||||
case DOMAIN_MULTIMODAL_TRANSPORT_EVENT_CONNECTED:
|
||||
if (lock_state()) {
|
||||
s_started = true;
|
||||
s_starting = false;
|
||||
s_stopping = false;
|
||||
s_transport_available = true;
|
||||
s_last_heartbeat_tick = xTaskGetTickCount();
|
||||
s_last_downstream_tick = s_last_heartbeat_tick;
|
||||
@@ -1538,17 +1635,29 @@ esp_err_t control_plane_multimodal_start(void)
|
||||
#endif
|
||||
|
||||
if (lock_state()) {
|
||||
bool already_started = s_started;
|
||||
unlock_state();
|
||||
bool already_started = s_started || s_auth.token[0] != '\0';
|
||||
bool starting = s_starting;
|
||||
bool stopping = s_stopping;
|
||||
if (already_started) {
|
||||
unlock_state();
|
||||
return ESP_OK;
|
||||
}
|
||||
if (starting || stopping) {
|
||||
unlock_state();
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
s_starting = true;
|
||||
unlock_state();
|
||||
}
|
||||
|
||||
control_plane_multimodal_auth_info_t auth = {0};
|
||||
esp_err_t err = control_plane_multimodal_auth_fetch(&auth);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Auth fetch failed: %s", esp_err_to_name(err));
|
||||
if (lock_state()) {
|
||||
s_starting = false;
|
||||
unlock_state();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1557,6 +1666,10 @@ esp_err_t control_plane_multimodal_start(void)
|
||||
NULL);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Register downstream playback callback failed: %s", esp_err_to_name(err));
|
||||
if (lock_state()) {
|
||||
s_starting = false;
|
||||
unlock_state();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1564,12 +1677,15 @@ esp_err_t control_plane_multimodal_start(void)
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Transport start failed: %s", esp_err_to_name(err));
|
||||
(void)domain_runtime_multimodal_audio_register_downstream_playback_callback(NULL, NULL);
|
||||
if (lock_state()) {
|
||||
s_starting = false;
|
||||
unlock_state();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
if (lock_state()) {
|
||||
s_auth = auth;
|
||||
s_started = true;
|
||||
unlock_state();
|
||||
}
|
||||
return ESP_OK;
|
||||
@@ -1577,7 +1693,22 @@ esp_err_t control_plane_multimodal_start(void)
|
||||
|
||||
void control_plane_multimodal_stop(void)
|
||||
{
|
||||
bool already_stopping = false;
|
||||
if (lock_state()) {
|
||||
already_stopping = s_stopping;
|
||||
if (!s_stopping) {
|
||||
s_stopping = true;
|
||||
}
|
||||
s_starting = false;
|
||||
unlock_state();
|
||||
}
|
||||
if (already_stopping) {
|
||||
ESP_LOGI(TAG, "Multimodal stop already in progress");
|
||||
return;
|
||||
}
|
||||
|
||||
control_plane_multimodal_stop_uplink();
|
||||
stop_local_responding_event_worker();
|
||||
(void)control_plane_multimodal_register_downstream_audio_callback(NULL, NULL);
|
||||
(void)control_plane_multimodal_register_event_callback(NULL, NULL);
|
||||
(void)domain_runtime_multimodal_audio_register_downstream_playback_callback(NULL, NULL);
|
||||
@@ -1589,6 +1720,9 @@ void control_plane_multimodal_stop(void)
|
||||
|
||||
if (lock_state()) {
|
||||
s_started = false;
|
||||
s_starting = false;
|
||||
s_stopping = false;
|
||||
s_transport_available = false;
|
||||
memset(&s_auth, 0, sizeof(s_auth));
|
||||
reset_session_state_locked();
|
||||
unlock_state();
|
||||
@@ -2123,6 +2257,8 @@ esp_err_t control_plane_multimodal_get_runtime_state(control_plane_multimodal_ru
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
out_state->connected = transport_available_locked();
|
||||
out_state->starting = s_starting;
|
||||
out_state->stopping = s_stopping;
|
||||
out_state->has_active_task = s_task_id[0] != '\0';
|
||||
out_state->server_listening = s_server_listening;
|
||||
out_state->remote_responding = s_remote_responding;
|
||||
@@ -2227,6 +2363,9 @@ esp_err_t control_plane_multimodal_start_uplink(void)
|
||||
|
||||
void control_plane_multimodal_stop_uplink(void)
|
||||
{
|
||||
if (!domain_runtime_multimodal_audio_is_uplink_running()) {
|
||||
return;
|
||||
}
|
||||
esp_err_t err = domain_runtime_multimodal_audio_stop_uplink(CONTROL_PLANE_UPLINK_READY_WAIT_MS);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Mic uplink stop ended with %s", esp_err_to_name(err));
|
||||
|
||||
@@ -48,6 +48,10 @@
|
||||
#define CONFIG_TQ_KEY_INPUT_POLL_MS 20
|
||||
#endif
|
||||
|
||||
#ifndef CONTROL_PLANE_PTT_INITIAL_INACTIVE_STABLE_MS
|
||||
#define CONTROL_PLANE_PTT_INITIAL_INACTIVE_STABLE_MS 120
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_LOCAL_TASK_CORE
|
||||
#define CONFIG_TQ_LOCAL_TASK_CORE 1
|
||||
#endif
|
||||
@@ -98,6 +102,7 @@ typedef struct {
|
||||
bool long_press_sent;
|
||||
bool click_pending;
|
||||
int64_t debounce_deadline_ms;
|
||||
int64_t inactive_since_ms;
|
||||
int64_t press_start_ms;
|
||||
int64_t click_deadline_ms;
|
||||
} control_plane_key_state_t;
|
||||
@@ -113,9 +118,9 @@ static const control_plane_key_config_t s_key_configs[] = {
|
||||
.long_press_ms = CONFIG_TQ_KEY_LONG_PRESS_MS,
|
||||
.double_click_ms = CONFIG_TQ_KEY_DOUBLE_CLICK_MS,
|
||||
.emit_press_release = true,
|
||||
.enable_short_press = true,
|
||||
.enable_short_press = false,
|
||||
.enable_long_press = false,
|
||||
.enable_double_click = true,
|
||||
.enable_double_click = false,
|
||||
},
|
||||
{
|
||||
.key_id = CONTROL_PLANE_KEY_ID_SW2,
|
||||
@@ -256,9 +261,14 @@ static void control_plane_handle_debounced_transition(const control_plane_key_co
|
||||
if (state->wait_inactive_before_press) {
|
||||
if (pressed) {
|
||||
state->debounced_pressed = false;
|
||||
state->inactive_since_ms = 0;
|
||||
return;
|
||||
}
|
||||
state->wait_inactive_before_press = false;
|
||||
state->inactive_since_ms = 0;
|
||||
ESP_LOGI(TAG,
|
||||
"key=%s initial active gate cleared by debounced inactive level",
|
||||
control_plane_key_id_name(config->key_id));
|
||||
}
|
||||
|
||||
if (pressed == state->debounced_pressed) {
|
||||
@@ -334,6 +344,33 @@ static void control_plane_process_key_timers(void)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state->wait_inactive_before_press) {
|
||||
const int level = gpio_get_level(config->gpio_num);
|
||||
const bool pressed = control_plane_key_level_is_pressed(config, level);
|
||||
if (!pressed) {
|
||||
if (state->inactive_since_ms == 0) {
|
||||
state->inactive_since_ms = now_ms;
|
||||
}
|
||||
uint32_t stable_ms = config->debounce_ms;
|
||||
if (stable_ms < CONTROL_PLANE_PTT_INITIAL_INACTIVE_STABLE_MS) {
|
||||
stable_ms = CONTROL_PLANE_PTT_INITIAL_INACTIVE_STABLE_MS;
|
||||
}
|
||||
if ((now_ms - state->inactive_since_ms) >= stable_ms) {
|
||||
state->wait_inactive_before_press = false;
|
||||
state->debounced_pressed = false;
|
||||
state->debounce_pending = false;
|
||||
state->press_start_ms = 0;
|
||||
state->long_press_sent = false;
|
||||
ESP_LOGI(TAG,
|
||||
"key=%s initial active gate cleared by stable inactive poll (%" PRId64 "ms)",
|
||||
control_plane_key_id_name(config->key_id),
|
||||
now_ms - state->inactive_since_ms);
|
||||
}
|
||||
} else {
|
||||
state->inactive_since_ms = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (state->debounce_pending && now_ms >= state->debounce_deadline_ms) {
|
||||
state->debounce_pending = false;
|
||||
const int level = gpio_get_level(config->gpio_num);
|
||||
@@ -390,6 +427,7 @@ static void control_plane_key_input_task(void *arg)
|
||||
"PTT initial level is active; wait for next stable press before emitting press event");
|
||||
s_key_states[i].debounced_pressed = false;
|
||||
s_key_states[i].wait_inactive_before_press = true;
|
||||
s_key_states[i].inactive_since_ms = 0;
|
||||
}
|
||||
|
||||
setup_err = control_plane_configure_key_gpio(config);
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define CONTROL_PLANE_MODEL_CMD_TASK_STACK_SIZE (1024 * 4)
|
||||
#define CONTROL_PLANE_DIRECTION_TASK_STACK_SIZE (1024 * 4)
|
||||
#define CONTROL_PLANE_LCD_RENDER_LOOP_TASK_STACK_SIZE (1024 * 12)
|
||||
#define CONTROL_PLANE_WIFI_STATE_TASK_STACK_SIZE (1024 * 12)
|
||||
#define CONTROL_PLANE_TASK_MONITOR_TASK_STACK_SIZE (1024 * 6)
|
||||
@@ -29,7 +28,6 @@
|
||||
#define CONTROL_PLANE_MODEL_CMD_RECV_TIMEOUT_MS 200
|
||||
#define CONTROL_PLANE_TASK_STOP_WAIT_MS 1500
|
||||
#define CONTROL_PLANE_TASK_START_GATE_WAIT_MS 5000
|
||||
#define CONTROL_PLANE_INPUT_BUF_SIZE 64
|
||||
#define CONTROL_PLANE_WIFI_STATE_POLL_INTERVAL_MS 1000
|
||||
#define CONTROL_PLANE_PTT_ACTIVE_POLL_INTERVAL_MS 80
|
||||
#define CONTROL_PLANE_WIFI_DISCONNECTED_REFRESH_MS 5000
|
||||
@@ -71,10 +69,6 @@
|
||||
#define CONFIG_TQ_TASK_MONITOR_ENABLE 0
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_DIRECTION_CONSOLE_ENABLE
|
||||
#define CONFIG_TQ_DIRECTION_CONSOLE_ENABLE 0
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_TASK_MONITOR_INTERVAL_MS
|
||||
#define CONFIG_TQ_TASK_MONITOR_INTERVAL_MS 10000
|
||||
#endif
|
||||
@@ -157,6 +151,8 @@ typedef struct {
|
||||
bool foreground_connect;
|
||||
uint8_t foreground_attempts;
|
||||
bool pending_press_after_teardown;
|
||||
bool start_action_sent;
|
||||
bool speech_action_sent;
|
||||
} ptt_runtime_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -170,9 +166,6 @@ static TaskHandle_t s_model_cmd_task;
|
||||
static bool s_ws_start_requested;
|
||||
static bool s_ptt_active;
|
||||
static ptt_runtime_t s_ptt_runtime;
|
||||
#if CONFIG_TQ_DIRECTION_CONSOLE_ENABLE
|
||||
static TaskHandle_t s_direction_console_task;
|
||||
#endif
|
||||
static TaskHandle_t s_lcd_render_loop_task;
|
||||
static TaskHandle_t s_wifi_state_task;
|
||||
#if CONFIG_TQ_TASK_MONITOR_ENABLE
|
||||
@@ -240,10 +233,20 @@ static void control_plane_ptt_transition_locked(ptt_state_t next_state, const ch
|
||||
(void)reason;
|
||||
s_ptt_runtime.state = next_state;
|
||||
s_ptt_runtime.state_enter_tick = xTaskGetTickCount();
|
||||
if (next_state == PTT_STATE_WAIT_START_SENT) {
|
||||
s_ptt_runtime.start_action_sent = false;
|
||||
}
|
||||
if (next_state == PTT_STATE_WAIT_SPEECH_STARTED) {
|
||||
s_ptt_runtime.speech_action_sent = false;
|
||||
}
|
||||
if (next_state == PTT_STATE_IDLE ||
|
||||
next_state == PTT_STATE_STREAMING ||
|
||||
next_state == PTT_STATE_DENIED) {
|
||||
s_ptt_runtime.foreground_connect = false;
|
||||
if (next_state == PTT_STATE_IDLE || next_state == PTT_STATE_DENIED) {
|
||||
s_ptt_runtime.start_action_sent = false;
|
||||
s_ptt_runtime.speech_action_sent = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,6 +264,20 @@ static void control_plane_ptt_log_transition(ptt_state_t old_state,
|
||||
ptt_state_name(next_state),
|
||||
(unsigned)generation,
|
||||
reason != NULL ? reason : "-");
|
||||
control_plane_multimodal_runtime_state_t mm_state = {0};
|
||||
esp_err_t state_err = control_plane_multimodal_get_runtime_state(&mm_state);
|
||||
ESP_LOGI(TAG,
|
||||
"PTT snapshot generation=%u wifi=%d ws(conn=%d starting=%d stopping=%d) task=%d listening=%d speech=%d responding=%d uplink=%d",
|
||||
(unsigned)generation,
|
||||
control_plane_is_sta_connected() ? 1 : 0,
|
||||
(state_err == ESP_OK && mm_state.connected) ? 1 : 0,
|
||||
(state_err == ESP_OK && mm_state.starting) ? 1 : 0,
|
||||
(state_err == ESP_OK && mm_state.stopping) ? 1 : 0,
|
||||
(state_err == ESP_OK && mm_state.has_active_task) ? 1 : 0,
|
||||
(state_err == ESP_OK && mm_state.server_listening) ? 1 : 0,
|
||||
(state_err == ESP_OK && mm_state.client_speech_active) ? 1 : 0,
|
||||
(state_err == ESP_OK && mm_state.remote_responding) ? 1 : 0,
|
||||
control_plane_multimodal_is_uplink_running() ? 1 : 0);
|
||||
}
|
||||
|
||||
static void control_plane_notify_wifi_state_task(void)
|
||||
@@ -278,6 +295,7 @@ void control_plane_set_ptt_active(bool active)
|
||||
ptt_state_t new_state = PTT_STATE_IDLE;
|
||||
uint32_t generation = 0;
|
||||
const char *transition_reason = active ? "press" : "release";
|
||||
bool uplink_running = !active && control_plane_multimodal_is_uplink_running();
|
||||
portENTER_CRITICAL(&s_runtime_lock);
|
||||
old_state = s_ptt_runtime.state;
|
||||
s_ptt_active = active;
|
||||
@@ -288,6 +306,8 @@ void control_plane_set_ptt_active(bool active)
|
||||
s_ptt_runtime.press_tick = xTaskGetTickCount();
|
||||
s_ptt_runtime.foreground_connect = true;
|
||||
s_ptt_runtime.foreground_attempts = 0;
|
||||
s_ptt_runtime.start_action_sent = false;
|
||||
s_ptt_runtime.speech_action_sent = false;
|
||||
if (s_ptt_runtime.state == PTT_STATE_TEARDOWN_LOCAL ||
|
||||
s_ptt_runtime.state == PTT_STATE_TEARDOWN_REMOTE) {
|
||||
s_ptt_runtime.pending_press_after_teardown = true;
|
||||
@@ -302,7 +322,10 @@ void control_plane_set_ptt_active(bool active)
|
||||
s_ptt_runtime.foreground_attempts = 0;
|
||||
s_ptt_runtime.pending_press_after_teardown = false;
|
||||
s_ptt_runtime.generation++;
|
||||
if (s_ptt_runtime.state == PTT_STATE_WAIT_WIFI ||
|
||||
if (uplink_running || s_ptt_runtime.state == PTT_STATE_STREAMING) {
|
||||
control_plane_ptt_transition_locked(PTT_STATE_TEARDOWN_LOCAL, "release_streaming");
|
||||
transition_reason = "release_streaming";
|
||||
} else if (s_ptt_runtime.state == PTT_STATE_WAIT_WIFI ||
|
||||
s_ptt_runtime.state == PTT_STATE_WAIT_WS ||
|
||||
s_ptt_runtime.state == PTT_STATE_WAIT_START_SENT ||
|
||||
s_ptt_runtime.state == PTT_STATE_WAIT_LISTENING ||
|
||||
@@ -310,9 +333,6 @@ void control_plane_set_ptt_active(bool active)
|
||||
s_ptt_runtime.state == PTT_STATE_DENIED) {
|
||||
control_plane_ptt_transition_locked(PTT_STATE_IDLE, "release_before_streaming");
|
||||
transition_reason = "release_before_streaming";
|
||||
} else if (s_ptt_runtime.state == PTT_STATE_STREAMING) {
|
||||
control_plane_ptt_transition_locked(PTT_STATE_TEARDOWN_LOCAL, "release_streaming");
|
||||
transition_reason = "release_streaming";
|
||||
}
|
||||
notify_worker = true;
|
||||
}
|
||||
@@ -631,90 +651,6 @@ static void control_plane_task_start_gate(void *arg)
|
||||
control_plane_delete_task(NULL, task_with_caps);
|
||||
}
|
||||
|
||||
#if CONFIG_TQ_DIRECTION_CONSOLE_ENABLE
|
||||
static void control_plane_print_help(void)
|
||||
{
|
||||
printf("\n========================================\n");
|
||||
printf(" Direction Control Console\n");
|
||||
printf("========================================\n");
|
||||
printf("Available directions:\n");
|
||||
size_t count = domain_get_direction_count();
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
const char *name_cn = NULL;
|
||||
const char *name_en = NULL;
|
||||
float angle = 0.0f;
|
||||
if (!domain_get_direction_view(i, &name_cn, &name_en, &angle)) {
|
||||
continue;
|
||||
}
|
||||
if (i == 0) {
|
||||
printf(" %s/%s - %.1f deg [default]\n", name_cn, name_en, angle);
|
||||
} else {
|
||||
printf(" %s/%s - %.1f deg\n", name_cn, name_en, angle);
|
||||
}
|
||||
}
|
||||
printf("----------------------------------------\n");
|
||||
printf("Commands:\n");
|
||||
printf(" help - show help\n");
|
||||
printf(" pos - show current position\n");
|
||||
printf(" cal - run calibration (if configured)\n");
|
||||
printf("========================================\n\n");
|
||||
}
|
||||
|
||||
static void control_plane_print_current_position(void)
|
||||
{
|
||||
float angle = domain_get_current_angle();
|
||||
size_t index = 0;
|
||||
|
||||
if (domain_get_current_direction_index(&index)) {
|
||||
const char *name_cn = NULL;
|
||||
const char *name_en = NULL;
|
||||
float direction_angle = 0.0f;
|
||||
if (domain_get_direction_view(index, &name_cn, &name_en, &direction_angle)) {
|
||||
printf("Current position: %s/%s (%.1f deg)\n", name_cn, name_en, angle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Current position: %.1f deg (not a named direction)\n", angle);
|
||||
}
|
||||
|
||||
static void control_plane_process_direction_command(char *input)
|
||||
{
|
||||
char *cmd = input;
|
||||
while (*cmd == ' ') {
|
||||
cmd++;
|
||||
}
|
||||
if (*cmd != '\0') {
|
||||
char *end = cmd + strlen(cmd) - 1;
|
||||
while (end > cmd && *end == ' ') {
|
||||
*end-- = '\0';
|
||||
}
|
||||
}
|
||||
if (*cmd == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
control_plane_direction_command_t parsed = CONTROL_PLANE_DIRECTION_CMD_UNKNOWN;
|
||||
esp_err_t err = control_plane_execute_direction_command(cmd, &parsed, NULL);
|
||||
if (parsed == CONTROL_PLANE_DIRECTION_CMD_HELP) {
|
||||
control_plane_print_help();
|
||||
} else if (parsed == CONTROL_PLANE_DIRECTION_CMD_POSITION) {
|
||||
control_plane_print_current_position();
|
||||
} else if (parsed == CONTROL_PLANE_DIRECTION_CMD_CALIBRATE) {
|
||||
if (err != ESP_OK) {
|
||||
printf("Calibration failed, err=0x%x\n", err);
|
||||
}
|
||||
} else if (parsed == CONTROL_PLANE_DIRECTION_CMD_DIRECTION) {
|
||||
if (err != ESP_OK) {
|
||||
printf("Rotation failed, err=0x%x\n", err);
|
||||
}
|
||||
} else {
|
||||
printf("Unknown command or direction: '%s'\n", cmd);
|
||||
printf("Type 'help' to list available directions\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void control_plane_on_model_command(control_plane_model_command_type_t command_type,
|
||||
const char *value,
|
||||
void *user_data)
|
||||
@@ -962,74 +898,6 @@ static void control_plane_task_monitor_task(void *arg)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_TQ_DIRECTION_CONSOLE_ENABLE
|
||||
static void control_plane_direction_console_task(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
ESP_LOGI(TAG, "Direction console task started");
|
||||
control_plane_print_help();
|
||||
|
||||
char input_buf[CONTROL_PLANE_INPUT_BUF_SIZE] = {0};
|
||||
size_t input_len = 0;
|
||||
bool prompt_printed = false;
|
||||
bool ignore_lf_after_cr = false;
|
||||
|
||||
while (!control_plane_runtime_stop_requested()) {
|
||||
if (!prompt_printed) {
|
||||
printf("Enter direction > ");
|
||||
fflush(stdout);
|
||||
prompt_printed = true;
|
||||
}
|
||||
|
||||
int ch = fgetc(stdin);
|
||||
if (ch == EOF) {
|
||||
clearerr(stdin);
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '\n' && ignore_lf_after_cr) {
|
||||
ignore_lf_after_cr = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '\r' || ch == '\n') {
|
||||
ignore_lf_after_cr = (ch == '\r');
|
||||
input_buf[input_len] = '\0';
|
||||
printf("\n");
|
||||
fflush(stdout);
|
||||
prompt_printed = false;
|
||||
control_plane_process_direction_command(input_buf);
|
||||
input_len = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
ignore_lf_after_cr = false;
|
||||
|
||||
if (ch == '\b' || ch == 0x7F) {
|
||||
if (input_len > 0) {
|
||||
input_len--;
|
||||
printf("\b \b");
|
||||
fflush(stdout);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch >= 0x20 && ch <= 0x7E) {
|
||||
if (input_len < CONTROL_PLANE_INPUT_BUF_SIZE - 1) {
|
||||
input_buf[input_len++] = (char)ch;
|
||||
putchar(ch);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
control_plane_set_task_handle(&s_direction_console_task, NULL);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_TQ_SCREEN_ENABLE
|
||||
static void control_plane_lcd_render_loop_task(void *arg)
|
||||
{
|
||||
@@ -1130,6 +998,16 @@ static esp_err_t control_plane_ensure_user_config_server(void)
|
||||
static bool control_plane_try_start_multimodal_from_state_machine(TickType_t *last_attempt_tick,
|
||||
bool foreground)
|
||||
{
|
||||
control_plane_multimodal_runtime_state_t state = {0};
|
||||
if (control_plane_multimodal_get_runtime_state(&state) == ESP_OK) {
|
||||
if (state.connected) {
|
||||
return true;
|
||||
}
|
||||
if (state.starting || state.stopping) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
TickType_t now = xTaskGetTickCount();
|
||||
TickType_t retry_ticks = pdMS_TO_TICKS(CONTROL_PLANE_WS_START_RETRY_DELAY_MS);
|
||||
if (retry_ticks == 0) {
|
||||
@@ -1150,11 +1028,21 @@ static bool control_plane_try_start_multimodal_from_state_machine(TickType_t *la
|
||||
ESP_LOGI(TAG, "Multimodal client started");
|
||||
return true;
|
||||
}
|
||||
if (err == ESP_ERR_INVALID_STATE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGW(TAG, "Multimodal client start failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool control_plane_multimodal_start_busy(void)
|
||||
{
|
||||
control_plane_multimodal_runtime_state_t state = {0};
|
||||
return control_plane_multimodal_get_runtime_state(&state) == ESP_OK &&
|
||||
(state.starting || state.stopping);
|
||||
}
|
||||
|
||||
static void control_plane_ptt_set_state(ptt_state_t next_state, const char *reason)
|
||||
{
|
||||
ptt_state_t old_state = PTT_STATE_IDLE;
|
||||
@@ -1214,10 +1102,11 @@ static void control_plane_handle_ptt_teardown(void)
|
||||
control_plane_ptt_transition_locked(PTT_STATE_IDLE, "teardown_complete");
|
||||
if (pending_press && active) {
|
||||
s_ws_start_requested = true;
|
||||
s_ptt_runtime.generation++;
|
||||
s_ptt_runtime.foreground_connect = true;
|
||||
s_ptt_runtime.foreground_attempts = 0;
|
||||
s_ptt_runtime.press_tick = xTaskGetTickCount();
|
||||
s_ptt_runtime.start_action_sent = false;
|
||||
s_ptt_runtime.speech_action_sent = false;
|
||||
control_plane_ptt_transition_locked(PTT_STATE_WAIT_WIFI, "pending_press");
|
||||
}
|
||||
generation = s_ptt_runtime.generation;
|
||||
@@ -1270,6 +1159,10 @@ static void control_plane_drive_ptt_state_machine(TickType_t *last_ws_start_atte
|
||||
}
|
||||
|
||||
if (!control_plane_multimodal_is_connected()) {
|
||||
if (ptt.state == PTT_STATE_STREAMING || control_plane_multimodal_is_uplink_running()) {
|
||||
control_plane_ptt_set_state(PTT_STATE_TEARDOWN_LOCAL, "ws_unavailable_abort_uplink");
|
||||
return;
|
||||
}
|
||||
control_plane_ptt_set_state(PTT_STATE_WAIT_WS, "ws_unavailable");
|
||||
bool try_foreground = ptt.foreground_connect &&
|
||||
ptt.foreground_attempts <
|
||||
@@ -1278,10 +1171,14 @@ static void control_plane_drive_ptt_state_machine(TickType_t *last_ws_start_atte
|
||||
bool started = control_plane_try_start_multimodal_from_state_machine(
|
||||
last_ws_start_attempt_tick,
|
||||
true);
|
||||
portENTER_CRITICAL(&s_runtime_lock);
|
||||
s_ptt_runtime.foreground_attempts++;
|
||||
portEXIT_CRITICAL(&s_runtime_lock);
|
||||
bool busy = control_plane_multimodal_start_busy();
|
||||
if (!busy) {
|
||||
portENTER_CRITICAL(&s_runtime_lock);
|
||||
s_ptt_runtime.foreground_attempts++;
|
||||
portEXIT_CRITICAL(&s_runtime_lock);
|
||||
}
|
||||
if (!started &&
|
||||
!busy &&
|
||||
ptt.foreground_attempts + 1U >=
|
||||
CONTROL_PLANE_PTT_FOREGROUND_RECONNECT_MAX_ATTEMPTS) {
|
||||
control_plane_ptt_set_state(PTT_STATE_DENIED, "ws_foreground_attempts_exhausted");
|
||||
@@ -1333,8 +1230,27 @@ static void control_plane_drive_ptt_state_machine(TickType_t *last_ws_start_atte
|
||||
return;
|
||||
}
|
||||
|
||||
ptt = control_plane_ptt_snapshot();
|
||||
bool should_run_press_step = true;
|
||||
if (ptt.state == PTT_STATE_WAIT_START_SENT && ptt.start_action_sent) {
|
||||
should_run_press_step = false;
|
||||
}
|
||||
if (ptt.state == PTT_STATE_WAIT_SPEECH_STARTED && ptt.speech_action_sent) {
|
||||
should_run_press_step = false;
|
||||
}
|
||||
if (!should_run_press_step) {
|
||||
return;
|
||||
}
|
||||
|
||||
esp_err_t press_err = control_plane_multimodal_handle_ptt_press_step();
|
||||
if (press_err == ESP_OK) {
|
||||
portENTER_CRITICAL(&s_runtime_lock);
|
||||
if (s_ptt_runtime.state == PTT_STATE_WAIT_START_SENT) {
|
||||
s_ptt_runtime.start_action_sent = true;
|
||||
} else if (s_ptt_runtime.state == PTT_STATE_WAIT_SPEECH_STARTED) {
|
||||
s_ptt_runtime.speech_action_sent = true;
|
||||
}
|
||||
portEXIT_CRITICAL(&s_runtime_lock);
|
||||
control_plane_multimodal_reset_uplink_failure_window();
|
||||
} else if (press_err != ESP_ERR_INVALID_STATE &&
|
||||
press_err != ESP_ERR_NOT_SUPPORTED) {
|
||||
@@ -1502,7 +1418,9 @@ static void control_plane_wifi_state_task(void *arg)
|
||||
|
||||
if (connected) {
|
||||
bool ws_start_due = control_plane_consume_ws_start_requested();
|
||||
if (ws_start_due && !control_plane_multimodal_is_connected()) {
|
||||
if (ws_start_due &&
|
||||
!control_plane_get_ptt_active() &&
|
||||
!control_plane_multimodal_is_connected()) {
|
||||
control_plane_try_start_multimodal_from_state_machine(&last_ws_start_attempt_tick, false);
|
||||
now_tick = xTaskGetTickCount();
|
||||
}
|
||||
@@ -1844,23 +1762,6 @@ esp_err_t control_plane_start_runtime_workers(void)
|
||||
|
||||
(void)control_plane_schedule_websocket_wait_task();
|
||||
|
||||
#if CONFIG_TQ_DIRECTION_CONSOLE_ENABLE
|
||||
if (control_plane_get_task_handle(&s_direction_console_task) == NULL) {
|
||||
esp_err_t task_err = control_plane_create_task(control_plane_direction_console_task,
|
||||
"cp_direction",
|
||||
CONTROL_PLANE_DIRECTION_TASK_STACK_SIZE,
|
||||
5,
|
||||
CONTROL_PLANE_LOCAL_TASK_CORE,
|
||||
&s_direction_console_task);
|
||||
if (task_err != ESP_OK) {
|
||||
control_plane_stop_runtime_workers();
|
||||
return task_err;
|
||||
}
|
||||
}
|
||||
#else
|
||||
ESP_LOGI(TAG, "Direction console disabled by configuration");
|
||||
#endif
|
||||
|
||||
#if CONFIG_TQ_SCREEN_ENABLE
|
||||
esp_err_t lcd_worker_err = control_plane_start_lcd_render_worker();
|
||||
if (lcd_worker_err != ESP_OK) {
|
||||
@@ -1988,11 +1889,6 @@ void control_plane_stop_runtime_workers(void)
|
||||
ESP_LOGW(TAG, "LCD render loop stop ended with %s", esp_err_to_name(lcd_stop_err));
|
||||
}
|
||||
|
||||
#if CONFIG_TQ_DIRECTION_CONSOLE_ENABLE
|
||||
if (!control_plane_wait_task_exit(&s_direction_console_task, CONTROL_PLANE_TASK_STOP_WAIT_MS)) {
|
||||
control_plane_force_delete_task(&s_direction_console_task, "cp_direction");
|
||||
}
|
||||
#endif
|
||||
if (!control_plane_wait_task_exit(&s_lcd_render_loop_task, CONTROL_PLANE_TASK_STOP_WAIT_MS)) {
|
||||
control_plane_force_delete_task(&s_lcd_render_loop_task, "cp_lcd_render");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user