Files
story/firmware/echoear-platform-agent/tools/test_voice_diagnostic_host.c.in
T
Codex Local bf1233d5df
ci / verify (push) Canceled after 0s
feat: expand companion platform, device controls, and memoir workflows
Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
2026-10-08 10:06:44 +08:00

167 lines
11 KiB
Plaintext

/* Synthetic-only SDK/RTOS boundaries. No device, network or captured speech. */
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdatomic.h>
#include <string.h>
#include <stdarg.h>
static void host_log(const char *fmt, ...) {(void)fmt;}
#define LOGI(...) host_log(__VA_ARGS__)
#define ESP_LOGI(tag,...) host_log(__VA_ARGS__)
#define pdFALSE 0
#define eInvalid 0
#define eDeleted 1
#define ESP_PLATFORM 1
typedef unsigned UBaseType_t;
typedef struct {unsigned number, priority, base; int core;} host_task_t;
typedef host_task_t *TaskHandle_t;
typedef struct {unsigned xTaskNumber,uxBasePriority;int eCurrentState;} TaskStatus_t;
static host_task_t rtc_task={42,4,4,1}, conv_task={43,5,5,1};
static bool rtc_present=true, conv_present;
static bool engine_ready=true, publish_fail;
static const char *current_name="VolcRTCMain";
static unsigned priority_writes, start_events, tts_sends, audio_sends, creates;
static bool authorized=true, rtc_mode=true, create_fail;
static int tts_result;
static TaskHandle_t xTaskGetHandle(const char *name) {
if(!strcmp(name,"VolcRTCMain")) return rtc_present?&rtc_task:NULL;
if(!strcmp(name,"conv_ai")) return conv_present?&conv_task:NULL;
assert(0);return NULL;
}
static void vTaskGetInfo(TaskHandle_t task,TaskStatus_t *s,int a,int b) {assert(!task);(void)a;(void)b;s->xTaskNumber=rtc_task.number;s->uxBasePriority=rtc_task.base;s->eCurrentState=eInvalid;}
static unsigned uxTaskPriorityGet(TaskHandle_t task) {assert(!task);return rtc_task.priority;}
static void vTaskPrioritySet(TaskHandle_t task,unsigned priority) {
assert(!task);
bool inherited=rtc_task.priority>rtc_task.base;
rtc_task.base=priority;
if(!inherited || rtc_task.priority<priority) rtc_task.priority=priority;
priority_writes++;
}
static int xTaskGetCoreID(TaskHandle_t task) {assert(!task);return rtc_task.core;}
static TaskHandle_t xTaskGetCurrentTaskHandle(void) {return &rtc_task;}
static const char *pcTaskGetName(TaskHandle_t task) {assert(!task);return current_name;}
static uint64_t esp_timer_get_time(void) {return 987654321;}
enum {VOLC_SERVICE_AI_CONVERSATION=1,VOLC_SERVICE_AI_CONVERSATION_START,VOLC_SERVICE_AI_CONVERSATION_INTERRUPT,
VOLC_SERVICE_AI_CONVERSATION_QUIT,VOLC_SERVICE_AI_CONVERSATION_OVER,VOLC_SERVICE_AI_CONVERSATION_TOGGLE};
static int aios_event_pub(int event,void *a,void *b) {assert(event==VOLC_SERVICE_AI_CONVERSATION);assert(!a&&!b);start_events++;return publish_fail?-1:0;}
static bool call_client_is_engine_ready(void) {return engine_ready;}
bool conv_ai_service_diagnostic_active(void);
typedef void *volc_hal_capture_t;
typedef struct {int data_type;} volc_hal_frame_info_t;
typedef struct {bool commit;int data_type;} volc_audio_frame_info_t;
enum {VOLC_AUDIO_DATA_TYPE_PCM=11,VOLC_AGENT_TYPE_TTS=12};
static struct {void *engine;int wait_time;} conv_service={(void*)1,0};
static atomic_bool is_stop,stop_requested;
static bool platform_conversation_audio_authorized(void) {return authorized;}
bool platform_runtime_rtc(void) {return rtc_mode;}
static uint64_t host_now_ms=1234;
static uint64_t volc_osal_get_time_ms(void) {return host_now_ms;}
typedef struct {int type;} esp_gmf_afe_evt_t;
enum {ESP_GMF_AFE_EVT_VAD_START=1,ESP_GMF_AFE_EVT_VAD_END};
static int get_rssi(void) {return -10;}
static int volc_send_audio_data(void *engine,const void *data,int len,volc_audio_frame_info_t *info) {
assert(engine==conv_service.engine&&data&&len>0&&!info->commit);audio_sends++;return 0;
}
static int volc_send_text_to_agent(void *engine,const char *text,int type,int interrupt) {
assert(engine==conv_service.engine&&type==VOLC_AGENT_TYPE_TTS&&interrupt==1);
assert(strstr(text,"这是连续语音播放测试。")&&strstr(text,"平稳地读完。"));tts_sends++;return tts_result;
}
typedef int aios_ret_t;
typedef struct {int id;} aios_event_t;
typedef struct {void *conv_thread_id;} volc_conv_service_manager_t;
typedef struct {void *display_handle;} volc_hal_context_t;
typedef void *volc_hal_display_t;
static volc_hal_context_t hal_context={NULL};
static volc_hal_context_t *volc_get_global_hal_context(void) {return &hal_context;}
enum {AIOS_Ret_NotHandled,AIOS_Ret_Handled,VOLC_DISPLAY_OBJ_STATUS,VOLC_DISPLAY_TEXT};
typedef struct {char name[32];int stack_size,priority,stack_in_ext;} volc_osal_thread_param_t;
static void volc_hal_display_set_content(void *h,int obj,int kind,const char *text) {(void)h;(void)obj;(void)kind;(void)text;}
static void diagnostic_prepare(void);
static void conv_ai_service_prepare_task(void) {diagnostic_prepare();}
static void conv_ai_service_task(void *arg) {(void)arg;}
void conv_ai_service_diagnostic_reset(void);
static void volc_osal_thread_destroy(void *handle) {(void)handle;}
static void vTaskDeleteWithCaps(TaskHandle_t handle) {assert(handle==&conv_task);conv_present=false;}
static void conv_ai_service_task_stop(void) {conv_ai_service_diagnostic_reset();}
static int volc_osal_thread_create(void **handle,volc_osal_thread_param_t *p,void (*f)(void*),void *arg) {
(void)p;(void)f;(void)arg;creates++;if(create_fail)return -1;*handle=&conv_task;conv_present=true;return 0;
}
/* @PRODUCTION@ */
static void capture(void) {
int16_t pcm[8]={1,2,3,4,5,6,7,8};volc_hal_frame_info_t info={VOLC_AUDIO_DATA_TYPE_PCM};
__audio_capture_cb(NULL,pcm,sizeof(pcm),&info);
}
int main(void) {
// Execute the real VAD + PCM callback: silence does not postpone idle,
// continuous detected speech does, and its end starts a fresh minute.
is_stop=false;stop_requested=false;call_idle_activity();
host_now_ms+=59000;capture();assert(!call_idle_expired());
host_now_ms+=1000;capture();assert(call_idle_expired());
esp_gmf_afe_evt_t vad={ESP_GMF_AFE_EVT_VAD_START};
call_idle_capture_event(&vad,NULL);
for(int tick=0;tick<180;tick++){host_now_ms+=1000;capture();assert(!call_idle_expired());}
vad.type=ESP_GMF_AFE_EVT_VAD_END;call_idle_capture_event(&vad,NULL);
host_now_ms+=60000;capture();assert(call_idle_expired());
audio_sends=0;
rtc_task.priority=rtc_task.base=3;
current_name="worker";__rtc_diagnostic_priority_apply();assert(priority_writes==0&&rtc_task.base==3);
diagnostic_rtc_priority(false);diagnostic_rtc_priority(true);assert(priority_writes==0);
current_name="VolcRTCMain";__rtc_diagnostic_priority_apply();
assert(rtc_task.priority==6&&rtc_task.base==6&&priority_writes==1&&!conv_ai_service_diagnostic_active());
unsigned writes=priority_writes;__rtc_diagnostic_priority_apply();assert(priority_writes==writes);
rtc_task.priority=rtc_task.base=7;__rtc_diagnostic_priority_apply();assert(rtc_task.base==7&&priority_writes==writes);
rtc_task.priority=8;rtc_task.base=3; // inherited effective priority is not the configured base
__rtc_diagnostic_priority_apply();assert(rtc_task.base==6&&rtc_task.priority==8);
rtc_task.priority=rtc_task.base;__rtc_diagnostic_priority_apply();assert(rtc_task.priority==6);
rtc_task.number++;rtc_task.priority=rtc_task.base=3; // a new same-name task must receive the floor too
__rtc_diagnostic_priority_apply();assert(rtc_task.base==6);
rtc_task.priority=rtc_task.base=4;__rtc_diagnostic_priority_apply();assert(rtc_task.base==6);
writes=priority_writes;diagnostic_rtc_priority(false);diagnostic_rtc_priority(true);
conv_ai_service_diagnostic_reset();diagnostic_close();diagnostic_prepare();
__rtc_diagnostic_priority_apply();assert(rtc_task.base==6&&priority_writes==writes);
volc_rtc_diagnostic_snapshot();assert(atomic_load(&diagnostic_rtc_seen));
assert(atomic_load(&diagnostic_rtc_base)==6&&priority_writes==writes);
puts("PASS normal-call self-only floor, higher base, inherited effective priority, new task, no diagnostic override");
capture();assert(audio_sends==1);
conv_ai_service_diagnostic_tts();assert(start_events==1&&diagnostic_uplink_suppressed());
capture();assert(audio_sends==1);
assert(diagnostic_take_pending()==DIAGNOSTIC_TTS&&diagnostic_uplink_suppressed());
diagnostic_send_tts();assert(tts_sends==1&&diagnostic_uplink_suppressed());
conv_present=true;conv_ai_service_diagnostic_tts();assert(start_events==1);
assert(diagnostic_take_pending()==DIAGNOSTIC_TTS);diagnostic_send_tts();assert(tts_sends==2);
assert(diagnostic_take_pending()==DIAGNOSTIC_NONE);capture();assert(audio_sends==1);
conv_ai_service_diagnostic_say();assert(start_events==1&&!diagnostic_uplink_suppressed());
assert(diagnostic_take_pending()==DIAGNOSTIC_SAY);capture();assert(audio_sends==2);
conv_ai_service_diagnostic_tts();diagnostic_rtc_priority(true);conv_ai_service_diagnostic_reset();__rtc_diagnostic_priority_apply();
assert(diagnostic_take_pending()==DIAGNOSTIC_NONE&&!diagnostic_uplink_suppressed()&&rtc_task.priority==6);
capture();assert(audio_sends==3);
puts("PASS fixed TTS bypass, same-room repetition, mic-only gate, say/reset restores uplink");
conv_ai_service_diagnostic_tts();tts_result=-1;diagnostic_send_tts();assert(!diagnostic_uplink_suppressed());
assert(diagnostic_take_pending()==DIAGNOSTIC_NONE);
rtc_mode=false;conv_ai_service_diagnostic_tts();unsigned sends=tts_sends;diagnostic_send_tts();
assert(tts_sends==sends&&!diagnostic_uplink_suppressed());rtc_mode=true;tts_result=0;
conv_present=false;create_fail=true;volc_conv_service_manager_t manager={0};
aios_event_t event={VOLC_SERVICE_AI_CONVERSATION_START};
conv_ai_service_diagnostic_tts();diagnostic_rtc_priority(true);
assert(__state_conversation(&manager,&event)==AIOS_Ret_Handled&&creates==1&&!manager.conv_thread_id);
assert(!diagnostic_uplink_suppressed()&&diagnostic_take_pending()==DIAGNOSTIC_NONE&&rtc_task.priority==6);
authorized=false;capture();assert(audio_sends==3);
puts("PASS send failure, unsupported transport and actual task-create failure restore diagnostics");
conv_present=true;conv_ai_service_diagnostic_tts();diagnostic_close();
conv_ai_service_diagnostic_tts();assert(!diagnostic_uplink_suppressed()&&!conv_ai_service_diagnostic_active());
assert(diagnostic_take_pending()==DIAGNOSTIC_NONE);
diagnostic_rtc_priority(true);assert(rtc_task.priority==6);
conv_ai_service_diagnostic_reset();conv_ai_service_diagnostic_tts(); // reset must preserve closing gate
assert(!diagnostic_uplink_suppressed());
conv_present=false;publish_fail=true;conv_ai_service_diagnostic_tts();
assert(!diagnostic_uplink_suppressed()&&!conv_ai_service_diagnostic_active());publish_fail=false;
conv_ai_service_diagnostic_tts();assert(diagnostic_uplink_suppressed());diagnostic_prepare();
assert(diagnostic_take_pending()==DIAGNOSTIC_TTS);diagnostic_close();diagnostic_prepare();
assert(!diagnostic_uplink_suppressed()&&!conv_ai_service_diagnostic_active());
puts("PASS closing request rejection, no priority pollution, event failure and next-call reset");
puts("voice diagnostic host PASS");return 0;
}