feat: 6ch 필터 + 도넛 확대 + Fill/Level 기준 변경 + 슬라이딩 윈도우
BV 계산: - 6채널 모두 수신(buffer>=20) 시에만 BV 계산 (불완전 데이터 제외) 도넛 차트: - 크기: compact 180→220dp, normal 240→280dp - 선 두께: 20→22, 28→30dp - 캐릭터: 70→80, 100→110dp - 볼륨 텍스트: 16→18sp, maxVolumeMl → lastVolumeMl 표시 Info Cards: - Fill: appState.fillPercentage → lastVolumeMl/maxVolume×100 (실측 기준) - Level: appState.bladderLevel → lastVolumeMl/maxVolume×MAX_LEVEL - 미측정 시 "—" 표시 Auto 측정: - 배치 윈도우 → 슬라이딩 윈도우 (최대 10개 유지, 오래된 것 제거) - 5개 이상이면 매 측정마다 trimmed mean 업데이트 - 이전: 10개 모이면 한 번에 업데이트 → clear - 변경: [1,2,3,4,5]→출력, [2,3,4,5,6]→출력 (실시간 반영) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+21
-15
@@ -151,7 +151,8 @@ fun PiezoMonitoringView(appState: AppState) {
|
|||||||
var totalWallCount = 0
|
var totalWallCount = 0
|
||||||
|
|
||||||
val validChannels = channels.filter { it.isValid }
|
val validChannels = channels.filter { it.isValid }
|
||||||
if (validChannels.isNotEmpty()) {
|
val allChannelsReceived = channels.filter { it.buffer.size >= 20 }.size >= 6
|
||||||
|
if (validChannels.isNotEmpty() && allChannelsReceived) {
|
||||||
val method = com.example.medilightv2android.managers.GreenZoneConstants.detectionMethod
|
val method = com.example.medilightv2android.managers.GreenZoneConstants.detectionMethod
|
||||||
val analyzerA = com.example.medilightv2android.managers.PiezoEchoAnalyzerA.shared
|
val analyzerA = com.example.medilightv2android.managers.PiezoEchoAnalyzerA.shared
|
||||||
|
|
||||||
@@ -287,18 +288,20 @@ fun PiezoMonitoringView(appState: AppState) {
|
|||||||
handler.post {
|
handler.post {
|
||||||
if (finalRawVol != null && finalRawVol > 0) {
|
if (finalRawVol != null && finalRawVol > 0) {
|
||||||
if (isAutoMeasuring) {
|
if (isAutoMeasuring) {
|
||||||
if (finalTotalWalls >= 4) {
|
if (allChannelsReceived) {
|
||||||
autoVolumeWindow.add(finalRawVol)
|
autoVolumeWindow.add(finalRawVol)
|
||||||
Log.d("PiezoMonitor", "AUTO window[${autoVolumeWindow.size}/$autoWindowSize] +${"%.1f".format(finalRawVol)}ml (valid=$finalTotalWalls)")
|
// 슬라이딩 윈도우: 오래된 값 제거
|
||||||
|
while (autoVolumeWindow.size > autoWindowSize) autoVolumeWindow.removeAt(0)
|
||||||
|
Log.d("PiezoMonitor", "AUTO window[${autoVolumeWindow.size}/$autoWindowSize] +${"%.1f".format(finalRawVol)}ml")
|
||||||
} else {
|
} else {
|
||||||
Log.d("PiezoMonitor", "AUTO skip ${"%.1f".format(finalRawVol)}ml (valid=$finalTotalWalls < 4)")
|
Log.d("PiezoMonitor", "AUTO skip ${"%.1f".format(finalRawVol)}ml (channels incomplete)")
|
||||||
}
|
}
|
||||||
if (autoVolumeWindow.size >= autoWindowSize) {
|
// 윈도우에 5개 이상이면 매번 trimmed mean 업데이트
|
||||||
|
if (autoVolumeWindow.size >= 5) {
|
||||||
val displayVol = trimmedMean(autoVolumeWindow.toList())
|
val displayVol = trimmedMean(autoVolumeWindow.toList())
|
||||||
lastVolumeMl = displayVol
|
lastVolumeMl = displayVol
|
||||||
maxVolumeMl = maxOf(maxVolumeMl, displayVol)
|
maxVolumeMl = maxOf(maxVolumeMl, displayVol)
|
||||||
Log.d("PiezoMonitor", "AUTO trimmedMean=${"%.1f".format(displayVol)}ml from ${autoVolumeWindow.map { "%.0f".format(it) }}")
|
Log.d("PiezoMonitor", "AUTO trimmedMean=${"%.1f".format(displayVol)}ml from ${autoVolumeWindow.map { "%.0f".format(it) }}")
|
||||||
autoVolumeWindow.clear()
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
lastVolumeMl = finalRawVol
|
lastVolumeMl = finalRawVol
|
||||||
@@ -753,20 +756,20 @@ fun PiezoMonitoringView(appState: AppState) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Donut chart with Bladdy
|
// Donut chart with Bladdy
|
||||||
val donutSize = if (isCompact) 180.dp else 240.dp
|
val donutSize = if (isCompact) 220.dp else 280.dp
|
||||||
Box(contentAlignment = Alignment.Center) {
|
Box(contentAlignment = Alignment.Center) {
|
||||||
DonutChartView(
|
DonutChartView(
|
||||||
progress = appState.bladderLevel.progress,
|
progress = appState.bladderLevel.progress,
|
||||||
urgency = urgency,
|
urgency = urgency,
|
||||||
lineWidth = if (isCompact) 20.dp else 28.dp,
|
lineWidth = if (isCompact) 22.dp else 30.dp,
|
||||||
size = donutSize
|
size = donutSize
|
||||||
)
|
)
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
BladdyRiveView(urgency = urgency, size = if (isCompact) 70.dp else 100.dp)
|
BladdyRiveView(urgency = urgency, size = if (isCompact) 80.dp else 110.dp)
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
Text(
|
Text(
|
||||||
if (maxVolumeMl > 0) "${"%.0f".format(maxVolumeMl)} ml" else "${appState.estimatedVolume} ml",
|
if (lastVolumeMl > 0) "${"%.0f".format(lastVolumeMl)} ml" else "${appState.estimatedVolume} ml",
|
||||||
fontSize = 16.sp,
|
fontSize = 18.sp,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
color = urgency.accentColor,
|
color = urgency.accentColor,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -778,12 +781,15 @@ fun PiezoMonitoringView(appState: AppState) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info cards
|
// Info cards (Fill/Level은 maxVolume 기준)
|
||||||
|
val maxVol = appState.piezoSettings.maxVolume.toDouble()
|
||||||
|
val fillPct = if (maxVol > 0 && lastVolumeMl > 0) (lastVolumeMl / maxVol * 100).coerceAtMost(100.0) else 0.0
|
||||||
|
val levelVal = if (maxVol > 0 && lastVolumeMl > 0) ((lastVolumeMl / maxVol) * BladderLevel.MAX_LEVEL).toInt().coerceIn(0, BladderLevel.MAX_LEVEL) else 0
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||||
val displayVol = if (lastVolumeMl > 0) "%.0f".format(lastVolumeMl) else "${appState.estimatedVolume}"
|
val displayVol = if (lastVolumeMl > 0) "%.0f".format(lastVolumeMl) else "—"
|
||||||
InfoCard("Volume", displayVol, "ml", urgency.accentColor, Modifier.weight(1f))
|
InfoCard("Volume", displayVol, "ml", urgency.accentColor, Modifier.weight(1f))
|
||||||
InfoCard("Fill", String.format("%.0f", appState.fillPercentage), "%", urgency.accentColor, Modifier.weight(1f))
|
InfoCard("Fill", if (fillPct > 0) "%.0f".format(fillPct) else "—", "%", urgency.accentColor, Modifier.weight(1f))
|
||||||
InfoCard("Level", "${appState.bladderLevel.currentLevel}/${BladderLevel.MAX_LEVEL}", "", urgency.accentColor, Modifier.weight(1f))
|
InfoCard("Level", if (levelVal > 0) "$levelVal/${BladderLevel.MAX_LEVEL}" else "—", "", urgency.accentColor, Modifier.weight(1f))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Control buttons: Void/Catheterize / Auto / Spot
|
// Control buttons: Void/Catheterize / Auto / Spot
|
||||||
|
|||||||
Reference in New Issue
Block a user