0e13f2bbb6
A. Reminder: ReminderService (AlarmManager chain), ReminderSettingsView, bell icon in Piezo top bar, reschedule on catheterization B. Consent: ConsentView (3 mandatory items), onboarding→consent→registration flow, persistent consent storage, sex field in User + Registration Step 2 C. Diary: 24hUV, NUV, NPI, max capacity, day/night frequency metrics card D. Design: DesignTokens.kt, 48dp touch targets, DonutChart semantics, pressScale modifier E. Quality: BuildConfig.DEBUG log wrapping, DateFormatter caching, background I/O for diary store Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
95 lines
3.0 KiB
Kotlin
95 lines
3.0 KiB
Kotlin
package com.example.medilightv2android.managers
|
|
|
|
import androidx.compose.runtime.mutableIntStateOf
|
|
import androidx.compose.runtime.mutableStateListOf
|
|
import androidx.compose.runtime.mutableStateOf
|
|
import com.example.medilightv2android.models.NirsMagResponse
|
|
import com.example.medilightv2android.models.NirsMcjPacket
|
|
import com.example.medilightv2android.models.NirsConstants
|
|
import com.example.medilightv2android.BuildConfig
|
|
import com.example.medilightv2android.models.NirsOxyResult
|
|
|
|
class NirsManager private constructor() {
|
|
companion object {
|
|
val instance: NirsManager by lazy { NirsManager() }
|
|
}
|
|
|
|
// State
|
|
val isStreaming = mutableStateOf(false)
|
|
val isCalibrated = mutableStateOf(false)
|
|
val isFilterEnabled = mutableStateOf(true)
|
|
|
|
// Current Values
|
|
val currentOxy = mutableStateOf(NirsOxyResult.zero)
|
|
val sampleCount = mutableIntStateOf(0)
|
|
|
|
// Chart History
|
|
val oxyHbO2History = mutableStateListOf<Double>()
|
|
val oxyHbHistory = mutableStateListOf<Double>()
|
|
|
|
// Internal
|
|
val calcEngine = NirsCalcEngine()
|
|
private val maxPoints = NirsConstants.maxHistoryPoints
|
|
|
|
// Calibration
|
|
fun setCalibrationGain(response: NirsMagResponse) {
|
|
calcEngine.setGainFromMag(response)
|
|
isCalibrated.value = true
|
|
}
|
|
|
|
// Process mcj Packet
|
|
fun processMcjPacket(packet: NirsMcjPacket) {
|
|
val result = calcEngine.process(packet.ledValues)
|
|
currentOxy.value = result.oxyFiltered
|
|
sampleCount.intValue++
|
|
|
|
if (BuildConfig.DEBUG && (sampleCount.intValue <= 3 || sampleCount.intValue % 50 == 0)) {
|
|
android.util.Log.d("NirsManager", "Sample #${sampleCount.intValue}: " +
|
|
"hasGain=${calcEngine.hasGain}, " +
|
|
"led[0..5]=${packet.ledValues.take(6)}, " +
|
|
"degain[0..2]=${result.degain.take(3).map { "%.2f".format(it) }}, " +
|
|
"oxy=StO2:${"%.1f".format(result.oxyFiltered.stO2)} HbO2:${"%.1f".format(result.oxyFiltered.hbO2)} HHb:${"%.1f".format(result.oxyFiltered.hb)}")
|
|
}
|
|
|
|
appendTrimming(oxyHbO2History, result.oxyFiltered.hbO2)
|
|
appendTrimming(oxyHbHistory, result.oxyFiltered.hb)
|
|
}
|
|
|
|
// Controls
|
|
fun toggleFilter() {
|
|
isFilterEnabled.value = !isFilterEnabled.value
|
|
calcEngine.isFilterEnabled = isFilterEnabled.value
|
|
if (isFilterEnabled.value) calcEngine.resetFilter()
|
|
}
|
|
|
|
fun clearData() {
|
|
oxyHbO2History.clear()
|
|
oxyHbHistory.clear()
|
|
currentOxy.value = NirsOxyResult.zero
|
|
sampleCount.intValue = 0
|
|
calcEngine.resetFilter()
|
|
}
|
|
|
|
fun startStreaming() {
|
|
isStreaming.value = true
|
|
calcEngine.resetFilter()
|
|
}
|
|
|
|
fun stopStreaming() {
|
|
isStreaming.value = false
|
|
}
|
|
|
|
fun reset() {
|
|
stopStreaming()
|
|
clearData()
|
|
isCalibrated.value = false
|
|
calcEngine.clearGain()
|
|
}
|
|
|
|
// Helpers
|
|
private fun appendTrimming(list: MutableList<Double>, value: Double) {
|
|
list.add(value)
|
|
while (list.size > maxPoints) list.removeAt(0)
|
|
}
|
|
}
|