diff --git a/iosApp/iosApp/Libs/Modules/DeviceCenter.m b/iosApp/iosApp/Libs/Modules/DeviceCenter.m index b172821..56b7d00 100644 --- a/iosApp/iosApp/Libs/Modules/DeviceCenter.m +++ b/iosApp/iosApp/Libs/Modules/DeviceCenter.m @@ -292,8 +292,6 @@ NSString * const CP_NAME = @"BlackShark"; #pragma mark -- 睡眠相关 -(void)querySleep:(NSDate *)date { - - self.calcSleepDate = date; // 0 通知日期变更,其他界面各自查询 [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadWithName:NOTI_NAME_SLEEP_DATE_CHANGE object:self.calcSleepDate]; @@ -331,6 +329,7 @@ NSString * const CP_NAME = @"BlackShark"; // [results addObjectsFromArray:@[napData, napData2]]; #endif + if (results.count) { // strongSelf.currentSelectDataSleepArray = [NSMutableArray new]; strongSelf.currentSelectNapArray = [NSMutableArray new]; diff --git a/shared/src/commonMain/kotlin/com/whitefish/ring/data/DeviceDataProvider.kt b/shared/src/commonMain/kotlin/com/whitefish/ring/data/DeviceDataProvider.kt index e747a79..c66291d 100644 --- a/shared/src/commonMain/kotlin/com/whitefish/ring/data/DeviceDataProvider.kt +++ b/shared/src/commonMain/kotlin/com/whitefish/ring/data/DeviceDataProvider.kt @@ -3,7 +3,6 @@ package com.whitefish.ring.data import com.whitefish.ring.bean.ui.HeartRate import com.whitefish.ring.bean.ui.SleepState import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.last import kotlinx.coroutines.flow.map suspend fun mac() = obtainDataStore().data.map { it[address] }.first() diff --git a/shared/src/commonMain/kotlin/com/whitefish/ring/ui/chart/sleep/SleepChartPreview.kt b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/chart/sleep/SleepChartPreview.kt index b82cd98..0f986e1 100644 --- a/shared/src/commonMain/kotlin/com/whitefish/ring/ui/chart/sleep/SleepChartPreview.kt +++ b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/chart/sleep/SleepChartPreview.kt @@ -22,7 +22,7 @@ fun SleepChartPreview( modifier: Modifier = Modifier ) { // 使用示例睡眠数据 - val sampleData = remember { createSampleSleepData() } +// val sampleData = remember { createSampleSleepData() } Card( modifier = modifier.padding(16.dp), @@ -40,16 +40,16 @@ fun SleepChartPreview( modifier = Modifier.padding(bottom = 16.dp) ) - // 睡眠图表 - SleepChart( - sleepData = sampleData, - modifier = Modifier - .fillMaxWidth() - .height(120.dp), - barWidthMultiplier = 1f, - barHeight = 10f - ) - +// // 睡眠图表 +// SleepChart( +// sleepData = sampleData, +// modifier = Modifier +// .fillMaxWidth() +// .height(120.dp), +// barWidthMultiplier = 1f, +// barHeight = 10f +// ) +// // 图例 SleepChartLegend( modifier = Modifier @@ -57,13 +57,13 @@ fun SleepChartPreview( .padding(top = 16.dp) ) - // 睡眠统计信息 - SleepStatistics( - sleepData = sampleData, - modifier = Modifier - .fillMaxWidth() - .padding(top = 16.dp) - ) +// // 睡眠统计信息 +// SleepStatistics( +// sleepData = sampleData, +// modifier = Modifier +// .fillMaxWidth() +// .padding(top = 16.dp) +// ) } } } @@ -71,17 +71,17 @@ fun SleepChartPreview( /** * 预览用的示例数据 */ -fun createSampleSleepData(): List { - return listOf( - SleepSegment(state = 0, durationMinutes = 30f), // 清醒 - SleepSegment(state = 2, durationMinutes = 120f), // 深睡 - SleepSegment(state = 1, durationMinutes = 90f), // 浅睡 - SleepSegment(state = 3, durationMinutes = 60f), // REM - SleepSegment(state = 2, durationMinutes = 100f), // 深睡 - SleepSegment(state = 1, durationMinutes = 80f), // 浅睡 - SleepSegment(state = 0, durationMinutes = 20f) // 清醒 - ) -} +//fun createSampleSleepData(): List { +// return listOf( +// SleepSegment(state = 0, durationMinutes = 30f), // 清醒 +// SleepSegment(state = 2, durationMinutes = 120f), // 深睡 +// SleepSegment(state = 1, durationMinutes = 90f), // 浅睡 +// SleepSegment(state = 3, durationMinutes = 60f), // REM +// SleepSegment(state = 2, durationMinutes = 100f), // 深睡 +// SleepSegment(state = 1, durationMinutes = 80f), // 浅睡 +// SleepSegment(state = 0, durationMinutes = 20f) // 清醒 +// ) +//} /** * 睡眠图表图例 @@ -150,9 +150,9 @@ private fun SleepStatistics( // 计算各状态总时长 val stateTime = remember(sleepData) { val times = mutableMapOf() - sleepData.forEach { segment -> - times[segment.state] = (times[segment.state] ?: 0f) + segment.durationMinutes - } +// sleepData.forEach { segment -> +// times[segment.state] = (times[segment.state] ?: 0f) + segment.durationMinutes +// } times } diff --git a/shared/src/commonMain/kotlin/com/whitefish/ring/ui/home/state/StateViewModel.kt b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/home/state/StateViewModel.kt index c8166c8..6844d11 100644 --- a/shared/src/commonMain/kotlin/com/whitefish/ring/ui/home/state/StateViewModel.kt +++ b/shared/src/commonMain/kotlin/com/whitefish/ring/ui/home/state/StateViewModel.kt @@ -2,8 +2,6 @@ package com.whitefish.ring.ui.home.state import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.whitefish.app.ui.chart.sleep.createSampleSleepData -import com.whitefish.ring.data.MockDataProvider import com.whitefish.ring.data.getHeartRate import com.whitefish.ring.data.getSleep import com.whitefish.ring.obtainDeviceManager @@ -12,6 +10,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import com.whitefish.ring.ui.home.state.components.ExerciseGoalData import com.whitefish.ring.ui.home.state.components.RecoveryScoreData +import com.whitefish.ring.utils.getPastDayStartMillis import com.whitefish.ring.utils.getTodayEndMillis import com.whitefish.ring.utils.getTodayStartMillis import com.whitefish.ring.utils.nowMilliseconds @@ -36,7 +35,7 @@ class StateViewModel : ViewModel() { val uiState: StateFlow = _uiState.asStateFlow() init { - loadStateData() + loadInitData() viewModelScope.launch { obtainDeviceManager().bleReadyStateFlow.collectLatest { if (it){ @@ -44,40 +43,48 @@ class StateViewModel : ViewModel() { today().atTime(0, 0).toInstant(TimeZone.currentSystemDefault()).toEpochMilliseconds(), nowMilliseconds() ) - Napier.i { "heart rates:${heartRates}" } - _uiState.value.copy(stateCards = _uiState.value.stateCards.map { card -> - if (card.type is StateCardType.HeartRate) { - card.copy(type = StateCardType.HeartRate(heartRates.map { it.value })) - } else { - card - } - }).also { newState -> + val sleepState = getSleep(getPastDayStartMillis(1), getTodayEndMillis()) + + _uiState.value.copy(stateCards = arrayListOf( + StateCardData( + title = "心率", + date = "${today().month}/${today().dayOfMonth}", + subtitle = "bpm", + isFullWidth = false, + type = StateCardType.HeartRate(heartRates.map { it.value }) + ), + StateCardData( + title = "睡眠", + date = "${today().month}/${today().dayOfMonth}", + subtitle = "昨晚", + isFullWidth = false, + type = StateCardType.SleepState(sleepState.segments) + ) + )).let { newState -> _uiState.value = newState } - - getSleep(getTodayStartMillis(), getTodayEndMillis()) } } } } - private fun loadStateData() { + private fun loadInitData() { val mockData = listOf( StateCardData( title = "心率", - date = "8/9", + date = "${today().month}/${today().dayOfMonth}", subtitle = "bpm", isFullWidth = false, type = StateCardType.HeartRate(emptyList()) ), StateCardData( title = "睡眠", - date = "8/9", + date = "${today().month}/${today().dayOfMonth}", subtitle = "昨晚", isFullWidth = false, - type = StateCardType.SleepState(createSampleSleepData()) + type = StateCardType.SleepState(emptyList()) ) ) _uiState.value = _uiState.value.copy(stateCards = mockData) } -} \ No newline at end of file +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/com/whitefish/ring/utils/Utils.kt b/shared/src/commonMain/kotlin/com/whitefish/ring/utils/Utils.kt index 979258f..ed6e599 100644 --- a/shared/src/commonMain/kotlin/com/whitefish/ring/utils/Utils.kt +++ b/shared/src/commonMain/kotlin/com/whitefish/ring/utils/Utils.kt @@ -1,5 +1,6 @@ package com.whitefish.ring.utils +import androidx.compose.ui.geometry.Offset import kotlinx.datetime.Clock import kotlinx.datetime.DateTimeUnit import kotlinx.datetime.LocalDate @@ -24,3 +25,9 @@ fun getTodayStartMillis(): Long { fun getTodayEndMillis(): Long { return today().atTime(23, 59, 59, 999999999).toInstant(UtcOffset.ZERO).toEpochMilliseconds() } + + +fun pastDay(offset: Int) = today().minus(offset, DateTimeUnit.DAY) +fun getPastDayStartMillis(offset: Int): Long { + return pastDay(offset).atTime(0,0).toInstant(UtcOffset.ZERO).toEpochMilliseconds() +} \ No newline at end of file diff --git a/shared/src/iosMain/kotlin/com/whitefish/ring/data/DeviceDataProvider.ios.kt b/shared/src/iosMain/kotlin/com/whitefish/ring/data/DeviceDataProvider.ios.kt index 0b8f8fb..fb426de 100644 --- a/shared/src/iosMain/kotlin/com/whitefish/ring/data/DeviceDataProvider.ios.kt +++ b/shared/src/iosMain/kotlin/com/whitefish/ring/data/DeviceDataProvider.ios.kt @@ -1,18 +1,31 @@ package com.whitefish.ring.data import com.whitefish.app.ui.chart.sleep.SleepSegment +import com.whitefish.ring.DeviceManager import com.whitefish.ring.bean.ui.HeartRate import com.whitefish.ring.bean.ui.SleepState import com.whitefish.ring.objc.DBHeartRate import com.whitefish.ring.objc.DBSleepData import com.whitefish.ring.objc.StagingListObj import com.whitefish.ring.objc.StagingSubObj +import com.whitefish.ring.obtainDeviceManager import com.whitefish.ring.utils.getCurrentTimestamp import com.whitefish.ring.utils.timestampToDate +import com.whitefish.ring.utils.today +import io.github.aakira.napier.Napier import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.IO import kotlinx.coroutines.withContext +import kotlinx.datetime.LocalDate +import kotlinx.datetime.LocalTime +import kotlinx.datetime.TimeZone +import kotlinx.datetime.UtcOffset +import kotlinx.datetime.atStartOfDayIn +import kotlinx.datetime.atTime +import kotlinx.datetime.toInstant +import kotlinx.datetime.toNSDateComponents +import platform.Foundation.NSAllocateMemoryPages import platform.Foundation.timeIntervalSince1970 import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine @@ -50,56 +63,52 @@ actual suspend fun getSleep( return@withContext suspendCoroutine { coroutine -> macAddress?.let { val segments = mutableListOf() - + Napier.i { "${start.timestampToDate()} --> ${end.timestampToDate()}" } DBSleepData.queryDbSleepBy( it, start.timestampToDate().timeIntervalSince1970, end.timestampToDate().timeIntervalSince1970 ) { result -> - val sleepDataList = result?.map { it as DBSleepData } var totalSeconds = 0 + result?.let { + it.forEach { + (it as DBSleepData).apply { + totalSeconds += it.duration.intValue + Napier.i { "sleep isNap:${isNap},start:${it.sleepStart},end:${it.sleepEnd},duration:${duration}" } + stagingData.ousideStagingList?.forEach { + (it as StagingSubObj).apply { + Napier.i { "staging type:${type.value}, list size:${list.size} ========>" } + list.forEach { item -> + (item as StagingListObj).apply { + Napier.i { "staging item time:${time.doubleValue}" } + } + } + } + } + } + } + } + + val sleepDataList = result?.map { it as DBSleepData } + sleepDataList?.let { dataList -> dataList.forEach { sleepData -> if (!sleepData.isNap) { - totalSeconds += sleepData.duration.intValue // 处理睡眠分期数据 - sleepData.stagingData?.ousideStagingList?.let { stagingList -> + sleepData.stagingData.ousideStagingList?.let { stagingList -> for (i in 0 until stagingList.size) { val stagingObj = stagingList[i] as StagingSubObj - - // 计算该阶段的持续时间 - val duration = if (i == 0) { - // 第一个阶段:从开始到结束 - stagingObj.list.let { list -> - val firstTime = (list.first() as? StagingListObj)?.time?.doubleValue ?: 0.0 - val lastTime = (list.last() as? StagingListObj)?.time?.doubleValue ?: 0.0 - lastTime - firstTime - } - } else { - // 后续阶段:从前一个阶段结束到当前阶段结束 - val prevStagingObj = stagingList[i - 1] as StagingSubObj - val currentEndTime = (stagingObj.list.first() as? StagingListObj)?.time?.doubleValue ?: 0.0 - val prevEndTime = (prevStagingObj.list.last() as? StagingListObj)?.time?.doubleValue ?: 0.0 - currentEndTime - prevEndTime - } - - // 转换睡眠状态类型到用户定义的状态 + val stageDuration = (stagingObj.list.last() as StagingListObj).time.doubleValue - (stagingObj.list.first() as StagingListObj).time.doubleValue / 60 val mappedState = mapSleepStageToState(stagingObj.type.value.toInt()) - - // 只添加有效的睡眠状态(跳过 NONE 状态) - if (mappedState >= 0) { - val durationMinutes = (duration / 60.0).toFloat() - if (durationMinutes > 0) { - segments.add(SleepSegment(mappedState, durationMinutes)) - } - } + segments.add(SleepSegment(mappedState, stageDuration.toFloat())) } } } } } - coroutine.resume(SleepState(totalSeconds =totalSeconds, segments = segments )) + + coroutine.resume(SleepState(totalSeconds =totalSeconds,segments = segments)) } } }