SETTINGS: Auto Scan Interval 슬라이더 추가 (600~5000ms, 200ms 단위)

기존 hardcoded 600ms를 사용자가 Settings에서 조절 가능하도록 변경.

추가:
- PiezoSettings.autoScanIntervalMs (default 600ms) 필드
- AppState.savePiezoSettings() 헬퍼 — storage에 piezoSettings 영속화
- Settings 패널에 슬라이더 (Catheter Threshold 다음, Bladder Capacity와 동일 톤)
  - 범위 600~5000ms, 200ms 단위로 snap (roundToInt + 200 배수)
  - 현재값 mL 표시 패턴과 동일하게 "X ms" 우측 표시
- Settings Save 버튼이 실제로 영속화하도록 savePiezoSettings() 호출 추가
  (이전에는 showSettings=false만 호출 — maxVolume 등도 메모리만 변경되던 버그 동시 fix)

동작:
- PiezoMonitor Auto loop의 remaining = targetIntervalMs - elapsed
- BleManager throttle(600ms)이 하한이라 600 미만 설정해도 막힘 — 의미 있는 최소가 600
- 5000ms로 설정 시 거의 정적 모니터링 (배터리/CPU 절약)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-26 11:37:25 +09:00
parent 4252464ef6
commit 63bb8586ec
3 changed files with 33 additions and 8 deletions
@@ -231,6 +231,10 @@ class AppState(private val context: Context) {
storage.catheterizationLevel = threshold storage.catheterizationLevel = threshold
} }
fun savePiezoSettings() {
storage.piezoSettings = piezoSettings
}
val estimatedVolume: Int val estimatedVolume: Int
get() { get() {
if (BladderLevel.MAX_LEVEL <= 0) return 0 if (BladderLevel.MAX_LEVEL <= 0) return 0
@@ -15,9 +15,9 @@ data class PiezoSettings(
var posture: Posture = Posture.SITTING, var posture: Posture = Posture.SITTING,
var sensorPosition: SensorPosition = SensorPosition.LOWER_ABDOMEN, var sensorPosition: SensorPosition = SensorPosition.LOWER_ABDOMEN,
var maxVolume: Int = 500, var maxVolume: Int = 500,
// BV calculation tweak — anterior-wall pull-in magnitude in millimetres. // Charles 833c110: BV calculation tweak — anterior-wall pull-in magnitude in millimetres.
// The stored value is always non-negative; BV calculation applies it as a
// negative (probe-ward) shift on the ant index. Default 0 keeps legacy
// behaviour.
var antOffsetMm: Int = 0, var antOffsetMm: Int = 0,
// 우리 4ae25d9: Auto Scan 측정 사이클 목표 간격 (ms).
// BleManager.canSendMaa() time gate와 동일 값이 기본. 600ms 미만은 throttle이 막음.
var autoScanIntervalMs: Int = 600
) )
@@ -59,6 +59,7 @@ import com.example.medilightv2android.ui.components.ScanLogDialog
import com.example.medilightv2android.R import com.example.medilightv2android.R
import com.example.medilightv2android.ui.theme.* import com.example.medilightv2android.ui.theme.*
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import kotlin.math.roundToInt
@Composable @Composable
fun PiezoMonitoringView(appState: AppState) { fun PiezoMonitoringView(appState: AppState) {
@@ -532,8 +533,8 @@ fun PiezoMonitoringView(appState: AppState) {
} }
// Auto-measure: 측정 완료 대기 + 최소 간격 보장 // Auto-measure: 측정 완료 대기 + 최소 간격 보장
// FW VBTFW0116 + MTU 247 + CONN_PRIORITY HIGH 조합에서 응답 ~330ms. // 간격은 piezoSettings.autoScanIntervalMs (사용자 설정, 600~5000ms).
// BleManager throttle 600ms와 동기화해 사이클당 ~0.6~0.7s로 회전. // BleManager throttle(600ms)이 하한이라 600 미만은 무의미.
LaunchedEffect(isAutoMeasuring) { LaunchedEffect(isAutoMeasuring) {
if (isAutoMeasuring) { if (isAutoMeasuring) {
while (isAutoMeasuring) { while (isAutoMeasuring) {
@@ -545,7 +546,8 @@ fun PiezoMonitoringView(appState: AppState) {
} }
autoTickCount++ autoTickCount++
val elapsed = System.currentTimeMillis() - measureStartMs val elapsed = System.currentTimeMillis() - measureStartMs
val remaining = 600L - elapsed val targetIntervalMs = appState.piezoSettings.autoScanIntervalMs.toLong()
val remaining = targetIntervalMs - elapsed
if (remaining > 0) kotlinx.coroutines.delay(remaining) if (remaining > 0) kotlinx.coroutines.delay(remaining)
} }
kotlinx.coroutines.delay(50) kotlinx.coroutines.delay(50)
@@ -1377,6 +1379,25 @@ fun PiezoMonitoringView(appState: AppState) {
colors = SliderDefaults.colors(thumbColor = MlPrimary, activeTrackColor = MlPrimary) colors = SliderDefaults.colors(thumbColor = MlPrimary, activeTrackColor = MlPrimary)
) )
HorizontalDivider(modifier = Modifier.padding(vertical = dividerPadding))
// Auto Scan Interval — 측정 사이클 간격 (ms). 슬라이더 600~5000, 200 단위.
Row(verticalAlignment = Alignment.CenterVertically) {
Text("Auto Scan Interval", fontSize = labelSize, fontWeight = FontWeight.SemiBold)
Spacer(modifier = Modifier.weight(1f))
Text("${appState.piezoSettings.autoScanIntervalMs} ms", fontSize = labelSize, fontWeight = FontWeight.Bold, color = MlPrimary)
}
Slider(
value = appState.piezoSettings.autoScanIntervalMs.toFloat(),
onValueChange = {
val snapped = ((it / 200f).roundToInt() * 200).coerceIn(600, 5000)
appState.piezoSettings = appState.piezoSettings.copy(autoScanIntervalMs = snapped)
},
valueRange = 600f..5000f,
steps = 21,
colors = SliderDefaults.colors(thumbColor = MlPrimary, activeTrackColor = MlPrimary)
)
// ── 개발자 전용 설정 ── // ── 개발자 전용 설정 ──
if (appState.isDevMode) { if (appState.isDevMode) {
HorizontalDivider(modifier = Modifier.padding(vertical = dividerPadding)) HorizontalDivider(modifier = Modifier.padding(vertical = dividerPadding))
@@ -1583,7 +1604,7 @@ fun PiezoMonitoringView(appState: AppState) {
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
Button( Button(
onClick = { onClick = {
appState.savePiezoSettingsNow() appState.savePiezoSettingsNow() // Charles 함수명 통일
showSettings = false showSettings = false
}, },
shape = RoundedCornerShape(10.dp), shape = RoundedCornerShape(10.dp),