feat: add device diagnostics for missing voice replies
Track microphone levels, short reads and PCM buffer drops. Report audio queue admission separately from TLS writes, expose server replies and ACKs before filtering, and capture per-round counters before timeouts. Add diagnostic regression coverage and support POSIX hosts in WSS tests.
This commit is contained in:
@@ -62,6 +62,12 @@ enum { WAIT_NONE, WAIT_NO_SPEECH, WAIT_REPLY, WAIT_SEND_FAILURE };
|
||||
static int conversation_wait = WAIT_NONE;
|
||||
static int timeout_action_busy = 0;
|
||||
static int round_response_received = 0;
|
||||
/* Queue admission is distinct from a completed TLS write and from server ACK. */
|
||||
static struct {
|
||||
unsigned int tx_packets, tx_bytes, tx_failed, rx_text, rx_bin;
|
||||
unsigned int start_ack, end_ack;
|
||||
uint64_t started_ms, last_rx_ms;
|
||||
} voice_diag;
|
||||
static unsigned int active_session_round = 0;
|
||||
static int active_session_valid = 0;
|
||||
static uint64_t startup_start_ms = 0;
|
||||
@@ -87,6 +93,10 @@ static void conversation_unlock(void)
|
||||
static void conversation_begin_round(void)
|
||||
{
|
||||
conversation_round++;
|
||||
memset(&voice_diag, 0, sizeof(voice_diag));
|
||||
voice_diag.started_ms = hal_os_up_time_ms();
|
||||
printf("[VOICE-DIAG] begin round=%u uptime_ms=%u\n",
|
||||
conversation_round, (unsigned int)voice_diag.started_ms);
|
||||
conversation_wait = WAIT_NONE;
|
||||
round_response_received = 0;
|
||||
active_session_valid = 0;
|
||||
@@ -217,6 +227,20 @@ unsigned char get_xiaoice_connected_status(void)
|
||||
return xiaoice_wss_connect_status;
|
||||
}
|
||||
|
||||
/* Called with conversation_mutex held. Ages use monotonic milliseconds. */
|
||||
static void voice_diag_snapshot(const char *stage)
|
||||
{
|
||||
uint64_t now = hal_os_up_time_ms();
|
||||
printf("[VOICE-DIAG] %s round=%u session=%s age_ms=%u queue_pkts=%u queue_bytes=%u queue_fail=%u rx_text=%u rx_bin=%u start_ack=%u end_ack=%u last_rx_age_ms=%d wait=%d wait_ms=%u response=%d\n",
|
||||
stage, conversation_round, active_session_valid ? sessionId : "none",
|
||||
(unsigned int)(now - voice_diag.started_ms), voice_diag.tx_packets,
|
||||
voice_diag.tx_bytes, voice_diag.tx_failed, voice_diag.rx_text,
|
||||
voice_diag.rx_bin, voice_diag.start_ack, voice_diag.end_ack,
|
||||
voice_diag.rx_text || voice_diag.rx_bin ? (int)(now - voice_diag.last_rx_ms) : -1,
|
||||
conversation_wait, conversation_wait == WAIT_REPLY ?
|
||||
(unsigned int)(now - reply_wait_start_ms) : 0, round_response_received);
|
||||
}
|
||||
|
||||
static void onopen(rws_socket socket)
|
||||
{
|
||||
if (socket != app->socket || !app->init) return;
|
||||
@@ -245,6 +269,9 @@ static void onclose(rws_socket socket)
|
||||
}
|
||||
|
||||
if (socket != app->socket) return;
|
||||
conversation_lock();
|
||||
voice_diag_snapshot("disconnected");
|
||||
conversation_unlock();
|
||||
xiaoice_wss_connect_status = 0;
|
||||
app->socket = NULL;
|
||||
/* The worker owns and releases this socket after the callback. */
|
||||
@@ -649,6 +676,16 @@ static void conversation_log_error_message(const char *text, unsigned int len)
|
||||
static void onmessage_text(rws_socket socket, const char *text, const unsigned int len)
|
||||
{
|
||||
conversation_lock();
|
||||
voice_diag.rx_text++;
|
||||
voice_diag.last_rx_ms = hal_os_up_time_ms();
|
||||
if (strstr(text, "START_ACK")) voice_diag.start_ack++;
|
||||
if (strstr(text, "END_ACK")) voice_diag.end_ack++;
|
||||
/* Bounded preview includes unrecognized replies and ACKs before filtering. */
|
||||
printf("[VOICE-RX] round=%u session=%s uptime_ms=%u len=%u accepted=%d preview=%.*s%s\n",
|
||||
conversation_round, active_session_valid ? sessionId : "none",
|
||||
(unsigned int)voice_diag.last_rx_ms, len,
|
||||
conversation_accept_voice_message(text), (int)(len > 192 ? 192 : len), text,
|
||||
len > 192 ? " [truncated]" : "");
|
||||
if (strstr(text, "tts_error") != NULL || strstr(text, "tts_err") != NULL ||
|
||||
strstr(text, "PROMPT_SOUND_URL") != NULL) {
|
||||
conversation_log_error_message(text, len);
|
||||
@@ -663,8 +700,16 @@ static void onmessage_text(rws_socket socket, const char *text, const unsigned i
|
||||
}
|
||||
|
||||
static void onmessage_bin(rws_socket socket, const void * data, const unsigned int len)
|
||||
{
|
||||
LOG_D("message(bin): %d ", len);
|
||||
{
|
||||
(void)socket;
|
||||
(void)data;
|
||||
conversation_lock();
|
||||
voice_diag.rx_bin++;
|
||||
voice_diag.last_rx_ms = hal_os_up_time_ms();
|
||||
if (voice_diag.rx_bin == 1 || voice_diag.rx_bin % 50 == 0)
|
||||
printf("[VOICE-RX] binary round=%u len=%u count=%u ignored=1\n",
|
||||
conversation_round, len, voice_diag.rx_bin);
|
||||
conversation_unlock();
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -811,7 +856,11 @@ int talkingq_ws_send_text(char *senddata)
|
||||
|
||||
LOG_D("talkingq_wss_send_text string = %s, len = %d", senddata, strlen(senddata));
|
||||
|
||||
rws_socket_send_text(app->socket, senddata);
|
||||
{
|
||||
int queued = rws_socket_send_text(app->socket, senddata);
|
||||
printf("[VOICE-TX] auth/text queue uptime_ms=%u bytes=%u accepted=%d\n",
|
||||
(unsigned int)hal_os_up_time_ms(), (unsigned int)strlen(senddata), queued != 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1041,6 +1090,7 @@ void sendStartPacket(void) {
|
||||
startPacket.dataSize = 0;
|
||||
|
||||
active_session_valid = sendRecordingAudioPacket(&startPacket) == 0;
|
||||
voice_diag_snapshot("start-queued");
|
||||
conversation_unlock();
|
||||
// rt_kprintf("sendStartPacket, seq=%u\r\n", startPacket.sequenceNumber);
|
||||
}
|
||||
@@ -1059,6 +1109,7 @@ void sendEndPacket(void) {
|
||||
reply_wait_start_ms = hal_os_up_time_ms();
|
||||
conversation_wait = end_ret == 0 ? WAIT_REPLY : WAIT_SEND_FAILURE;
|
||||
}
|
||||
voice_diag_snapshot("end-queued");
|
||||
}
|
||||
conversation_unlock();
|
||||
// rt_kprintf("sendEndPacket, seq=%u\r\n", endPacket.sequenceNumber);
|
||||
@@ -1074,12 +1125,19 @@ void sendAudioPacket(const uint8_t* data, size_t dataSize) {
|
||||
audioPacket.data = malloc(audioPacket.dataSize);
|
||||
if(audioPacket.data==NULL){
|
||||
RLOGE("audioPacket.data NULL");
|
||||
conversation_lock();
|
||||
voice_diag.tx_failed++;
|
||||
printf("[VOICE-TX] alloc_failed round=%u seq=%u bytes=%u\n",
|
||||
conversation_round, audioPacket.sequenceNumber, (unsigned int)dataSize);
|
||||
conversation_unlock();
|
||||
return;
|
||||
}
|
||||
memcpy(audioPacket.data, data, dataSize);
|
||||
}
|
||||
|
||||
conversation_lock();
|
||||
sendRecordingAudioPacket(&audioPacket);
|
||||
conversation_unlock();
|
||||
// rt_kprintf("sendAudioPacket, seq=%u\r\n", audioPacket.sequenceNumber);
|
||||
if(audioPacket.data){
|
||||
free(audioPacket.data);
|
||||
@@ -1092,7 +1150,9 @@ void sendInterruptPacket(void) {
|
||||
interruptPacket.packetType = 3;
|
||||
interruptPacket.dataSize = 0;
|
||||
|
||||
conversation_lock();
|
||||
sendRecordingAudioPacket(&interruptPacket);
|
||||
conversation_unlock();
|
||||
}
|
||||
|
||||
void sendAuthentication()
|
||||
@@ -1122,6 +1182,9 @@ int sendRecordingAudioPacket(AudioPacket *packet) {
|
||||
sendBuffer = malloc(DEVICE_ID_SIZE + SESSION_ID_SIZE + sizeof(uint32_t) * 2 + sizeof(uint32_t) + packet->dataSize);
|
||||
if(sendBuffer==NULL){
|
||||
RLOGE("sendBuffer NULL");
|
||||
voice_diag.tx_failed++;
|
||||
printf("[VOICE-TX] alloc_failed seq=%u type=%u bytes=%u\n",
|
||||
packet->sequenceNumber, packet->packetType, (unsigned int)packet->dataSize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1151,7 +1214,21 @@ int sendRecordingAudioPacket(AudioPacket *packet) {
|
||||
// memcpy(recorder->save_buf, (void *)sendBuffer, recorder->save_len);
|
||||
//record_msg_send(recorder, RECORD_MSG_DATA, recorder->save_len);
|
||||
|
||||
uint64_t queue_started = hal_os_up_time_ms();
|
||||
int send_ret = talkingq_ws_send_bin((uint8_t*)sendBuffer, totalSize);
|
||||
if (send_ret == 0) {
|
||||
voice_diag.tx_packets++;
|
||||
voice_diag.tx_bytes += (unsigned int)packet->dataSize;
|
||||
} else {
|
||||
voice_diag.tx_failed++;
|
||||
}
|
||||
if (packet->packetType == 1 || packet->packetType == 2 || send_ret != 0 ||
|
||||
voice_diag.tx_packets % 50 == 0) {
|
||||
printf("[VOICE-TX] queue round=%u session=%s seq=%u type=%u bytes=%u ret=%d elapsed_ms=%u\n",
|
||||
conversation_round, sessionId, packet->sequenceNumber, packet->packetType,
|
||||
(unsigned int)packet->dataSize, send_ret,
|
||||
(unsigned int)(hal_os_up_time_ms() - queue_started));
|
||||
}
|
||||
free(sendBuffer);
|
||||
return send_ret;
|
||||
// rt_kprintf("sendRecordingAudioPacket - Device ID: %s, Session ID: %s\n", deviceId, sessionId);
|
||||
@@ -1493,6 +1570,7 @@ static void conversation_check_timeout(void)
|
||||
(conversation_wait == WAIT_NO_SPEECH ||
|
||||
conversation_wait == WAIT_SEND_FAILURE ||
|
||||
hal_os_up_time_ms() - reply_wait_start_ms > 5000)) {
|
||||
voice_diag_snapshot("timeout-before-disconnect");
|
||||
expired = conversation_wait;
|
||||
expired_round = wait_round;
|
||||
conversation_wait = WAIT_NONE;
|
||||
|
||||
Reference in New Issue
Block a user