feat(dev): dev-mode RSSI live overlay · PiezoMonitoringView 도넛차트 위
BleManager: - val rssi: MutableStateOf<Int?> — 미연결 시 null - startRssiPolling() / stopRssiPolling() — 2초 주기 readRemoteRssi() - Service ready 3 경로에 startRssiPolling() wire - disconnect · gatt error · reconnect · forceDisconnect 5 경로에 stopRssiPolling() + rssi.value = null PiezoMonitoringView: - val liveRssi by bleManager.rssi - Donut 위에 dev-mode gate overlay (badge chip): · null → "-- dBm" gray · >= -60 dBm → green (strong) · >= -75 dBm → amber (ok) · < -75 dBm → red (weak) - 개발자모드 (3-tap Bladdy) 진입 시에만 표시 일반 사용자 UI 영향 없음 (isDevMode=false 기본).
This commit is contained in:
@@ -64,6 +64,8 @@ class BleManager private constructor(private val context: Context) {
|
||||
private val BATTERY_POLL_INTERVAL_MS = 30_000L // 정상 polling 간격
|
||||
private val BATTERY_RETRY_DELAY_MS = 3_000L // 첫 응답 재시도 지연
|
||||
private val BATTERY_RETRY_MAX = 2 // 최대 재시도 횟수
|
||||
private var rssiTimer: Runnable? = null
|
||||
private val RSSI_POLL_INTERVAL_MS = 2_000L // dev-mode UI polling 간격
|
||||
private var connectionTimer: Runnable? = null
|
||||
private var isUserDisconnect = false
|
||||
var lastConnectedAddress: String? = null
|
||||
@@ -112,6 +114,8 @@ class BleManager private constructor(private val context: Context) {
|
||||
}
|
||||
|
||||
val batteryLevel = mutableStateOf(0)
|
||||
/** 현재 연결된 디바이스 RSSI (dBm) · 미측정/미연결 시 null. dev-mode UI 오버레이용. */
|
||||
val rssi = mutableStateOf<Int?>(null)
|
||||
val bluetoothEnabled = mutableStateOf(bluetoothAdapter?.isEnabled == true)
|
||||
val isServiceReady = mutableStateOf(false)
|
||||
|
||||
@@ -364,6 +368,8 @@ class BleManager private constructor(private val context: Context) {
|
||||
cancelAutoReconnect()
|
||||
stopWatchdog()
|
||||
stopBatteryPolling()
|
||||
stopRssiPolling()
|
||||
rssi.value = null
|
||||
com.medithings.vesiscan.services.BleForegroundService.stop(context)
|
||||
connectionTimer?.let { handler.removeCallbacks(it) }
|
||||
connectionTimer = null
|
||||
@@ -733,6 +739,29 @@ class BleManager private constructor(private val context: Context) {
|
||||
batteryRetryTimer = null
|
||||
}
|
||||
|
||||
/**
|
||||
* RSSI polling — 2 초 주기로 `readRemoteRssi()` 요청 → `onReadRemoteRssi` 콜백이
|
||||
* `rssi.value` 업데이트. dev-mode UI 오버레이가 이를 관찰한다.
|
||||
* 실패해도 필수기능 영향 없음 (UC-05).
|
||||
*/
|
||||
fun startRssiPolling() {
|
||||
stopRssiPolling()
|
||||
val pollRunnable = object : Runnable {
|
||||
override fun run() {
|
||||
if (!isConnected.value) return
|
||||
try { bluetoothGatt?.readRemoteRssi() } catch (_: Exception) {}
|
||||
handler.postDelayed(this, RSSI_POLL_INTERVAL_MS)
|
||||
}
|
||||
}
|
||||
rssiTimer = pollRunnable
|
||||
handler.post(pollRunnable)
|
||||
}
|
||||
|
||||
fun stopRssiPolling() {
|
||||
rssiTimer?.let { handler.removeCallbacks(it) }
|
||||
rssiTimer = null
|
||||
}
|
||||
|
||||
// ── Watchdog (zombie connection detection, runs on separate thread) ──
|
||||
|
||||
private var watchdogJob: Thread? = null
|
||||
@@ -780,6 +809,8 @@ class BleManager private constructor(private val context: Context) {
|
||||
mtbTimeoutRunnable?.let { handler.removeCallbacks(it); mtbTimeoutRunnable = null }
|
||||
cccdRetryTimer?.let { handler.removeCallbacks(it); cccdRetryTimer = null }
|
||||
stopBatteryPolling()
|
||||
stopRssiPolling()
|
||||
rssi.value = null
|
||||
isConnected.value = false
|
||||
isServiceReady.value = false
|
||||
connectedDeviceName.value = ""
|
||||
@@ -800,6 +831,8 @@ class BleManager private constructor(private val context: Context) {
|
||||
|
||||
fun forceDisconnectAndReconnect() {
|
||||
stopBatteryPolling()
|
||||
stopRssiPolling()
|
||||
rssi.value = null
|
||||
stopWatchdog()
|
||||
isConnected.value = false
|
||||
isServiceReady.value = false
|
||||
@@ -937,6 +970,8 @@ class BleManager private constructor(private val context: Context) {
|
||||
debugLogger.error("GATT_ERROR status=$status newState=$newState wasConnected=$wasConnected")
|
||||
|
||||
stopBatteryPolling()
|
||||
stopRssiPolling()
|
||||
rssi.value = null
|
||||
stopWatchdog()
|
||||
// 2026-07-07 fix: 연결 실패 시 대기 중인 timer 취소.
|
||||
fwFallbackTimer?.let { handler.removeCallbacks(it); fwFallbackTimer = null }
|
||||
@@ -1011,6 +1046,8 @@ class BleManager private constructor(private val context: Context) {
|
||||
logd { "Disconnected from ${gatt.device.address}" }
|
||||
isConnecting.value = false
|
||||
stopBatteryPolling()
|
||||
stopRssiPolling()
|
||||
rssi.value = null
|
||||
// 2026-07-07 fix: disconnect 시 대기 중인 timer 취소.
|
||||
fwFallbackTimer?.let { handler.removeCallbacks(it); fwFallbackTimer = null }
|
||||
mtbTimeoutRunnable?.let { handler.removeCallbacks(it); mtbTimeoutRunnable = null }
|
||||
@@ -1085,6 +1122,7 @@ class BleManager private constructor(private val context: Context) {
|
||||
isServiceReady.value = true
|
||||
onConnectionStateChanged?.invoke(true)
|
||||
startBatteryPolling()
|
||||
startRssiPolling()
|
||||
startWatchdog()
|
||||
logd { "Service ready (no CCCD)" }
|
||||
}
|
||||
@@ -1096,6 +1134,7 @@ class BleManager private constructor(private val context: Context) {
|
||||
isServiceReady.value = true
|
||||
onConnectionStateChanged?.invoke(true)
|
||||
startBatteryPolling()
|
||||
startRssiPolling()
|
||||
startWatchdog()
|
||||
logd { "Service ready (no RX)" }
|
||||
}
|
||||
@@ -1124,6 +1163,7 @@ class BleManager private constructor(private val context: Context) {
|
||||
com.medithings.vesiscan.services.AdcCsvLogger.newSession()
|
||||
com.medithings.vesiscan.services.BleForegroundService.start(context)
|
||||
startBatteryPolling()
|
||||
startRssiPolling()
|
||||
startWatchdog()
|
||||
sendDeviceInfoQuery()
|
||||
// 4초 후에도 펌웨어 미수신이면 mfv? fallback (구 펌웨어는 mid? 미지원 가능성).
|
||||
@@ -1196,7 +1236,10 @@ class BleManager private constructor(private val context: Context) {
|
||||
|
||||
override fun onReadRemoteRssi(gatt: BluetoothGatt, rssi: Int, status: Int) {
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
handler.post { debugLogger.rssi(rssi) }
|
||||
handler.post {
|
||||
debugLogger.rssi(rssi)
|
||||
this@BleManager.rssi.value = rssi
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user