#include "TjdUiAppLefunAiModel.h" #include "TjdUiAppLefunAiPresenter.h" #include "TjdUiAppLefunAiView.h" #include "TjdUiImageIds.h" #include "TjdUiScreenManage.h" #include "ble_port_protocol.h" #include "cJSON.h" #include "common/image_cache_manager.h" #include "graphic_service.h" #include "hal_tick.h" #include "power_display_service.h" #include "sql_setting.h" #include "sys_config.h" #include "thread_adapter.h" #include #include #include #include #include #include #include #include #include namespace TJD { #define ENABLE_PRINT_INFO 0 #if ENABLE_PRINT_INFO #define static_print_info(...) sys_ui_log_i(__VA_ARGS__) // 一般信息打印宏控制 #define static_print_warn(...) sys_ui_log_w(__VA_ARGS__) // 警告信息打印一般常开 #define static_print_error(...) sys_ui_log_e(__VA_ARGS__) // 错误信息打印一般常开 #define static_print_debug(...) sys_ui_log_d(__VA_ARGS__) // 调试信息打印一般常开 #else #define static_print_info(...) #define static_print_warn(...) #define static_print_error(...) #define static_print_debug(...) #endif #define LOAD_BUFFER_SIZE 8 * 1024 #define LEFUN_AI_SCREEN_AUTO_OFF_TIME (1000 * 60 * 60) static MutexId g_lefun_ai_mutex = 0; static bool story_question_flag = false; static bool story_answer_flag = false; struct data_flow *data_flow = nullptr; std::string question = ""; std::string answer = ""; extern osThreadId_t resend_audio_data_id; int TjdUiAppLefunAiModel::file_exists() { FILE *file = fopen(LEFUN_AI_STORY_FILE_PATH, "r"); if (file) { fclose(file); return 1; // 文件存在 } return 0; // 文件不存在 } void TjdUiAppLefunAiModel::addRecord(History *history, std::string question, std::string answer) { if (history == nullptr || history->records == nullptr) { return; } if (history->total_records >= MAX_HISTORY) { for (int i = 1; i < MAX_HISTORY; i++) { history->records[i - 1] = history->records[i]; } history->total_records--; } if (history->total_records < MAX_HISTORY) { history->records[history->total_records].question = question; history->records[history->total_records].answer = answer; history->total_records++; } } void TjdUiAppLefunAiModel::saveToJson(const History *history, const char *filename) { // 检查输入参数 if (!history || history->total_records < 0) { printf("Error: Invalid history object\n"); return; } // 创建 JSON 对象 cJSON *json = cJSON_CreateObject(); if (!json) { printf("Error creating JSON object\n"); return; } // 创建历史记录数组 cJSON *historyArray = cJSON_CreateArray(); if (!historyArray) { printf("Error creating JSON array\n"); cJSON_Delete(json); return; } // 将 total_records 添加到 JSON 对象中 cJSON_AddNumberToObject(json, "total_records", history->total_records); // 遍历历史记录并添加到 JSON 数组中 for (int i = 0; i < history->total_records; i++) { const std::string &question = history->records[i].question; const std::string &answer = history->records[i].answer; cJSON *item = cJSON_CreateObject(); if (!item) { printf("Error creating JSON item for record \n"); continue; } if (item == nullptr) { return; } cJSON_AddStringToObject(item, "question", question.c_str()); cJSON_AddStringToObject(item, "answer", answer.c_str()); // 将记录添加到历史记录数组 cJSON_AddItemToArray(historyArray, item); } // 将历史记录数组添加到 JSON 对象 cJSON_AddItemToObject(json, "history", historyArray); // 打开文件以写入 JSON 数据 FILE *file = fopen(filename, "w"); if (!file) { printf("Error opening file: "); cJSON_Delete(json); return; } // 将 JSON 对象转换为字符串并写入文件 char *jsonString = cJSON_Print(json); if (jsonString) { fprintf(file, "%s\n", jsonString); free(jsonString); // 释放 JSON 字符串 } // 关闭文件和删除 JSON 对象 fclose(file); cJSON_Delete(json); } void TjdUiAppLefunAiModel::displayHistory(const History *history) { printf("displayHistory %d\n", history->total_records); for (int i = 0; i < history->total_records; i++) { // 打印每个记录的 question 和 answer printf("Record %d:\n", i + 1); printf("Question: %s\n", history->records[i].question.c_str()); printf("Answer: %s\n", history->records[i].answer.c_str()); printf("------------------\n"); } } void TjdUiAppLefunAiModel::loadFromJson(History *history) { char *filename = LEFUN_AI_STORY_FILE_PATH; FILE *file = fopen(filename, "r"); if (!file) { // 文件不存在,创建初始 JSON createInitialJson(filename); return; // 返回以避免进一步处理 } char *buffer = (char *)malloc(LOAD_BUFFER_SIZE); if (!buffer) { printf("Error: Failed to allocate memory for buffer.\n"); fclose(file); return; } size_t bytesRead = fread(buffer, sizeof(char), LOAD_BUFFER_SIZE - 1, file); buffer[bytesRead] = '\0'; fclose(file); cJSON *json = cJSON_Parse(buffer); if (json) { cJSON *totalRecordsItem = cJSON_GetObjectItem(json, "total_records"); history->total_records = totalRecordsItem ? totalRecordsItem->valueint : 0; cJSON *historyArray = cJSON_GetObjectItem(json, "history"); cJSON *record = NULL; int index = 0; // 当前存储记录的索引 cJSON_ArrayForEach(record, historyArray) { if (index < 10) { // 获取问题和答案 cJSON *questionItem = cJSON_GetObjectItem(record, "question"); cJSON *answerItem = cJSON_GetObjectItem(record, "answer"); if (questionItem && questionItem->valuestring && answerItem && answerItem->valuestring) { history->records[index].question = questionItem->valuestring; history->records[index].answer = answerItem->valuestring; index++; } else { fprintf(stderr, "Warning: Missing question or answer in record %d\n", index); } } history->total_records = index; } cJSON_Delete(json); } else { static_print_warn("Error parsing JSON json null\n"); } free(buffer); } uint8_t TjdUiAppLefunAiModel::get_total_records_from_json() { FILE *file = fopen(LEFUN_AI_STORY_FILE_PATH, "r"); if (file == NULL) { printf("Could not open file %s\n", LEFUN_AI_STORY_FILE_PATH); return 0; // 返回 0 表示文件未未初始化,直接返回 } // 获取文件大小 fseek(file, 0, SEEK_END); long length = ftell(file); fseek(file, 0, SEEK_SET); // 读取文件内容 char *data = (char *)malloc(length + 1); fread(data, 1, length, file); data[length] = '\0'; // 确保字符串以 null 结尾 fclose(file); cJSON *json = cJSON_Parse(data); free(data); if (json == NULL) { printf("Error parsing JSON\n"); return -1; // 返回 -1 表示解析错误 } cJSON *total_records_item = cJSON_GetObjectItem(json, "total_records"); uint8_t total_records = total_records_item ? total_records_item->valueint : -1; // 默认返回 -1 cJSON_Delete(json); return total_records; } void TjdUiAppLefunAiModel::createInitialJson(const char *filename) { cJSON *json = cJSON_CreateObject(); cJSON_AddNumberToObject(json, "total_records", 0); cJSON *historyArray = cJSON_CreateArray(); cJSON_AddItemToObject(json, "history", historyArray); FILE *file = fopen(filename, "w"); if (file) { char *jsonString = cJSON_Print(json); fprintf(file, "%s\n", jsonString); free(jsonString); fclose(file); } cJSON_Delete(json); } void TjdUiAppLefunAiModel::AudioInit(void) { if (audioInitStatus_) { static_print_info("LefunAiModel audioInitStatus_ is true"); return; } Audio::AudioManager &amIntance = Audio::AudioManager::GetInstance(); audioInitStatus_ = amIntance.Initialize(); playerCtr_ = std::make_shared(); // playerCallback_ = std::make_shared(); static_print_info("LefunAiModel AudioInit end!"); } int TjdUiAppLefunAiModel::RecordStart() { if (recordIsRuning_) { static_print_warn("LefunAiModel RecordStart failed, recordIsRuning_ is true"); return OHOS_FAILURE; } Audio::AudioManager &amIntance = Audio::AudioManager::GetInstance(); if (!isActivateAudioInterrupt_) { static_print_info("ActivateAudioInterrupt"); // 申请焦点 sessionId_ = amIntance.MakeSessionId(); if (sessionId_ == AUDIO_SESSION_ID_NONE) { static_print_warn("session invalid"); return OHOS_FAILURE; } std::shared_ptr recordInterruptListener_ = std::make_shared(); interrupt_ = {AUDIO_STREAM_VOICE_RECORD, sessionId_, recordInterruptListener_}; // 激活音频中断 5s超时 if (amIntance.ActivateAudioInterrupt(interrupt_) == INTERRUPT_FAILED) { static_print_warn("ActivateAudioInterrupt faild"); return OHOS_FAILURE; } isActivateAudioInterrupt_ = true; } lefunAiCapturer_ = std::make_shared(); url_ = std::string("/user/") + "lefunAi_test.opus"; pfd_.open(url_, std::ios::out | std::ios::binary); if (!pfd_.is_open()) { static_print_warn("open file failed"); lefunAiCapturer_.reset(); (void)amIntance.DeactivateAudioInterrupt(interrupt_); return -1; } // 设置音频参数 OHOS::Audio::AudioCapturerInfo info = {}; info.inputSource = AUDIO_MIC; info.audioFormat = AudioCodecFormat::OPUS; info.sampleRate = 16000; info.channelCount = 1; info.streamType = AUDIO_STREAM_VOICE_RECORD; info.bitWidth = AudioBitWidth::BIT_WIDTH_16; info.sessionID = sessionId_; if (lefunAiCapturer_->SetCapturerInfo(info) != 0) { static_print_warn("{%s,%d} set capturer info failed", __func__, __LINE__); (void)amIntance.DeactivateAudioInterrupt(interrupt_); return -1; } // 获取每帧大小 size_t frameCount = lefunAiCapturer_->GetFrameCount(); if (!frameCount) { static_print_warn("Can't GetFrameCount"); (void)lefunAiCapturer_->Release(); (void)amIntance.DeactivateAudioInterrupt(interrupt_); return -1; } framesize_ = frameCount * info.channelCount * info.bitWidth / 8; static_print_info("framesize_:%d", framesize_); lefunAiRecordBuffer_ = new uint8_t[framesize_]; if (!lefunAiCapturer_->Start()) { static_print_warn("{%s,%d} start failed", __func__, __LINE__); lefunAiCapturer_.reset(); (void)amIntance.DeactivateAudioInterrupt(interrupt_); // delete lefunAiCapturer_; return -1; } recordIsRuning_ = false; MediaThreadattr attr = {"lefun_ai_record", 0x2000, THREAD_SCHED_INVALID, 0, true}; threadIdHandle_ = MediaThreadCreate( [](void *arg) -> void * { TjdUiAppLefunAiModel::GetInstance().AudioCaptureProcess(); return nullptr; }, NULL, &attr); if (threadIdHandle_ == nullptr) { static_print_warn("thread create failed"); return -1; } return 0; } void TjdUiAppLefunAiModel::RecordStop() { static_print_info("RecordStop"); if (recordIsRuning_) { { std::lock_guard lock(pauseMutex_); needStop_.store(true); needPause_.store(false); // 确保线程不会因为暂停而阻塞 } pauseCondVar_.notify_all(); // 唤醒等待的线程 static_print_debug("LefunAi RecordStop wait thread join"); osThreadJoin(threadIdHandle_); static_print_debug("LefunAi RecordStop thread join end"); } LefunAiRequestEndAudioTransmit(); } void TjdUiAppLefunAiModel::AudioCaptureProcess(void) { static_print_info("TjdUiAppLefunAiModel AudioCaptureProcess start"); recordIsRuning_ = true; while (!needStop_) { // 检查是否需要暂停和停止 std::unique_lock lock(pauseMutex_); uint64_t pauseStartTime = OHOS::HALTick::GetInstance().GetTime(); pauseCondVar_.wait(lock, [this] { return !needPause_ || needStop_; }); if (needStop_) { static_print_debug("needStop_ is true"); break; } memset(lefunAiRecordBuffer_, 0, framesize_); ret = lefunAiCapturer_->Read(lefunAiRecordBuffer_, framesize_, false); printf("lefunAiCapturer_->Read ret:%d\n", ret); if (ret == -1) { static_print_warn("audioCap Read failed:0x%x", ret); continue; } if (ret == OHOS::Media::ERR_RETRY_READ) { usleep(10000); continue; } pfd_.write((const char *)lefunAiRecordBuffer_, ret); if (!pfd_) { static_print_warn("errno:%d, errmsg:%s", errno, strerror(errno)); break; } TransmitAudioData(lefunAiRecordBuffer_ + 8, ret - 8); } static_print_info("AudioCaptureProcess end"); pfd_.close(); RecordDestroy(); } static void resend_audio_task(void *argument) { osDelay(500); // 打开文件 std::ifstream inputFile("/user/lefunAi_test.opus", std::ios::in | std::ios::binary); if (!inputFile.is_open()) { printf("Failed to open file: %s", "/user/lefunAi_test.opus"); return; } // 定义缓冲区,每次读取168字节 const size_t bufferSize = 168; uint8_t buffer[168]; // 循环读取文件并发送数据 while (inputFile.read(reinterpret_cast(buffer), bufferSize)) { size_t bytesRead = inputFile.gcount(); // 获取实际读取的字节数 if (bytesRead > 8) { // 只传输从第9个字节开始的数据 printf("Read %zu bytes from file\n", bytesRead); TjdUiAppLefunAiModel::GetInstance().TransmitAudioData(buffer + 8, bytesRead - 8); // 发送数据(跳过前8个字节) osDelay(20); // 等待20ms,防止数据过快导致传输失败 } } printf("AudioCaptureFromFile end"); inputFile.close(); TjdUiAppLefunAiModel::GetInstance().LefunAiRequestEndAudioTransmit(); osThreadTerminate(resend_audio_data_id); } void TjdUiAppLefunAiModel::ResendAudioData() { static_print_info("ResendAudioData start"); osThreadAttr_t task_attr = {"resend_audio_data_task", 0, NULL, 0, NULL, 0x1000, (osPriority_t)17, 0, 0}; resend_audio_data_id = osThreadNew(resend_audio_task, NULL, &task_attr); if (resend_audio_data_id == NULL) { static_print_error("resend_audio_data_task failed"); return; } } void TjdUiAppLefunAiModel::RecordDestroy(void) { static_print_debug("RecordDestroy"); Audio::AudioManager &amIntance = Audio::AudioManager::GetInstance(); MediaMutexLock(g_lefun_ai_mutex); if (!lefunAiCapturer_->Stop()) { static_print_warn("Stop failed"); } if (!lefunAiCapturer_->Release()) { static_print_warn("Release failed"); } delete lefunAiRecordBuffer_; lefunAiRecordBuffer_ = nullptr; if (amIntance.DeactivateAudioInterrupt(interrupt_) != 0) { static_print_warn("deactivate audio interrupt failed"); } needStop_ = false; needPause_ = false; lefunAiCapturer_.reset(); lefunAiCapturer_ = nullptr; isActivateAudioInterrupt_ = false; recordIsRuning_ = false; MediaMutexUnLock(g_lefun_ai_mutex); } void TjdUiAppLefunAiModel::RequestIntoLefunAi() { tjd_lefun_ai_request_into_lefun_ai(); tjd_ble_upload_ai_func_attribute(); } void TjdUiAppLefunAiModel::LefunAiRequestIntoAudioTransmit() { tjd_lefun_ai_request_into_audio_transmit(); } void TjdUiAppLefunAiModel::LefunAiRequestStartAudioTransmit() { tjd_lefun_ai_request_start_audio_transmit(); } void TjdUiAppLefunAiModel::LefunAiRequestExitLefunAi() { tjd_lefun_ai_request_exit_lefun_ai(); } void TjdUiAppLefunAiModel::LefunAiRequestEndAudioTransmit() { tjd_lefun_ai_request_end_audio_transmit(); } void TjdUiAppLefunAiModel::TransmitAudioData(uint8_t *data, uint16_t len) { tjd_ble_transmit_audio_data(data, len); } bool TjdUiAppLefunAiModel::GetAiAudioTransFlag() { return tjd_ble_get_ai_audio_trans_flag(); } void lefun_ai_data_callback(struct data_flow *data) { printf("LefunAiModel data_callback"); if (data == nullptr) return; if (data->data == nullptr) { return; } data_flow = data; if (data_flow->data_flow_type == 0) { story_question_flag = true; // TjdUiAppLefunAiModel::question.clear(); question = std::string((char *)data_flow->data, data_flow->len); // TjdUiAppLefunAiModel::question(std::string((char *)data_flow->data)); printf("LefunAiModel data_callback question:%s\n", question.c_str()); } else if (data_flow->data_flow_type == 1) { story_answer_flag = true; // TjdUiAppLefunAiModel::answer.clear(); // TjdUiAppLefunAiModel::answer.append(std::string((char *)data_flow->data)); answer += std::string((char *)data_flow->data); } GraphicService::GetInstance()->PostGraphicEvent([&]() { if (story_question_flag && story_answer_flag) { TjdUiAppLefunAiView::GetInstance()->ShowView(LEFUN_AI_VIEW_ANSWER_VIEW); } }); } static OHOS::GraphicTimer *waiterTimer_ = nullptr; // 运动进行中计时器 std::atomic waiterTimerStart(false); static void WaiterTimerCallback(void *arg) { printf("LefunAiModel WaiterTimerCallback\n"); if (waiterTimerStart.load()) { printf("LefunAiModel WaiterTimerCallback start\n"); waiterTimerStart.store(false); waiterTimer_->Stop(); waiterTimer_->Start(); return; } waiterTimerStart.store(false); if (data_flow->data != NULL) { free(data_flow->data); data_flow->data = NULL; } data_flow->len = 0; data_flow->data_flow_id = 0; data_flow->data_flow_type = 0; // 保存历史记录到 JSON 文件 GraphicService::GetInstance()->PostGraphicEvent([&]() { History lefunAiHistory; TjdUiAppLefunAiModel::GetInstance().loadFromJson(&lefunAiHistory); // printf("line = %d=======lefunAiHistory=%d==========\n", __LINE__, lefunAiHistory.total_records); story_answer_flag = false; TjdUiAppLefunAiModel::GetInstance().addRecord(&lefunAiHistory, question, answer); // printf("line = %d=========lefunAiHistory=%d==========\n", __LINE__, lefunAiHistory.total_records); TjdUiAppLefunAiModel::GetInstance().saveToJson(&lefunAiHistory, (const char *)"/system/lefun_ai_record.json"); question.clear(); answer.clear(); }); } void TjdUiAppLefunAiModel::DeleDataFlow(void) { if (data_flow->data != NULL) { free(data_flow->data); data_flow->data = NULL; } if (data_flow) { data_flow->len = 0; data_flow->data_flow_id = 0; data_flow->data_flow_type = 0; } // 保存历史记录到 JSON 文件 History lefunAiHistory; // printf("line = %d==================\n", __LINE__); TjdUiAppLefunAiModel::GetInstance().loadFromJson(&lefunAiHistory); // printf("line = %d=======lefunAiHistory=%d==========\n", __LINE__, lefunAiHistory.total_records); story_answer_flag = false; TjdUiAppLefunAiModel::GetInstance().addRecord(&lefunAiHistory, question, answer); // printf("line = %d=========lefunAiHistory=%d==========\n", __LINE__, lefunAiHistory.total_records); TjdUiAppLefunAiModel::GetInstance().saveToJson(&lefunAiHistory, (const char *)"/system/lefun_ai_record.json"); question.clear(); answer.clear(); } void lefun_ai_data_end_callback(struct data_flow *data) { printf("LefunAiModel lefun_ai_data_end_callback"); if (data == nullptr) return; if (data->data == nullptr) { return; } GraphicService::GetInstance()->PostGraphicEvent([&]() { data_flow = data; if (story_question_flag && story_answer_flag) { TjdUiAppLefunAiView::GetInstance()->ShowView(LEFUN_AI_VIEW_ANSWER_VIEW); auto AnswerView = LefunAiAnswerView::GetInstance(); int bottom = AnswerView->answerView_.GetRect().GetBottom(); AnswerView->answerView_.ScrollBy(0, -bottom); printf("LefunAiModel ScrollBy %d\n", -bottom); } // if (waiterTimer_ == nullptr) { // waiterTimer_ = new OHOS::GraphicTimer(5000, WaiterTimerCallback, (void *)data, false); // waiterTimer_->Start(); // } }); } void TjdUiAppLefunAiModel::RegisterLefunAiDataCallback(lefun_ai_data_callback_t callback) { register_lefun_ai_data_callback(callback); } void TjdUiAppLefunAiModel::UnregisterLefunAiDataCallback() { unregister_lefun_ai_data_callback(); } void TjdUiAppLefunAiModel::RegisterLefunAiDataEndCallback(lefun_ai_data_callback_t callback) { register_lefun_ai_data_end_callback(callback); } void TjdUiAppLefunAiModel::UnregisterLefunAiDataEndCallback() { unregister_lefun_ai_data_end_callback(); } void TjdUiAppLefunAiModel::CloseAutoScreenOff(void) { const power_display_svr_api_t *handle = power_display_svr_get_api(); handle->set_screen_set_keepon_timeout(LEFUN_AI_SCREEN_AUTO_OFF_TIME); } void TjdUiAppLefunAiModel::OpenAutoScreenOff(void) { const power_display_svr_api_t *handle = power_display_svr_get_api(); handle->set_screen_set_keepon_timeout(0); // handle->set_screen_set_keepon_timeout(sql_setting_get_close_screen_time() * 1000); } } // namespace TJD