#include "TjdUiAppSportModel.h" #include "TjdUiAppSportPage.h" #include "TjdUiAppSportView.h" #include "rtc_api.h" #include "service_charger.h" #include "service_hrsensor.h" #include "sql_setting.h" #include "sys_config.h" #include #include #include #include #include #include #include #include #include "rtc_api.h" using namespace OHOS; #define ENABLE_PRINT_INFO 1 #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 namespace TJD { #define SUCCESS 1 #define ERROR 0 #define SPORT_DATA_PATHA_NAME "/system/tjd_sport/sport_data.json" bool TjdUiAppSportModel::GetGpsPositioningStatus(void) { return false; } SportingData_t TjdUiAppSportModel::GetServiceSportData() { SportingData_t sportingData; sportingData = tjd_service_sport_get_data(); return sportingData; } void TjdUiAppSportModel::RemoveFile(const char *filepath) { if (remove(filepath) != 0) { static_print_info("RemoveFile删除文件失败: %s\n", filepath); } else { static_print_info("RemoveFile 删除文件成功: %s\n", filepath); } } const char *TjdUiAppSportModel::RecordSportData() // 命名格式:sport_2024-8-20_15-54-23.json { static_print_info("TjdUiAppSportModel::RecordSportData\n"); struct rtc_time localTime = {0}; rtc_class_ops *rtc = tjd_driver_rtc_get_ops(); rtc->get_rtc_time(&localTime); std::ostringstream formattedFileName; formattedFileName << "sport_record_" << localTime.tm_year << "-" << std::setw(1) << std::setfill('0') << localTime.tm_mon << "-" << std::setw(1) << std::setfill('0') << localTime.tm_mday << "_" << std::setw(2) << std::setfill('0') << localTime.tm_hour << "-" << std::setw(2) << std::setfill('0') << localTime.tm_min << "-" << std::setw(2) << std::setfill('0') << localTime.tm_sec << ".json"; static_print_info("sport data record to fileName: %s\n", formattedFileName.str().c_str()); std::string DirPath(TJD_FS_DIR_SPORT); url_ = DirPath + "/" + formattedFileName.str(); static_print_info("filePath: %s\n", url_.c_str()); StoreSportData(url_.c_str()); return url_.c_str(); } std::string TjdUiAppSportModel::RenameSportDataFile(const std::string &oldFilePath) { struct rtc_time localTime = {0}; rtc_class_ops *rtc = tjd_driver_rtc_get_ops(); rtc->get_rtc_time(&localTime); // 构造新的文件名 std::ostringstream formattedFileName; formattedFileName << "sport_record_" << localTime.tm_year << "-" << std::setw(1) << std::setfill('0') << localTime.tm_mon << "-" << std::setw(1) << std::setfill('0') << localTime.tm_mday << "_" << std::setw(2) << std::setfill('0') << localTime.tm_hour << "-" << std::setw(2) << std::setfill('0') << localTime.tm_min << "-" << std::setw(2) << std::setfill('0') << localTime.tm_sec << ".json"; std::string DirPath(TJD_FS_DIR_SPORT); std::string newFilePath = DirPath + "/" + formattedFileName.str(); // 尝试重命名文件 if (std::rename(oldFilePath.c_str(), newFilePath.c_str()) == 0) { static_print_info("file rename success\n"); } else { static_print_info("file rename fail\n"); } return newFilePath; printf("oldFilePath: %s\n", oldFilePath.c_str()); printf("newFilePath: %s\n", newFilePath.c_str()); } void TjdUiAppSportModel::StoreSportData(const char *url) { auto sportingView = SportingView::GetInstance(); uint64_t timestamp = 0; tjd_driver_rtc_get_ops()->get_timestamp(×tamp); cJSON *jsonData = cJSON_CreateObject(); cJSON_AddNumberToObject(jsonData, "SportIconIndex", TjdUiAppSportView::currentIconIndex_ + 1); cJSON_AddNumberToObject(jsonData, "StartTime", timestamp); cJSON_AddNumberToObject(jsonData, "Time", 5); cJSON_AddNumberToObject(jsonData, "GPS_Status", GPSGetData().valid); cJSON_AddStringToObject(jsonData, "SportTime", sportingView->sportingRtcLabel_.GetText()); cJSON_AddStringToObject(jsonData, "HeartRate", sportingView->heartRateLabel_.GetText()); cJSON_AddStringToObject(jsonData, "Calories", sportingView->caloriesLabel_.GetText()); switch (SportingView::sportType) { case SPORT_TYPE_1: cJSON_AddStringToObject(jsonData, "Distance", sportingView->distanceLabel_.GetText()); cJSON_AddStringToObject(jsonData, "Speed", "00'00''"); cJSON_AddStringToObject(jsonData, "Step", sportingView->stepLabel_.GetText()); cJSON_AddStringToObject(jsonData, "Frequency", "0.0"); break; case SPORT_TYPE_2: cJSON_AddStringToObject(jsonData, "Distance", sportingView->distanceLabel_.GetText()); break; default: break; } // 创建运动轨迹数组 cJSON *trackArray = cJSON_CreateArray(); // 将运动轨迹数组添加到 JSON 对象 cJSON_AddItemToObject(jsonData, "Position", trackArray); FILE *outputFile = fopen(url, "w"); if (outputFile) { char *jsonString = cJSON_Print(jsonData); if (jsonString) { fprintf(outputFile, "%s", jsonString); free(jsonString); } fclose(outputFile); // 关闭文件 } else { static_print_info("无法打开文件进行写入\n"); } cJSON_Delete(jsonData); } void TjdUiAppSportModel::UpdateSportData(const char *url) { // printf("TjdUiAppSportModel::UpdateSportData,url:%s\n", url); auto sportingView = SportingView::GetInstance(); // 打开文件并读取内容 FILE *inputFile = fopen(url, "r"); if (!inputFile) { static_print_info("无法打开文件进行读取\n"); return; } fseek(inputFile, 0, SEEK_END); long fileSize = ftell(inputFile); fseek(inputFile, 0, SEEK_SET); char *fileContent = (char *)malloc(fileSize + 1); fread(fileContent, 1, fileSize, inputFile); fileContent[fileSize] = '\0'; fclose(inputFile); cJSON *jsonData = cJSON_Parse(fileContent); free(fileContent); if (!jsonData) { static_print_info("解析 JSON 失败\n"); return; } // 更新指定键的值 cJSON_ReplaceItemInObject(jsonData, "SportTime", cJSON_CreateString(std::string(sportingView->sportingRtcLabel_.GetText()).c_str())); cJSON_ReplaceItemInObject(jsonData, "HeartRate", cJSON_CreateString(std::string(sportingView->heartRateLabel_.GetText()).c_str())); cJSON_ReplaceItemInObject(jsonData, "Calories", cJSON_CreateString(std::string(sportingView->caloriesLabel_.GetText()).c_str())); Time time = TjdUiAppSportView::GetInstance()->GetCurrentTime(sportingView->sportingRtcLabel_.GetText()); uint32_t totalTime = time.hours * 3600 + time.minutes * 60 + time.seconds; cJSON_ReplaceItemInObject(jsonData, "Time", cJSON_CreateNumber(totalTime)); float distance = std::stof(std::string(SportingView::GetInstance()->distanceLabel_.GetText())); uint32_t step = std::stoi(std::string(SportingView::GetInstance()->stepLabel_.GetText())); // 根据运动类型更新其他键值 switch (SportingView::sportType) { case SPORT_TYPE_1: cJSON_ReplaceItemInObject(jsonData, "Distance", cJSON_CreateString(std::string(sportingView->distanceLabel_.GetText()).c_str())); if (distance) { float pace = totalTime / distance / 60.0; int minutes = static_cast(pace); int seconds = static_cast((pace - minutes) * 60); // 创建带单引号的目标字符串 stringstream finalStr; finalStr << minutes << "'" << setw(2) << setfill('0') << seconds << "\""; cJSON_ReplaceItemInObject(jsonData, "Speed", cJSON_CreateString(finalStr.str().c_str())); } else cJSON_ReplaceItemInObject(jsonData, "Speed", cJSON_CreateString("00'00\"")); cJSON_ReplaceItemInObject(jsonData, "Step", cJSON_CreateString(std::string(sportingView->stepLabel_.GetText()).c_str())); if (distance) { // 计算步频 float frequency = distance * 1000 / step; std::ostringstream oss; oss << std::fixed << std::setprecision(1) << frequency; // 保留2位小数 cJSON_ReplaceItemInObject(jsonData, "Frequency", cJSON_CreateString(oss.str().c_str())); // 需要改 } else cJSON_ReplaceItemInObject(jsonData, "Frequency", cJSON_CreateString("0.0")); break; case SPORT_TYPE_2: cJSON_ReplaceItemInObject( jsonData, "Distance", cJSON_CreateString(std::string(std::string(sportingView->distanceLabel_.GetText()) + std::string(" ") + std::string(sportingView->distanceUnitLabel_.GetText())) .c_str())); break; default: break; } // 将更新后的 JSON 数据写回文件 FILE *outputFile = fopen(url, "w"); if (outputFile) { char *jsonString = cJSON_Print(jsonData); if (jsonString) { fprintf(outputFile, "%s", jsonString); free(jsonString); } fclose(outputFile); // 关闭文件 } else { static_print_info("无法打开文件进行写入\n"); } cJSON_Delete(jsonData); // 释放 JSON 对象 } void TjdUiAppSportModel::AddDataToArry(const char *url, double newLatitude, double newLongitude) { // 打开文件并读取内容 FILE *inputFile = fopen(url, "r"); if (!inputFile) { static_print_info("AddDataToArry Failed\n"); return; } // 获取文件大小 fseek(inputFile, 0, SEEK_END); long fileSize = ftell(inputFile); fseek(inputFile, 0, SEEK_SET); // 读取文件内容到字符串 char *fileContent = (char *)malloc(fileSize + 1); fread(fileContent, 1, fileSize, inputFile); fileContent[fileSize] = '\0'; // 添加字符串结束符 fclose(inputFile); // 关闭文件 // 解析 JSON 数据 cJSON *jsonData = cJSON_Parse(fileContent); free(fileContent); // 释放内存 if (!jsonData) { static_print_info("解析 JSON 失败\n"); return; } // 查找运动轨迹数组 cJSON *trackArray = cJSON_GetObjectItem(jsonData, "Position"); if (!trackArray || !cJSON_IsArray(trackArray)) { static_print_info("运动轨迹数组不存在或不是数组类型\n"); cJSON_Delete(jsonData); // 释放 JSON 对象 return; } // 添加新的轨迹点 cJSON *newTrackPoint = cJSON_CreateObject(); cJSON_AddNumberToObject(newTrackPoint, "latitude", isnan(newLatitude)? 0 : newLatitude); cJSON_AddNumberToObject(newTrackPoint, "longitude", isnan(newLongitude)? 0 : newLongitude); cJSON_AddItemToArray(trackArray, newTrackPoint); // 添加新轨迹点到数组 // 将修改后的 JSON 数据写回文件 FILE *outputFile = fopen(url, "w"); if (outputFile) { char *jsonString = cJSON_Print(jsonData); if (jsonString) { fprintf(outputFile, "%s", jsonString); free(jsonString); } fclose(outputFile); // 关闭文件 } else { static_print_info("无法打开文件进行写入\n"); } cJSON_Delete(jsonData); // 释放 JSON 对象 } std::vector TjdUiAppSportModel::SearchAndRecordFilepath() { std::string path = TJD_FS_DIR_SPORT; std::vector fileList; struct dirent *entry; DIR *dp = opendir(path.c_str()); if (dp == nullptr) { static_print_info("SearchAndRecordFilepath 无法打开目录"); return fileList; } while ((entry = readdir(dp)) != nullptr) { std::string filename = entry->d_name; // 检查文件名是否以 "sport_record_" 开头且以 ".json" 结尾 if (filename.rfind("sport_record_", 0) == 0 && filename.size() > 5 && filename.compare(filename.size() - 5, 5, ".json") == 0) { fileList.push_back(path + "/" + filename); } } closedir(dp); std::sort(fileList.begin(), fileList.end(), [](const std::string &a, const std::string &b) { size_t startA = a.find_last_of('_') + 1; size_t startB = b.find_last_of('_') + 1; std::string timeA = a.substr(startA, 19); std::string timeB = b.substr(startB, 19); return timeA < timeB; }); // 输出文件数量和路径 static_print_info("文件数量: %d\n", fileList.size()); for (const auto &file : fileList) { static_print_info("filePath: %s\n", file.c_str()); } return fileList; } // 创建JSON对象 cJSON *TjdUiAppSportModel::CreateSportsJson(SportInfo *dataArry, int count) { cJSON *root = cJSON_CreateArray(); for (int i = 0; i < count; ++i) { cJSON *sport = cJSON_CreateObject(); cJSON_AddNumberToObject(sport, "sportTypeId", dataArry[i].sportTypeId); cJSON_AddNumberToObject(sport, "typeIndex", dataArry[i].index); cJSON_AddBoolToObject(sport, "state", dataArry[i].state); cJSON_AddItemToArray(root, sport); } return root; } // 保存JSON到文件 void TjdUiAppSportModel::SaveJsonToFile(const char *filename, cJSON *json) { char *jsonString = cJSON_Print(json); if (jsonString) { FILE *file = fopen(filename, "w"); if (file) { fprintf(file, "%s", jsonString); fclose(file); } else { fprintf(stderr, "Failed to open file for writing.\n"); } free(jsonString); } else { fprintf(stderr, "Failed to print JSON.\n"); } } cJSON *TjdUiAppSportModel::ReadAndParseJson(const char *fliePath) { char *json_string = ReadFileToString(fliePath); if (json_string == NULL) { fprintf(stderr, "Failed to read file\n"); // return nullptr; } cJSON *json = ParseJson(json_string); return json; } // 与ReadAndParseJson配套使用,使用完需要释放json内存 char *TjdUiAppSportModel::KeyValueAnalysis(cJSON *json, const char *key) // 键值解析 { cJSON *target_json = cJSON_GetObjectItem(json, key); if (cJSON_IsString(target_json) && target_json->valuestring != NULL) { printf("KeyValueAnalysis = %s: %s\n", key, target_json->valuestring); } return target_json->valuestring; } std::vector TjdUiAppSportModel::TrackAnalysis(cJSON *json) // 与ReadAndParseJson配套使用,减少读取文件系统次数 { std::vector coordinates; cJSON *track = cJSON_GetObjectItem(json, "Position"); if (cJSON_IsArray(track)) { printf("%s========:\n", "Position"); int track_size = cJSON_GetArraySize(track); printf(" 轨迹点数量: %d\n", track_size); coordinates.reserve(track_size); // 提前为 vector 预分配空间,避免多次重新分配 for (int i = 0; i < track_size; i++) { cJSON *coord = cJSON_GetArrayItem(track, i); cJSON *latitude = cJSON_GetObjectItem(coord, "latitude"); cJSON *longitude = cJSON_GetObjectItem(coord, "longitude"); if (cJSON_IsNumber(latitude) && cJSON_IsNumber(longitude)) { Coordinate coord_point; coord_point.latitude = latitude->valuedouble; coord_point.longitude = longitude->valuedouble; coordinates.push_back(coord_point); } } } // 释放 JSON 对象 // cJSON_Delete(json); return coordinates; } /// @brief 创建JSON对象并保存到文件 void TjdUiAppSportModel::CreateJsonAndSaveToFile(SportInfo *dataArry, uint8_t count) { cJSON *sportsJson = CreateSportsJson(dataArry, count); SaveJsonToFile(SPORT_DATA_PATHA_NAME, sportsJson); cJSON_Delete(sportsJson); // 记得释放内存 } // 读取文件内容到字符串 char *TjdUiAppSportModel::ReadFileToString(const char *filename) { FILE *file = fopen(filename, "r"); if (file == NULL) return NULL; fseek(file, 0, SEEK_END); long length = ftell(file); fseek(file, 0, SEEK_SET); char *buffer = (char *)malloc(length + 1); if (buffer == NULL) { fclose(file); return NULL; } fread(buffer, 1, length, file); buffer[length] = '\0'; // Null-terminate the string fclose(file); return buffer; } // 解析JSON字符串 cJSON *TjdUiAppSportModel::ParseJson(const char *json_string) { return cJSON_Parse(json_string); } // 转换JSON到SportInfo数组 SportInfo *TjdUiAppSportModel::JsonToSportInfo(const cJSON *json, size_t *count) { if (!cJSON_IsArray(json)) return NULL; *count = cJSON_GetArraySize(json); SportInfo *sports = (SportInfo *)malloc(*count * sizeof(SportInfo)); if (sports == NULL) return NULL; for (size_t i = 0; i < *count; ++i) { cJSON *item = cJSON_GetArrayItem(json, i); if (!cJSON_IsObject(item)) continue; cJSON *index = cJSON_GetObjectItemCaseSensitive(item, "typeIndex"); cJSON *state = cJSON_GetObjectItemCaseSensitive(item, "state"); if (cJSON_IsNumber(index) && cJSON_IsBool(state)) { sports[i].index = static_cast(index->valueint); sports[i].state = state->valueint; } } return sports; } // 读取文件内容并转换JSON到SportInfo数组 SportInfo *TjdUiAppSportModel::ReadFileAndTransformToJson() { char *json_string = ReadFileToString(SPORT_DATA_PATHA_NAME); if (json_string == NULL) { fprintf(stderr, "Failed to read file\n"); return nullptr; } cJSON *json = ParseJson(json_string); free(json_string); // No longer needed after parsing if (json == NULL) { fprintf(stderr, "Failed to parse JSON\n"); return nullptr; } size_t count = 0; SportInfo *sportsData = JsonToSportInfo(json, &count); cJSON_Delete(json); // Clean up after parsing if (sportsData == NULL) { fprintf(stderr, "Failed to convert JSON to SportInfo\n"); return nullptr; } return sportsData; } // 修改SportInfo数组中指定运动项目的状态: 0:未添加 1:已添加 void TjdUiAppSportModel::ChangeOneSportState(cJSON *jsonArray, int typeIndex, bool new_state) { if (jsonArray == NULL) { fprintf(stderr, "Failed to parse JSON\n"); return; } int array_size = cJSON_GetArraySize(jsonArray); for (int i = 0; i < array_size; i++) { cJSON *item = cJSON_GetArrayItem(jsonArray, i); // if (item == NULL) { // static_print_info("Failed to get JSON array item\n"); // continue; // } cJSON *index = cJSON_GetObjectItem(item, "typeIndex"); // if (index == NULL || index->type != cJSON_Number) { // static_print_info("Invalid or missing 'typeIndex' item\n"); // continue; // } if (index && index->valueint == typeIndex) { cJSON *state = cJSON_GetObjectItem(item, "state"); if (state == NULL) { static_print_info("Invalid or missing 'state' item\n"); continue; } cJSON_ReplaceItemInObject(item, "state", cJSON_CreateBool(new_state)); return; } } } void TjdUiAppSportModel::ReadAndChangeSportState(int typeIndex, bool new_state) { char *json_string = ReadFileToString(SPORT_DATA_PATHA_NAME); cJSON *json = cJSON_Parse(json_string); if (json == NULL) { fprintf(stderr, "Failed to parse JSON\n"); return; } ChangeOneSportState(json, typeIndex, new_state); SaveJsonToFile(SPORT_DATA_PATHA_NAME, json); cJSON_Delete(json); free(json_string); } void TjdUiAppSportModel::SqlSetGoalSportCalorie(int calorie) { /*sql_fit_set_goal_sport_calorie(calorie);*/ } void TjdUiAppSportModel::SetIfSportSuspend(bool state) { tjd_service_sport_set_if_suspend(state); } void TjdUiAppSportModel::GPSServiceOpen(void) { tjd_service_gps_open(); } void TjdUiAppSportModel::GPSServiceClose(void) { tjd_service_gps_close(); } gnss_data_t TjdUiAppSportModel::GPSGetData(void) { gnss_data_t rmc_data; tjd_service_gps_get_data(&rmc_data); return rmc_data; } uint8_t TjdUiAppSportModel::SetDistancePopUpRemind(std::string distStr) { tjd_service_sport_set_if_popup(false); service_reset_sport_pop_up_flag(); service_set_pop_up_flag(SPORT_POPUP_MSG_TYPE_DISTANCE, true); size_t pos = distStr.find_first_of("0123456789"); std::string numberStr = distStr.substr(pos, distStr.size() - pos - 1); int distance = std::stoi(numberStr); service_set_goal_sport_dist(distance); // sql_fit_set_goal_sport_dist(0);// 用于调试测试 // static_print_info("=====distance goal = %d", distance); return SUCCESS; } uint8_t TjdUiAppSportModel::SetTimePopUpRemind(std::string timeStr) { tjd_service_sport_set_if_popup(false); service_reset_sport_pop_up_flag(); service_set_pop_up_flag(SPORT_POPUP_MSG_TYPE_TIME, true); std::regex pattern("(\\d{2})H(\\d{2})min"); std::smatch matches; if (std::regex_match(timeStr, matches, pattern)) { // 提取小时和分钟 int hours = std::stoi(matches[1].str()); int minutes = std::stoi(matches[2].str()); int seconds = hours * 3600 + minutes * 60; service_set_sport_goal_time(seconds); static_print_info("======set sport goal time %d=====\n", seconds); return SUCCESS; } return ERROR; } uint8_t TjdUiAppSportModel::SetCaloriesPopUpRemind(std::string calStr) { tjd_service_sport_set_if_popup(false); service_reset_sport_pop_up_flag(); service_set_pop_up_flag(SPORT_POPUP_MSG_TYPE_CALORIES, true); // std::string calStr = "100Kcal"; // 用于调试测试 std::string numberStr; for (char ch : calStr) { if (std::isdigit(ch)) { numberStr += ch; } else { break; // 遇到非数字字符则停止 } } int calories = std::stoi(numberStr); service_set_goal_sport_calorie(calories); // static_print_info("=====calories goal = %d", calories); return SUCCESS; } void TjdUiAppSportModel::SetSportEndRemindType(uint8_t type) { tjd_service_sport_set_end_remind_type(type); } void TjdUiAppSportModel::SetSportType(uint8_t type) { tjd_service_sport_set_sport_type(type); } uint8_t TjdUiAppSportModel::GetCurHeartRateValue(void) { return tjd_service_hrsensor_get_cur_hrvalue(); } uint8_t TjdUiAppSportModel::GetBatteryValue(void) { return tjd_service_charger_get_battery(); } uint32_t TjdUiAppSportModel::GetMotionOutTime(void) { return sql_setting_get_motion_out_time(); } uint8_t TjdUiAppSportModel::SportHeartRateControlHandler(SportingAction action, void *param) { uint8_t ret = 0; hr_api_info_t hr_api_info = {HR_SERVICE_UI_SPORT, 0xffffffff}; service_hrs_api *hrsensorInfo = tjd_service_hrs_get_ops(); switch (action) { case SportingAction::Start: hrsensorInfo->open(hr_api_info); break; case SportingAction::Stop: hrsensorInfo->close(); break; case SportingAction::GetStatus: // ret = HeartRateGetWearStatus(); break; default: break; } return ret; } void GetGnssGsvDataProcess(const struct minmea_sentence_gsv *frame) { // clang-format on for (int i = 0; i < 4; i++) { if (i == 0) { TjdUiAppSportModel::GetInstance()->SetMaxSnr(frame->sats[0].snr); } if (frame->sats[i].snr > TjdUiAppSportModel::GetInstance()->GetMaxSnr()) { TjdUiAppSportModel::GetInstance()->SetMaxSnr(frame->sats[i].snr); } printf("snr: %d\n", frame->sats[i].snr); } } void TjdUiAppSportModel::RegisterGnssChangedCB(void) { gnss_service_cb_t gnssServiceCb = {.service_changed_cb = nullptr, .rmc_status_cb = nullptr, .gsv_status_cb = GetGnssGsvDataProcess, .zda_status_cb = nullptr, .gga_status_cb = nullptr, .vtg_status_cb = nullptr}; register_gnss_changed_cb(&gnssServiceCb); } void TjdUiAppSportModel::UnregisterGnssChangedCB(void) { unregister_gnss_changed_cb(); } } // namespace TJD