From b993264c24d8705cfc688520f78a244f05cb6d5a Mon Sep 17 00:00:00 2001 From: xue <2144439972@qq.com> Date: Fri, 20 Mar 2026 11:06:05 +0800 Subject: [PATCH] =?utf8?q?=E4=BC=98=E5=8C=96=E8=80=81=E5=8C=96=E6=B5=8B?= =?utf8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../thunderbird/include/ThunderBirdModel.h | 10 +- .../thunderbird/src/ThunderBirdModel.cpp | 133 +++++++++++++++++- .../leiniao/ln_api_cpp/ln_audio_simple.cpp | 12 +- .../leiniao/ln_api_cpp/ln_audio_simple.h | 6 + 4 files changed, 155 insertions(+), 6 deletions(-) diff --git a/code/sdk/application/wearable/ln/nativeapp/nativeui/thunderbird/include/ThunderBirdModel.h b/code/sdk/application/wearable/ln/nativeapp/nativeui/thunderbird/include/ThunderBirdModel.h index 85791cbc5..380f7a67d 100644 --- a/code/sdk/application/wearable/ln/nativeapp/nativeui/thunderbird/include/ThunderBirdModel.h +++ b/code/sdk/application/wearable/ln/nativeapp/nativeui/thunderbird/include/ThunderBirdModel.h @@ -50,6 +50,9 @@ namespace OHOS // 常亮屏刷新定时器(用于压测模式,每50分钟刷新一次,避免1小时超时) void StartAlwaysOnRefreshTimer(); void StopAlwaysOnRefreshTimer(); + /** 压测音频循环定时器(每1.5秒播放一次 FactoryMode.mp3) */ + void StartStressAudioLoopTimer(); + void StopStressAudioLoopTimer(); /** 息屏时间档位:0=30秒(默认) 1=5分钟 2=10分钟 3=30分钟 4=常亮(不息屏);KV 仅存 screen_timeout_sec */ static constexpr int SCREEN_KEEPON_LEVEL_30S = 0; @@ -481,7 +484,8 @@ namespace OHOS void* m_storyModeTimeoutTimer; // 故事模式超时定时器(1分钟无文本切换则退出) void* m_storyAudioPlaybackTimeoutTimer; // 故事模式音频播放超时定时器(5秒内无音频播放则退出) void* m_factoryResetTimer; // 恢复出厂设置定时器 - + void* m_stressAudioLoopTimer; // 压测音频循环定时器(每1.5秒播放一次) + // 对话模式相关标志 bool m_dialogueModeFinished = false; // 对话模式是否已finished(收到finished=true) @@ -496,6 +500,10 @@ namespace OHOS bool m_videoThreadRecoveryInProgress = false; // 视频线程恢复进行中标志 int m_videoThreadRecoveryAttempts = 0; // 视频线程恢复尝试次数(最多3次) + // AT^LN_STRESS_TEST_DIALOGUE 压测模式相关 + bool m_stressTestDialogueActive = false; // 是否处于压测对话模式 + time_t m_stressTestStartTime = 0; // 压测开始时间戳 + }; } diff --git a/code/sdk/application/wearable/ln/nativeapp/nativeui/thunderbird/src/ThunderBirdModel.cpp b/code/sdk/application/wearable/ln/nativeapp/nativeui/thunderbird/src/ThunderBirdModel.cpp index 0b8ec3cde..04a4bfb8d 100644 --- a/code/sdk/application/wearable/ln/nativeapp/nativeui/thunderbird/src/ThunderBirdModel.cpp +++ b/code/sdk/application/wearable/ln/nativeapp/nativeui/thunderbird/src/ThunderBirdModel.cpp @@ -27,6 +27,7 @@ #include #include #include "custom_utils.h" +#include "custom_utilscpp.h" #include "bluetooth/Ln_Bluetooth_apiC.h" #include #include @@ -48,6 +49,7 @@ #include "ln_api.h" #include #include +#include #include "vibrator_service.h" #include "ai_media.h" #include "ConfigModelC.h" @@ -112,6 +114,55 @@ extern "C" { static bool g_isRecording = false;//是否正在录音 + /**************************产测相关************************************/ + /* AT^LN_STRESS_TEST_DIALOGUE 压测线程:产测期间定期内存拷贝,拉高 CPU 使用率(仅拷贝,不写文件) */ + static osMessageQueueId_t g_stress_save_queue = nullptr; + static volatile bool g_stress_save_created = false; + static volatile bool g_stress_test_active_for_save = false; /* 产测进行中,线程定期做拷贝 */ + static const char *g_stress_audio_file = "/user/res/music/FactoryMode.mp3"; + #define STRESS_COPY_BUF_SIZE 4096 + #define STRESS_COPY_LOOPS 64 /* 每轮拷贝 64 次,共 256KB,拉高 CPU */ + static uint8_t g_stress_copy_src[STRESS_COPY_BUF_SIZE]; + static uint8_t g_stress_copy_dst[STRESS_COPY_BUF_SIZE]; + + /* 压测音频循环定时器回调:每1.5秒执行一次,投递到图形线程播放 */ + static void StressAudioLoopTimerCallback(uint8_t data) + { + GraphicService::GetInstance()->PostGraphicEvent([]() { + ln_audio_simple_stop(); + LOS_Msleep(100); + (void)ln_audio_simple_play_loop(g_stress_audio_file, false); + }); + } + + static void stress_test_do_copy_only(void) + { + /* 仅内存拷贝:拉高 CPU 使用率,不写文件 */ + for (int i = 0; i < STRESS_COPY_LOOPS; i++) { + memcpy(g_stress_copy_dst, g_stress_copy_src, STRESS_COPY_BUF_SIZE); + } + } + + static void stress_test_save_task(void *arg) + { + uint32_t msg = 0; + uint32_t copy_count = 0; + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "stress_test_save: task started (periodic memcpy only, no file)"); + for (;;) { + uint32_t timeout_ms = g_stress_test_active_for_save ? 1000 : osWaitForever; /* 产测中每 1 秒唤醒 */ + osStatus_t st = osMessageQueueGet(g_stress_save_queue, &msg, nullptr, timeout_ms); + if (st == osOK) continue; /* 收到消息则忽略(仅用于唤醒),不写文件 */ + if (st != osOK && g_stress_test_active_for_save) { + /* 超时且产测进行中:重复内存拷贝,拉高 CPU */ + stress_test_do_copy_only(); + copy_count++; + if (copy_count % 10 == 1) { /* 每 10 秒打印一次,便于确认在运行 */ + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "stress_test_save: memcpy running, count=%u", copy_count); + } + } + } + } + /**************************产测相关************************************/ ThunderBirdModel::ThunderBirdModel() : m_nextReminderId(1), m_otaStatus(0), m_timeDisplayTimer(nullptr), m_statusTimer(nullptr), m_currentStatus(DeviceStatus::IDLE), @@ -130,6 +181,7 @@ extern "C" { m_lastCat1RestartTime(0), m_storyModeTimeoutTimer(nullptr), m_storyAudioPlaybackTimeoutTimer(nullptr), + m_stressAudioLoopTimer(nullptr), m_networkConnectingReason(NetworkConnectingReason::NONE), m_networkConnectingTimeoutCount(0) { @@ -358,6 +410,8 @@ extern "C" { // ---------------------- 仅当当前在提醒界面时启动 3 秒退出定时器(供 LN_HTTP_SERVICE_CMD_AUDIO_STOP 使用,避免哄睡结束误触) ---------------------- void ThunderBirdModel::StartCountUpTimerForDialogueExitIfInSpeaking(void) { + /* 压测模式下不启动 3 秒退出定时器,避免 FactoryMode.mp3 播放完成后误退出 */ + if (m_stressTestDialogueActive) return; ThunderBirdMainView *view = ThunderBirdMainView::GetInstance(); if (view == nullptr) return; int16_t currentView = view->GetCurrentView(); @@ -1787,6 +1841,11 @@ extern "C" { WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "TimeDisplayCallback: %s", currentTime.c_str()); } + /* 产测模式下每秒重新断言常亮屏,防止被其他逻辑覆盖 */ + if (m_stressTestDialogueActive) { + ScreenAlwaysOnCtr(OPEN_SCREEN_ALWAYS_ON); + } + // 处理常亮屏刷新定时器(每5分钟刷新一次,避免1小时超时) if (m_alwaysOnRefreshActive) { m_alwaysOnRefreshTime++; @@ -3441,11 +3500,37 @@ extern "C" { { WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::HandleJumpDialogueSpeaking"); + m_stressTestDialogueActive = true; + m_stressTestStartTime = time(nullptr); + g_stress_test_active_for_save = true; + ln_audio_simple_set_suppress_stop_on_complete(true); /* 产测模式下播放完成不触发 CMD_AUDIO_STOP */ + + /* 首次进入时创建压测保存线程:独立线程做内存拷贝+写文件 */ + if (!g_stress_save_created) { + g_stress_save_queue = osMessageQueueNew(4, sizeof(uint32_t), nullptr); + if (g_stress_save_queue) { + osThreadAttr_t attr = {0}; + attr.name = "stress_save"; + attr.stack_size = 1024; + attr.priority = osPriorityBelowNormal; + if (osThreadNew(stress_test_save_task, nullptr, &attr) != nullptr) { + g_stress_save_created = true; + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "HandleJumpDialogueSpeaking: stress_test_save thread created"); + } else { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "HandleJumpDialogueSpeaking: stress_test_save thread create FAILED"); + osMessageQueueDelete(g_stress_save_queue); + g_stress_save_queue = nullptr; + } + } + } + // 1. 停止其他音频服务,避免冲突 ln_audio_simple_stop(); // AI_ForceInterrupt(); do_custom_cmd_service_check_msg(CUSTOM_CMD_MSG_AUDIO_DEINIT, NULL, 0); + LOS_Msleep(250); + // 2. 跳转到对话说话界面 if (ThunderBirdMainView::GetInstance()) { int currentView = ThunderBirdMainView::GetInstance()->GetCurrentView(); @@ -3453,19 +3538,20 @@ extern "C" { } // 3. 设置常亮屏(在ChangeView之后设置,确保不会被视图切换逻辑覆盖) + uapi_setScreenDuration(30 * 60 * 1000); /* 产测模式:30分钟息屏,避免被 KV 的短超时覆盖 */ ScreenAlwaysOnCtr(OPEN_SCREEN_ALWAYS_ON); StartAlwaysOnRefreshTimer(); const char *factory_mode_file = "/user/res/music/FactoryMode.mp3"; - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::HandleJumpDialogueSpeaking: Playing file: %s (will loop)", factory_mode_file); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::HandleJumpDialogueSpeaking: Playing file: %s (timer replay every 1.5s)", factory_mode_file); - // 使用 ln_audio_simple_play_loop 直接循环播放,无需线程 - int ret = ln_audio_simple_play_loop(factory_mode_file, true); + int ret = ln_audio_simple_play_loop(factory_mode_file, false); if (ret != 0) { WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::HandleJumpDialogueSpeaking: Failed to play file: %s, ret=%d", factory_mode_file, ret); } else { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::HandleJumpDialogueSpeaking: Successfully started loop playback"); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::HandleJumpDialogueSpeaking: started, timer will replay every 1.5s"); + StartStressAudioLoopTimer(); } } @@ -3488,6 +3574,36 @@ extern "C" { m_alwaysOnRefreshTime = 0; } + void ThunderBirdModel::StartStressAudioLoopTimer() + { + if (m_stressAudioLoopTimer != nullptr) { + StopStressAudioLoopTimer(); + } + m_stressAudioLoopTimer = osTimerNew((osTimerFunc_t)StressAudioLoopTimerCallback, osTimerPeriodic, nullptr, nullptr); + if (m_stressAudioLoopTimer == nullptr) { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "StartStressAudioLoopTimer: Failed to create timer"); + return; + } + osStatus_t ret = osTimerStart(m_stressAudioLoopTimer, 2000); // 1500ms = 1.5秒 + if (ret != osOK) { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "StartStressAudioLoopTimer: Failed to start, ret=%d", ret); + osTimerDelete(m_stressAudioLoopTimer); + m_stressAudioLoopTimer = nullptr; + } else { + WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "StartStressAudioLoopTimer: started, 1.5s interval"); + } + } + + void ThunderBirdModel::StopStressAudioLoopTimer() + { + if (m_stressAudioLoopTimer != nullptr) { + osTimerStop(m_stressAudioLoopTimer); + osTimerDelete(m_stressAudioLoopTimer); + m_stressAudioLoopTimer = nullptr; + WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "StopStressAudioLoopTimer: stopped"); + } + } + //测试BGM开始函数 void ThunderBirdModel::StartDialogueBgm() { @@ -4202,6 +4318,15 @@ extern "C" { // 退出对话模式时恢复表情 void ThunderBirdModel::OnExitDialogueMode() { + // 停止BGM/压测音频,停止常亮屏定时器 + StopStressAudioLoopTimer(); + StopDialogueBgm(); + StopAlwaysOnRefreshTimer(); + if (m_stressTestDialogueActive) { + ln_audio_simple_set_suppress_stop_on_complete(false); /* 恢复默认:播放完成发送 CMD_AUDIO_STOP */ + g_stress_test_active_for_save = false; /* 停止定期拷贝 */ + m_stressTestDialogueActive = false; + } //切换跑马灯速度 ThunderBirdDialogueView::GetInstance()->SwitchRollSpeed(75); WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ThunderBirdModel::OnExitDialogueMode: 开始退出对话模式"); diff --git a/code/sdk/zhihui/leiniao/ln_api_cpp/ln_audio_simple.cpp b/code/sdk/zhihui/leiniao/ln_api_cpp/ln_audio_simple.cpp index 124bbb2e1..48955db91 100644 --- a/code/sdk/zhihui/leiniao/ln_api_cpp/ln_audio_simple.cpp +++ b/code/sdk/zhihui/leiniao/ln_api_cpp/ln_audio_simple.cpp @@ -45,6 +45,7 @@ typedef struct } ln_audio_simple_impl_t; static ln_audio_simple_impl_t g_simple = {0}; +static bool g_suppress_stop_on_complete = false; /* ============================== 播放器回调类 ============================== */ @@ -57,11 +58,15 @@ public: { if (impl_) { + impl_->is_playing = false; + if (g_suppress_stop_on_complete) { + /* 压测模式下不发送 CMD_AUDIO_STOP,由 StressAudioLoopTimer 负责重播 */ + return; + } printf("[LN_AUDIO_SIMPLE] playback completed, sending cleanup message\n"); // 通过消息队列发送清理命令,避免在回调中直接调用导致卡住 // 发送消息到 ln_http_server_send_task 线程 do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_AUDIO_STOP, NULL, 0); - impl_->is_playing = false; } } @@ -399,6 +404,11 @@ bool ln_audio_simple_is_playing(void) return g_simple.is_playing; } +void ln_audio_simple_set_suppress_stop_on_complete(bool suppress) +{ + g_suppress_stop_on_complete = suppress; +} + } // extern "C" diff --git a/code/sdk/zhihui/leiniao/ln_api_cpp/ln_audio_simple.h b/code/sdk/zhihui/leiniao/ln_api_cpp/ln_audio_simple.h index 61f578304..fd0556024 100644 --- a/code/sdk/zhihui/leiniao/ln_api_cpp/ln_audio_simple.h +++ b/code/sdk/zhihui/leiniao/ln_api_cpp/ln_audio_simple.h @@ -51,6 +51,12 @@ int ln_audio_simple_stop(void); */ bool ln_audio_simple_is_playing(void); +/* + * 压测模式下:播放完成时不发送 LN_HTTP_SERVICE_CMD_AUDIO_STOP + * 参数:suppress - true 表示抑制,false 表示恢复默认行为 + */ +void ln_audio_simple_set_suppress_stop_on_complete(bool suppress); + #ifdef __cplusplus } #endif -- 2.17.1