mcu_hi3321_watch/tjd/ui/app/main/TjdUiAppMainPageHealth.cpp
2025-05-26 20:15:20 +08:00

386 lines
13 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*----------------------------------------------------------------------------
* Copyright (c) TJD Technologies Co., Ltd. 2024. All rights reserved.
*
* Description: TjdUiAppMainPageHealth.cpp
*
* Author: luziquan@ss-tjd.com
*
* Create: 2024-12-27
*--------------------------------------------------------------------------*/
#include "TjdUiAppMainPageHealth.h"
#include "TjdUiAppMainPresenter.h"
#include "TjdUiImageIds.h"
#include "TjdUiMemManage.h"
#include "TjdUiMultiLanguageExt.h"
#include "common/image_cache_manager.h"
#include "hal_tick.h"
#include "rtc_api.h"
#include "sql_fit.h"
#include "sys_config.h"
#include <iomanip>
#include <sstream>
#include <string>
#define ENABLE_PRINT_INFO 1
#define DEBUG_ENABLE 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__) // 错误信息打印一般常开
#if DEBUG_ENABLE
#define static_print_debug(...) sys_ui_log_d(__VA_ARGS__)
#else
#define static_print_debug(...)
#endif
#else
#define static_print_info(...)
#define static_print_warn(...)
#define static_print_error(...)
#endif
#define MAIN_HR_IMAGE_BIN_PATH TJD_IMAGE_PATH "img_main_health.bin"
namespace TJD {
static OHOS::ImageAnimatorInfo g_animatorInfo[3] = {
{nullptr, {135, 35}, 56, 50, OHOS::IMG_SRC_IMAGE_INFO},
{nullptr, {135, 35}, 56, 50, OHOS::IMG_SRC_IMAGE_INFO},
{nullptr, {135, 35}, 56, 50, OHOS::IMG_SRC_IMAGE_INFO},
};
static constexpr uint8_t AVERAGE_HOURS = 12;
static constexpr uint8_t VALUES_PER_HOUR = 24;
static TjdUiAppMainPageHealth *g_heartRatePage = nullptr;
TjdUiAppMainPageHealth::TjdUiAppMainPageHealth()
{
SetPosition(0, 0, OHOS::Screen::GetInstance().GetWidth(), OHOS::Screen::GetInstance().GetHeight());
g_heartRatePage = this;
}
TjdUiAppMainPageHealth::~TjdUiAppMainPageHealth()
{
UnLoad();
g_heartRatePage = nullptr;
}
TjdUiAppMainPageHealth *TjdUiAppMainPageHealth::GetInstance(void) { return g_heartRatePage; }
void TjdUiAppMainPageHealth::PreLoad(void)
{
auto &image = OHOS::ImageCacheManager::GetInstance();
// OHOS::MemCheck::GetInstance()->EnableLeakCheck(true);
static_print_debug("HeartRatePage::PreLoad");
if (!viewiInitStatus) {
image.LoadAllInMultiRes(MAIN_HR_IMAGE_BIN_PATH);
g_animatorInfo[0].imageInfo = image.LoadOneInMultiRes(IMG_MAIN_HEALTH_HR_DT_01_03, MAIN_HR_IMAGE_BIN_PATH);
g_animatorInfo[1].imageInfo = image.LoadOneInMultiRes(IMG_MAIN_HEALTH_HR_DT_02_03, MAIN_HR_IMAGE_BIN_PATH);
g_animatorInfo[2].imageInfo = image.LoadOneInMultiRes(IMG_MAIN_HEALTH_HR_DT_03_03, MAIN_HR_IMAGE_BIN_PATH);
InitView();
UpdateValue();
HrPointArrayInit();
imageAnimator_->Start();
viewiInitStatus = true;
}
}
void TjdUiAppMainPageHealth::UnLoad(void)
{
static_print_debug("HeartRatePage::UnLoad");
if (viewiInitStatus) {
OHOS::ImageCacheManager::GetInstance().UnloadAllInMultiRes(MAIN_HR_IMAGE_BIN_PATH);
RemoveAll();
if (container_) {
container_->RemoveAll();
delete container_;
container_ = nullptr;
}
if (chartBg_) {
delete chartBg_;
chartBg_ = nullptr;
}
if (imageAnimator_) {
imageAnimator_->Stop();
delete imageAnimator_;
imageAnimator_ = nullptr;
}
if (canvas_) {
canvas_->Clear();
delete canvas_;
canvas_ = nullptr;
}
if (hrTitleValue_) {
delete hrTitleValue_;
hrTitleValue_ = nullptr;
}
if (hrTitleUnit_) {
delete hrTitleUnit_;
hrTitleUnit_ = nullptr;
}
if (hrBg_) {
delete hrBg_;
hrBg_ = nullptr;
}
if (hrValue_) {
delete hrValue_;
hrValue_ = nullptr;
}
if (spo2Bg_) {
delete spo2Bg_;
spo2Bg_ = nullptr;
}
if (spo2Value_) {
delete spo2Value_;
spo2Value_ = nullptr;
}
if (sleepBg_) {
delete sleepBg_;
sleepBg_ = nullptr;
}
if (sleepValue_) {
delete sleepValue_;
sleepValue_ = nullptr;
}
viewiInitStatus = false;
}
}
static void DrawHeartRateCurve(OHOS::UICanvasExt *canvas, std::vector<int> &heartRates)
{
if (canvas == nullptr || heartRates.empty()) {
return;
}
canvas->Clear();
OHOS::PaintExt paint;
paint.SetFillColor(OHOS::Color::Black());
paint.SetStrokeWidth(3);
paint.SetStyle(OHOS::Paint::PaintStyle::STROKE_STYLE);
paint.SetOpacity(OHOS::OPA_OPAQUE);
paint.SetStrokeColor(OHOS::Color::Red());
paint.SetJoinType(OHOS::JoinType::JOIN_ROUND);
paint.SetGradientSpread(OHOS::GradientSpread::SPREAD_PAD);
paint.SetAntialiased(true);
const int width = canvas->GetWidth();
const int height = canvas->GetHeight();
const int minHeartRate = 0; // 最小心率
const int maxHeartRate = 120; // TODO: 不知道最大最小心率是多少暂定为0-120
const int halfStrokeWidth = paint.GetStrokeWidth() / 2;
const int adjustedWidth = width - paint.GetStrokeWidth();
const int adjustedHeight = height - paint.GetStrokeWidth();
OHOS::Point startPoint = {halfStrokeWidth,
adjustedHeight -
(heartRates[0] - minHeartRate) * adjustedHeight / (maxHeartRate - minHeartRate) +
halfStrokeWidth};
for (size_t i = 1; i < heartRates.size(); ++i) {
int x1 = (i - 1) * adjustedWidth / (heartRates.size() - 1) + halfStrokeWidth;
int y1 = adjustedHeight - (heartRates[i - 1] - minHeartRate) * adjustedHeight / (maxHeartRate - minHeartRate) +
halfStrokeWidth;
int x2 = i * adjustedWidth / (heartRates.size() - 1) + halfStrokeWidth;
int y2 = adjustedHeight - (heartRates[i] - minHeartRate) * adjustedHeight / (maxHeartRate - minHeartRate) +
halfStrokeWidth;
OHOS::Point controlPoint1 = {x1 + (x2 - x1) / 2, y1};
OHOS::Point controlPoint2 = {x1 + (x2 - x1) / 2, y2};
OHOS::Point endPoint = {x2, y2};
canvas->DrawCurve(startPoint, controlPoint1, controlPoint2, endPoint, paint);
startPoint = endPoint;
}
}
void TjdUiAppMainPageHealth::InitView(void)
{
auto &image = OHOS::ImageCacheManager::GetInstance();
container_ = new OHOS::UIViewGroup();
container_->SetPosition(0, 0, OHOS::Screen::GetInstance().GetWidth(), OHOS::Screen::GetInstance().GetHeight());
canvas_ = new OHOS::UICanvasExt();
canvas_->SetPosition(81, 102, 304, 60);
container_->Add(canvas_);
chartBg_ = new OHOS::UIImageView();
chartBg_->SetPosition(50, 107, 366, 85);
auto imageInfo = image.LoadOneInMultiRes(IMG_MAIN_HEALTH_CHART, MAIN_HR_IMAGE_BIN_PATH);
chartBg_->SetSrc(imageInfo);
container_->Add(chartBg_);
imageAnimator_ = new OHOS::UIImageAnimatorView();
imageAnimator_->SetPosition(135, 36, 56, 50);
imageAnimator_->SetImageAnimatorSrc(g_animatorInfo, 3, 250);
container_->Add(imageAnimator_);
hrTitleValue_ = new OHOS::UILabel();
hrTitleValue_->SetFont(TJD_DIN_MEDIUM_FONT_FILENAME, 40);
hrTitleValue_->SetText("098");
hrTitleValue_->SetLineBreakMode(OHOS::UILabel::LINE_BREAK_WRAP);
hrTitleValue_->SetAlign(OHOS::TEXT_ALIGNMENT_CENTER, OHOS::TEXT_ALIGNMENT_CENTER);
hrTitleValue_->SetPosition(198, 36, 64, 29);
container_->Add(hrTitleValue_);
hrTitleUnit_ = new OHOS::UILabelExt();
hrTitleUnit_->SetPosition(hrTitleValue_->GetX() + hrTitleValue_->GetWidth() + 11, 42, 72, 27);
hrTitleUnit_->SetFont(TJD_VECTOR_FONT_FILENAME, 28);
// hrTitleUnit_->SetText("次/分");
hrTitleUnit_->SetTextId(STR_ID_110);
hrTitleUnit_->SetLineBreakMode(OHOS::UILabel::LINE_BREAK_WRAP);
hrTitleUnit_->SetAlign(OHOS::TEXT_ALIGNMENT_CENTER, OHOS::TEXT_ALIGNMENT_CENTER);
container_->Add(hrTitleUnit_);
hrBg_ = new OHOS::UIImageView();
hrBg_->SetPosition(50, 214, 121, 124);
imageInfo = image.LoadOneInMultiRes(IMG_MAIN_HEALTH_HR, MAIN_HR_IMAGE_BIN_PATH);
hrBg_->SetSrc(imageInfo);
container_->Add(hrBg_);
hrValue_ = new OHOS::UILabel();
hrValue_->SetFont(TJD_DIN_MEDIUM_FONT_FILENAME, 30);
hrValue_->SetText("098");
hrValue_->SetLineBreakMode(OHOS::UILabel::LINE_BREAK_WRAP);
hrValue_->SetAlign(OHOS::TEXT_ALIGNMENT_CENTER, OHOS::TEXT_ALIGNMENT_CENTER);
hrValue_->SetPosition(86, 253, 48, 23);
container_->Add(hrValue_);
spo2Bg_ = new OHOS::UIImageView();
spo2Bg_->SetPosition(173, 309, 121, 124);
imageInfo = image.LoadOneInMultiRes(IMG_MAIN_HEALTH_SPO2, MAIN_HR_IMAGE_BIN_PATH);
spo2Bg_->SetSrc(imageInfo);
container_->Add(spo2Bg_);
spo2Value_ = new OHOS::UILabel();
spo2Value_->SetFont(TJD_DIN_MEDIUM_FONT_FILENAME, 30);
spo2Value_->SetText("098%");
spo2Value_->SetLineBreakMode(OHOS::UILabel::LINE_BREAK_WRAP);
spo2Value_->SetAlign(OHOS::TEXT_ALIGNMENT_CENTER, OHOS::TEXT_ALIGNMENT_CENTER);
spo2Value_->SetPosition(204, 348, 72, 21);
container_->Add(spo2Value_);
sleepBg_ = new OHOS::UIImageView();
sleepBg_->SetPosition(296, 214, 121, 124);
imageInfo = image.LoadOneInMultiRes(IMG_MAIN_HEALTH_SLEEP, MAIN_HR_IMAGE_BIN_PATH);
sleepBg_->SetSrc(imageInfo);
container_->Add(sleepBg_);
sleepValue_ = new OHOS::UILabel();
sleepValue_->SetFont(TJD_DIN_MEDIUM_FONT_FILENAME, 30);
sleepValue_->SetText("7.35");
sleepValue_->SetLineBreakMode(OHOS::UILabel::LINE_BREAK_WRAP);
sleepValue_->SetAlign(OHOS::TEXT_ALIGNMENT_CENTER, OHOS::TEXT_ALIGNMENT_CENTER);
sleepValue_->SetPosition(323, 253, 68, 20);
container_->Add(sleepValue_);
Add(container_);
}
void TjdUiAppMainPageHealth::UpdateValue(void)
{
std::stringstream ss;
struct rtc_time localTime = {};
tjd_driver_rtc_get_ops()->get_rtc_time(&localTime);
int curHrValue = sql_fit_get_last_hr(localTime.tm_wday);
int curSpo2Value = sql_fit_get_last_spo2(localTime.tm_wday);
int curSleepTime = sql_fit_get_currentSleepTime_data();
if (curHrValue == 0) {
if (localTime.tm_wday == 0) {
curHrValue = sql_fit_get_last_hr(6);
} else {
curHrValue = sql_fit_get_last_hr(localTime.tm_wday - 1);
}
}
if (hrTitleValue_ && hrValue_) {
if (curHrValue == 0) {
hrTitleValue_->SetText("---");
hrValue_->SetText("-");
} else {
ss.str("");
ss << std::setw(3) << std::setfill('0') << curHrValue;
hrTitleValue_->SetText(ss.str().c_str());
hrValue_->SetText(ss.str().c_str());
}
}
if (spo2Value_) {
if (curSpo2Value == 0) {
spo2Value_->SetText("-%");
} else {
ss.str("");
ss << std::setw(3) << std::setfill('0') << curSpo2Value << "%";
spo2Value_->SetText(ss.str().c_str());
}
}
if (sleepValue_) {
if (curSleepTime == 0) {
sleepValue_->SetText("-");
} else {
sleepValue_->SetText(std::to_string(curSleepTime).c_str());
}
}
}
static void CalculateHourlyAverages(const uint8_t *hr_day_array, uint8_t *hr_hourly_avg)
{
for (int i = 0; i < AVERAGE_HOURS; ++i) {
int sum = 0;
int count = 0;
const uint8_t *segment = hr_day_array + i * VALUES_PER_HOUR;
for (const uint8_t *ptr = segment; ptr < segment + VALUES_PER_HOUR; ++ptr) {
if (*ptr != 0) {
sum += *ptr;
++count;
}
}
hr_hourly_avg[i] = (count > 0) ? (sum / count) : 0;
}
}
void TjdUiAppMainPageHealth::HrPointArrayInit(void)
{
uint8_t hr_hourly_avg[AVERAGE_HOURS] = {};
uint8_t *storeArray;
int32_t num = sql_fit_get_hr_daydata(&storeArray);
CalculateHourlyAverages(storeArray, hr_hourly_avg);
hrDayArray_.clear();
for (int i = 0; i < AVERAGE_HOURS; ++i) {
hrDayArray_.emplace_back(hr_hourly_avg[i]);
}
DrawHeartRateCurve(canvas_, hrDayArray_);
}
void TjdUiAppMainPageHealth::NotifyTick(void)
{
static uint32_t lastUpdateTime = 0;
static uint32_t lastInitTime = 0;
uint32_t curTime = OHOS::HALTick::GetInstance().GetTime();
if (lastUpdateTime == 0) {
lastUpdateTime = curTime;
}
if (lastInitTime == 0) {
lastInitTime = curTime;
}
/* 每5秒刷新一次 */
if (curTime - lastUpdateTime >= 5 * 1000) {
lastUpdateTime = curTime;
UpdateValue();
}
/* 每5分钟刷新一次 */
if (curTime - lastInitTime >= 5 * 60 * 1000) {
lastInitTime = curTime;
HrPointArrayInit();
}
}
} // namespace TJD