diff --git a/app/build.gradle b/app/build.gradle index bb587ea..1544087 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,7 +14,7 @@ android { minSdk 24 targetSdk 33 versionCode 5 - versionName "1.2.2" + versionName "1.2.3" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { diff --git a/app/src/main/java/llc/arma/ble/app/ui/mapper/BleMapper.kt b/app/src/main/java/llc/arma/ble/app/ui/mapper/BleMapper.kt index bd135b5..a39c106 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/mapper/BleMapper.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/mapper/BleMapper.kt @@ -38,6 +38,10 @@ class BleMapper @Inject constructor( state = BleView.BleState( tx = txMapper.map(input.state.tx) ), + accelerometerState = BleView.Accelerometer.AccelerometerState( + saveHistory = input.accelerometerState.saveHistory, + historyInterval = input.accelerometerState.historyInterval, + ) ) } } diff --git a/app/src/main/java/llc/arma/ble/app/ui/mapper/BleViewMapper.kt b/app/src/main/java/llc/arma/ble/app/ui/mapper/BleViewMapper.kt index 4f3229d..012e920 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/mapper/BleViewMapper.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/mapper/BleViewMapper.kt @@ -38,6 +38,10 @@ class BleViewMapper @Inject constructor( state = Ble.BleState( tx = txMapper.map(input.state.tx) ), + accelerometerState = Ble.Accelerometer.AccelerometerState( + saveHistory = input.accelerometerState.saveHistory, + historyInterval = input.accelerometerState.historyInterval, + ) ) } } diff --git a/app/src/main/java/llc/arma/ble/app/ui/model/BleView.kt b/app/src/main/java/llc/arma/ble/app/ui/model/BleView.kt index a77cc30..e033468 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/model/BleView.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/model/BleView.kt @@ -11,8 +11,21 @@ sealed class BleView( class Accelerometer( info: BleInfo, - val state: BleState - ) : BleView(info) + val state: BleState, + val accelerometerState: AccelerometerState + ) : BleView(info) { + + class AccelerometerState( + saveHistory: Boolean, + historyInterval: Long + ) { + + var saveHistory by mutableStateOf(saveHistory) + var historyInterval by mutableStateOf(historyInterval) + + } + + } class Beacon( info: BleInfo, diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/ble/BleListScreen.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/ble/BleListScreen.kt index 34e8ab4..efa91a7 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/ble/BleListScreen.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/ble/BleListScreen.kt @@ -260,16 +260,40 @@ private fun BleItem( ) { - ItemIcon { - Icon( - modifier = Modifier.align(Alignment.Center), - imageVector = when(ble.type){ - BleInfo.Type.BEACON -> Icons.Rounded.Nfc - BleInfo.Type.THERMOMETER -> Icons.Rounded.Thermostat - BleInfo.Type.ACCELEROMETER -> Icons.Rounded.Speed - }, - contentDescription = null - ) + Box { + + ItemIcon { + Icon( + modifier = Modifier.align(Alignment.Center), + imageVector = when (ble.type) { + BleInfo.Type.BEACON -> Icons.Rounded.Nfc + BleInfo.Type.THERMOMETER -> Icons.Rounded.Thermostat + BleInfo.Type.ACCELEROMETER -> Icons.Rounded.Speed + }, + contentDescription = null + ) + } + + if(ble.recordEnabled){ + + Surface( + shape = CircleShape, + color = color, + modifier = Modifier.align(Alignment.TopEnd) + ) { + + Surface( + shape = CircleShape, + color = MaterialTheme.colorScheme.error, + modifier = Modifier.size(12.dp).padding(2.dp) + ) { + + } + + } + + } + } Column { @@ -281,21 +305,6 @@ private fun BleItem( text = ble.serial ) - /*Text( - style = MaterialTheme.typography.bodyMedium, - text = String.format("%.3f", (10.0.pow((ble.tx.toDouble() - (ble.rssi?.toDouble() ?: 0.0) - 74) / 20))) - ) - - Text( - style = MaterialTheme.typography.bodyMedium, - text = String.format("%.3f", (ble.tx.toDouble() - (ble.rssi?.toDouble() ?: 0.0))) - ) - - Text( - style = MaterialTheme.typography.bodyMedium, - text = ble.tx.toString() + " tx" - )*/ - Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.alpha(0.7f) diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/connection/ConnectionContract.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/connection/ConnectionContract.kt index 314ffa9..0d066ba 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/connection/ConnectionContract.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/connection/ConnectionContract.kt @@ -4,6 +4,7 @@ import llc.arma.ble.app.ui.common.ViewEvent import llc.arma.ble.app.ui.common.ViewSideEffect import llc.arma.ble.app.ui.common.ViewState import llc.arma.ble.app.ui.model.BleView +import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract import llc.arma.ble.app.ui.screen.inspection.beacon.BeaconContract import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract import llc.arma.ble.domain.common.BleException @@ -26,6 +27,11 @@ class ConnectionContract { val event: ThermometerContract.Effect.Navigation ) : Event() + data class OnAccelNavigationEvent( + val event: AccelerometerContract.Effect.Navigation + ) : Event() + + } sealed class State : ViewState { diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/connection/ConnectionScreen.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/connection/ConnectionScreen.kt index 9b3fa1b..e17bf59 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/connection/ConnectionScreen.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/connection/ConnectionScreen.kt @@ -20,6 +20,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import llc.arma.ble.app.ui.model.BleView import llc.arma.ble.app.ui.screen.BleInfoView +import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerScreen import llc.arma.ble.app.ui.screen.inspection.beacon.BeaconScreen import llc.arma.ble.app.ui.screen.password.ChangePasswordContract @@ -117,7 +118,11 @@ fun ConnectionScreen( } is Ble.Accelerometer -> { - AccelerometerScreen(ble = state.ble) + AccelerometerScreen(ble = state.ble) { + viewModel.setEvent( + ConnectionContract.Event.OnAccelNavigationEvent(it) + ) + } } } diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/connection/ConnectionViewModel.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/connection/ConnectionViewModel.kt index f972246..00f76b3 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/connection/ConnectionViewModel.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/connection/ConnectionViewModel.kt @@ -10,6 +10,7 @@ import llc.arma.ble.app.ui.common.BaseViewModel import llc.arma.ble.app.ui.mapper.BleMapper import llc.arma.ble.app.ui.mapper.BleViewMapper import llc.arma.ble.app.ui.model.BleView +import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract import llc.arma.ble.app.ui.screen.inspection.beacon.BeaconContract import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract import llc.arma.ble.domain.model.Ble @@ -35,6 +36,7 @@ class ConnectionViewModel @Inject constructor( is ConnectionContract.Event.OnNavigateUp -> reduce(viewState.value, event) is ConnectionContract.Event.OnThermometerNavigationEvent -> reduce(viewState.value, event) is ConnectionContract.Event.RefreshBle -> reduce(viewState.value, event) + is ConnectionContract.Event.OnAccelNavigationEvent -> reduce(viewState.value, event) } } @@ -74,6 +76,19 @@ class ConnectionViewModel @Inject constructor( } } + private fun reduce( + state: ConnectionContract.State, + event: ConnectionContract.Event.OnAccelNavigationEvent + ) { + when(event.event){ + AccelerometerContract.Effect.Navigation.NavigateToChangePassword -> { + setEffect { + ConnectionContract.Effect.Navigation.NavigateToChangePassword(savedStateHandle.get("serial")!!) + } + } + } + } + private fun reduce( state: ConnectionContract.State, event: ConnectionContract.Event.OnNavigateUp diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/AccelerometerContract.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/AccelerometerContract.kt index 8679048..0c33eb7 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/AccelerometerContract.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/AccelerometerContract.kt @@ -4,6 +4,7 @@ import llc.arma.ble.app.ui.common.ViewEvent import llc.arma.ble.app.ui.common.ViewSideEffect import llc.arma.ble.app.ui.common.ViewState import llc.arma.ble.app.ui.model.BleView +import llc.arma.ble.app.ui.screen.inspection.beacon.BeaconContract import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.usecase.AccelScale import llc.arma.ble.domain.usecase.AccelViewMode @@ -15,12 +16,15 @@ class AccelerometerContract { sealed class Event : ViewEvent { - object OnShowAccelerometerMeasure : Event() - object OnHideAccelerometerMeasure : Event() + object OnShowAccelerometerAccel : Event() + object OnHideAccelerometerAccel : Event() object OnShowAccelerometerSpectre : Event() object OnHideAccelerometerSpectre : Event() + object OnShowAccelerometerHistory : Event() + object OnHideAccelerometerHistory : Event() + data class OnAccelViewModeEdit( val next: Next ) : Event(){ @@ -44,6 +48,9 @@ class AccelerometerContract { object OnHideWriteBlePreview : Event() + object OnChangePassword : Event() + object OnSaveIntervalEdit : Event() + data class OnBleChanged( val ble: Ble.Accelerometer, ): Event() @@ -68,6 +75,14 @@ class AccelerometerContract { val mode: FftViewMode ) : Event() + data class OnSaveHistoryChanged( + val save: Boolean + ) : Event() + + data class OnSaveIntervalChanged( + val interval: Long + ) : Event() + } sealed class State : ViewState { @@ -107,7 +122,8 @@ class AccelerometerContract { sealed class Effect : ViewSideEffect { - object ShowAccelerometerMeasure : Effect() + object ShowAccelerometerAccel : Effect() + object ShowAccelerometerSpectre : Effect() object ShowAccelerometerHistory : Effect() object ShowPowerPicker : Effect() @@ -125,6 +141,15 @@ class AccelerometerContract { object ShowFftAxisEdit : Effect() object ShowFftModeEdit : Effect() + object HideIntervalPicker : Effect() + object ShowIntervalPicker : Effect() + + sealed class Navigation : Effect() { + + object NavigateToChangePassword : Navigation() + + } + } } \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/AccelerometerScreen.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/AccelerometerScreen.kt index f962fc8..e58527b 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/AccelerometerScreen.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/AccelerometerScreen.kt @@ -1,6 +1,5 @@ package llc.arma.ble.app.ui.screen.inspection.accelerometer -import android.util.Log import androidx.compose.foundation.layout.Column import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ModalBottomSheetValue @@ -23,22 +22,25 @@ import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.AccelFftModeEdit import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.AccelFrequencyEdit import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.AccelSpectreEdit import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.AccelViewEdit +import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.AccelerometerSpectre +import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.AccelerometerAccel import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.AccelerometerHistory -import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.AccelerometerMeasure import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.DisplayState +import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.IntervalEdit import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.LoadingState import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.PowerEdit import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.Write import llc.arma.ble.domain.model.Ble enum class SheetPage { - MEASURE, POWER, WRITE, HISTORY, ACCEL_MODE_EDIT, SPECTRE_MODE_EDIT, SPECTRE_EDIT, FREQUENCY_EDIT, AXIS_EDIT, FFT_MODE_EDIT + HISTORY, ACCEL, POWER, WRITE, SPECTRE, ACCEL_MODE_EDIT, SPECTRE_MODE_EDIT, SPECTRE_EDIT, FREQUENCY_EDIT, AXIS_EDIT, FFT_MODE_EDIT, INTERVAL_EDIT } @OptIn(ExperimentalMaterialApi::class) @Composable fun AccelerometerScreen( ble: Ble.Accelerometer, + onEvent: (AccelerometerContract.Effect.Navigation) -> Unit ) { val viewModel = hiltViewModel() @@ -57,9 +59,7 @@ fun AccelerometerScreen( LaunchedEffect( key1 = bottomDialog.sheetState?.currentValue, block = { - Log.d("sheet", bottomDialog.sheetState?.currentValue.toString()) if(bottomDialog.sheetState?.currentValue == ModalBottomSheetValue.Hidden) { - Log.d("sheet", "dispose") bottomDialog.setContent({}) sheetPage = null } @@ -70,12 +70,12 @@ fun AccelerometerScreen( LaunchedEffect(sheetPage) { when (sheetPage) { - SheetPage.MEASURE -> launch { + SheetPage.HISTORY -> launch { val currentState = viewModel.viewState.value if (currentState is AccelerometerContract.State.Display) { bottomDialog.show { - AccelerometerMeasure( + AccelerometerHistory( ble = currentState.accelerometer.info, accelMode = currentState.accelViewMode, fftAxis = currentState.fftAxis, @@ -86,12 +86,28 @@ fun AccelerometerScreen( } } } - SheetPage.HISTORY -> launch { + SheetPage.ACCEL -> launch { val currentState = viewModel.viewState.value if (currentState is AccelerometerContract.State.Display) { bottomDialog.show { - AccelerometerHistory( + AccelerometerAccel( + ble = currentState.accelerometer.info, + accelMode = currentState.accelViewMode, + fftAxis = currentState.fftAxis, + fftMode = currentState.fftViewMode, + frequency = currentState.fftFrequency, + accelScale = currentState.accelScale + ) + } + } + } + SheetPage.SPECTRE -> launch { + val currentState = viewModel.viewState.value + + if (currentState is AccelerometerContract.State.Display) { + bottomDialog.show { + AccelerometerSpectre( ble = currentState.accelerometer.info, accelMode = currentState.accelViewMode, fftAxis = currentState.fftAxis, @@ -221,9 +237,27 @@ fun AccelerometerScreen( } } + + SheetPage.INTERVAL_EDIT -> bottomDialog.show { + + val currentState = viewModel.viewState.value + + if(currentState is AccelerometerContract.State.Display) { + IntervalEdit( + state = currentState.accelerometer, + onEvent = { + viewModel.setEvent(it) + } + ) + } + + } + null -> { bottomDialog.hide() } + + } } @@ -238,10 +272,10 @@ fun AccelerometerScreen( LaunchedEffect("effect"){ viewModel.effect.onEach { when(it){ - is AccelerometerContract.Effect.ShowAccelerometerMeasure -> launch { + is AccelerometerContract.Effect.ShowAccelerometerAccel -> launch { sheetPage = null delay(100) - sheetPage = SheetPage.MEASURE + sheetPage = SheetPage.ACCEL } is AccelerometerContract.Effect.HidePowerPicker -> launch { sheetPage = null @@ -261,10 +295,10 @@ fun AccelerometerScreen( delay(100) sheetPage = SheetPage.WRITE } - is AccelerometerContract.Effect.ShowAccelerometerHistory -> launch { + is AccelerometerContract.Effect.ShowAccelerometerSpectre -> launch { sheetPage = null delay(100) - sheetPage = SheetPage.HISTORY + sheetPage = SheetPage.SPECTRE } is AccelerometerContract.Effect.ShowAccelEdit -> launch { sheetPage = null @@ -294,6 +328,25 @@ fun AccelerometerScreen( delay(100) sheetPage = SheetPage.FFT_MODE_EDIT } + + AccelerometerContract.Effect.ShowAccelerometerHistory -> launch { + sheetPage = null + delay(100) + sheetPage = SheetPage.HISTORY + } + + AccelerometerContract.Effect.HideIntervalPicker -> { + sheetPage = null + delay(100) + } + AccelerometerContract.Effect.ShowIntervalPicker -> { + sheetPage = null + delay(100) + sheetPage = SheetPage.INTERVAL_EDIT + } + is AccelerometerContract.Effect.Navigation -> { + onEvent(it) + } } }.launchIn(this) } diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/AccelerometerViewModel.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/AccelerometerViewModel.kt index 6104aeb..fcba183 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/AccelerometerViewModel.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/AccelerometerViewModel.kt @@ -1,14 +1,14 @@ package llc.arma.ble.app.ui.screen.inspection.accelerometer -import android.util.Log import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.delay import kotlinx.coroutines.launch import llc.arma.ble.app.ui.common.BaseViewModel import llc.arma.ble.app.ui.mapper.BleMapper import llc.arma.ble.app.ui.mapper.BleViewMapper import llc.arma.ble.app.ui.model.BleView +import llc.arma.ble.app.ui.screen.inspection.beacon.BeaconContract +import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.usecase.AccelScale import llc.arma.ble.domain.usecase.AccelViewMode @@ -30,8 +30,8 @@ class AccelerometerViewModel @Inject constructor( override fun handleEvents(event: AccelerometerContract.Event) { when(event){ is AccelerometerContract.Event.OnBleChanged -> reduce(viewState.value, event) - is AccelerometerContract.Event.OnHideAccelerometerMeasure -> reduce(viewState.value, event) - is AccelerometerContract.Event.OnShowAccelerometerMeasure -> reduce(viewState.value, event) + is AccelerometerContract.Event.OnHideAccelerometerAccel -> reduce(viewState.value, event) + is AccelerometerContract.Event.OnShowAccelerometerAccel -> reduce(viewState.value, event) is AccelerometerContract.Event.OnPowerChanged -> reduce(viewState.value, event) is AccelerometerContract.Event.OnPowerEdit -> reduce(viewState.value, event) is AccelerometerContract.Event.OnShowWriteBlePreview -> reduce(viewState.value, event) @@ -48,9 +48,62 @@ class AccelerometerViewModel @Inject constructor( is AccelerometerContract.Event.OnFftModeChanged -> reduce(viewState.value, event) is AccelerometerContract.Event.OnFftAxisEdit -> reduce(viewState.value, event) is AccelerometerContract.Event.OnFftModeEdit -> reduce(viewState.value, event) + is AccelerometerContract.Event.OnSaveHistoryChanged -> reduce(viewState.value, event) + is AccelerometerContract.Event.OnHideAccelerometerHistory -> reduce(viewState.value, event) + is AccelerometerContract.Event.OnShowAccelerometerHistory -> reduce(viewState.value, event) + is AccelerometerContract.Event.OnChangePassword -> reduce(viewState.value, event) + is AccelerometerContract.Event.OnSaveIntervalChanged -> reduce(viewState.value, event) + is AccelerometerContract.Event.OnSaveIntervalEdit -> reduce(viewState.value, event) } } + private fun reduce( + state: AccelerometerContract.State, + event: AccelerometerContract.Event.OnSaveIntervalEdit + ) { + setEffect { + AccelerometerContract.Effect.ShowIntervalPicker + } + } + + private fun reduce( + state: AccelerometerContract.State, + event: AccelerometerContract.Event.OnSaveIntervalChanged + ) { + if(state is AccelerometerContract.State.Display) { + + state.accelerometer.accelerometerState.historyInterval = event.interval + + } + + setEffect { + AccelerometerContract.Effect.HideIntervalPicker + } + + } + + private fun reduce( + state: AccelerometerContract.State, + event: AccelerometerContract.Event.OnChangePassword + ) { + setEffect { + AccelerometerContract.Effect.Navigation.NavigateToChangePassword + } + } + + private fun reduce( + state: AccelerometerContract.State, + event: AccelerometerContract.Event.OnSaveHistoryChanged + ) { + + if(state is AccelerometerContract.State.Display) { + + state.accelerometer.accelerometerState.saveHistory = event.save + + } + + } + private fun reduce( state: AccelerometerContract.State, event: AccelerometerContract.Event.OnFftModeEdit @@ -152,7 +205,7 @@ class AccelerometerViewModel @Inject constructor( ) { setEffect { - AccelerometerContract.Effect.ShowAccelerometerHistory + AccelerometerContract.Effect.ShowAccelerometerSpectre } } @@ -164,6 +217,26 @@ class AccelerometerViewModel @Inject constructor( + } + + private fun reduce( + state: AccelerometerContract.State, + event: AccelerometerContract.Event.OnShowAccelerometerHistory + ) { + + setEffect { + AccelerometerContract.Effect.ShowAccelerometerHistory + } + + } + + private fun reduce( + state: AccelerometerContract.State, + event: AccelerometerContract.Event.OnHideAccelerometerHistory + ) { + + + } private fun reduce( @@ -197,8 +270,10 @@ class AccelerometerViewModel @Inject constructor( val newBle = bleViewMapper.map(state.accelerometer) as Ble.Accelerometer val writeRequest = Ble.Accelerometer.WriteRequest( - tx = if(newBle.state.tx == state.origin.state.tx) null else newBle.state.tx - ) + tx = if(newBle.state.tx == state.origin.state.tx) null else newBle.state.tx, + saveHistory = if(newBle.accelerometerState.saveHistory == state.origin.accelerometerState.saveHistory) null else newBle.accelerometerState.saveHistory, + historyInterval = if(newBle.accelerometerState.historyInterval == state.origin.accelerometerState.historyInterval) null else newBle.accelerometerState.historyInterval, + ) setState { state.copy( @@ -274,7 +349,7 @@ class AccelerometerViewModel @Inject constructor( private fun reduce( state: AccelerometerContract.State, - event: AccelerometerContract.Event.OnHideAccelerometerMeasure + event: AccelerometerContract.Event.OnHideAccelerometerAccel ) { @@ -283,13 +358,13 @@ class AccelerometerViewModel @Inject constructor( private fun reduce( state: AccelerometerContract.State, - event: AccelerometerContract.Event.OnShowAccelerometerMeasure + event: AccelerometerContract.Event.OnShowAccelerometerAccel ) { viewModelScope.launch { setEffect { - AccelerometerContract.Effect.ShowAccelerometerMeasure + AccelerometerContract.Effect.ShowAccelerometerAccel } } @@ -308,7 +383,8 @@ class AccelerometerViewModel @Inject constructor( state.copy( origin = Ble.Accelerometer( info = event.ble.info, - state = event.ble.state + state = event.ble.state, + accelerometerState = state.origin.accelerometerState ) ) } @@ -361,6 +437,10 @@ class AccelerometerViewModel @Inject constructor( info = currentState.origin.info, state = currentState.origin.state.copy( tx = request.writeRequest.tx ?: state.origin.state.tx + ), + accelerometerState = currentState.origin.accelerometerState.copy( + saveHistory = request.writeRequest.saveHistory + ?: currentState.origin.accelerometerState.saveHistory ) ) diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelViewEdit.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelViewEdit.kt index 243acbd..d437536 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelViewEdit.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelViewEdit.kt @@ -13,9 +13,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp -import llc.arma.ble.app.ui.model.BleView import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract -import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract import llc.arma.ble.domain.usecase.AccelViewMode import llc.arma.ble.domain.usecase.AccelViewMode.* @@ -90,7 +88,7 @@ fun AccelViewEdit( onEvent(AccelerometerContract.Event.OnAccelViewModelChanged(value)) when(next){ AccelerometerContract.Event.OnAccelViewModeEdit.Next.ACCEL -> { - onEvent(AccelerometerContract.Event.OnShowAccelerometerMeasure) + onEvent(AccelerometerContract.Event.OnShowAccelerometerAccel) } AccelerometerContract.Event.OnAccelViewModeEdit.Next.SPECTRE -> { onEvent(AccelerometerContract.Event.OnSpectreEdit) diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelerometerSpectre.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelerometerSpectre.kt new file mode 100644 index 0000000..16e13ea --- /dev/null +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelerometerSpectre.kt @@ -0,0 +1,499 @@ +package llc.arma.ble.app.ui.screen.inspection.accelerometer.view + +import android.util.Log +import androidx.compose.animation.core.FastOutSlowInEasing +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.viewModelScope +import com.patrykandpatrick.vico.compose.axis.horizontal.bottomAxis +import com.patrykandpatrick.vico.compose.axis.vertical.startAxis +import com.patrykandpatrick.vico.compose.chart.Chart +import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import llc.arma.ble.app.ui.common.BaseViewModel +import llc.arma.ble.app.ui.common.ViewEvent +import llc.arma.ble.app.ui.common.ViewSideEffect +import llc.arma.ble.app.ui.common.ViewState +import llc.arma.ble.domain.model.BleInfo +import javax.inject.Inject +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Refresh +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.text.style.TextAlign +import com.patrykandpatrick.vico.compose.chart.column.columnChart +import com.patrykandpatrick.vico.compose.chart.scroll.rememberChartScrollState +import com.patrykandpatrick.vico.core.axis.AxisPosition +import com.patrykandpatrick.vico.core.axis.formatter.AxisValueFormatter +import com.patrykandpatrick.vico.core.entry.ChartEntry +import com.patrykandpatrick.vico.core.entry.FloatEntry +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import llc.arma.ble.domain.common.ProgressState +import llc.arma.ble.domain.model.Ble +import llc.arma.ble.domain.usecase.AccelScale +import llc.arma.ble.domain.usecase.AccelViewMode +import llc.arma.ble.domain.usecase.FftAxis +import llc.arma.ble.domain.usecase.FftFrequency +import llc.arma.ble.domain.usecase.FftViewMode +import llc.arma.ble.domain.usecase.GetAccelerometerSpectreBySerial + +class AccelerometerEntry( + val frequency: Long, + override val x: Float, + override val y: Float, +) : ChartEntry { + + override fun withY(y: Float) = AccelerometerEntry(frequency, x, y) + +} + +@Composable +fun AccelerometerSpectre( + ble: BleInfo, + accelMode: AccelViewMode, + fftAxis: FftAxis, + fftMode: FftViewMode, + frequency: FftFrequency, + accelScale: AccelScale +) { + + val viewModel = hiltViewModel() + val state = viewModel.viewState.value + + LaunchedEffect(ble.serial, accelMode, fftAxis, fftMode, frequency) { + viewModel.setEvent(AccelerometerSpectreContract.Event.OnStart(ble.serial, accelMode, fftAxis, fftMode, frequency, accelScale)) + } + + DisposableEffect(key1 = "ble", effect = { + + onDispose { + Log.d("history", "dispose") + viewModel.setEvent(AccelerometerSpectreContract.Event.StopMeasure) + } + + }) + + Column( + modifier = Modifier.fillMaxHeight(0.9f) + ) { + + Row( + modifier = Modifier.padding(horizontal = 12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + + val title = when(state){ + is AccelerometerSpectreContract.State.Display -> { + if (state.previousHistory !== null) { + "${fftMode.localized} (${state.previousHistory.size})" + }else { + fftMode.localized + } + } + AccelerometerSpectreContract.State.Exception -> fftMode.localized + } + + Row( + modifier = Modifier.weight(1f), + verticalAlignment = Alignment.CenterVertically + ) { + + Text( + modifier = Modifier.padding(end = 16.dp), + text = title, + style = MaterialTheme.typography.titleLarge + ) + + if(state is AccelerometerSpectreContract.State.Display) { + + when (state.loadingHistoryState) { + is ProgressState.Indeterminate -> { + + CircularProgressIndicator( + modifier = Modifier.size(16.dp), + strokeWidth = 2.dp, + strokeCap = StrokeCap.Round, + ) + + } + is ProgressState.Progress -> { + + val progressAnimDuration = 1500 + val progressAnimation by animateFloatAsState( + targetValue = state.loadingHistoryState.value, + animationSpec = tween( + durationMillis = progressAnimDuration, + easing = FastOutSlowInEasing + ) + ) + + CircularProgressIndicator( + modifier = Modifier.size(16.dp), + strokeWidth = 2.dp, + strokeCap = StrokeCap.Round, + progress = progressAnimation, + ) + + } + else -> {} + } + + } + + } + + IconButton( + onClick = { + viewModel.setEvent(AccelerometerSpectreContract.Event.OnStart(ble.serial, accelMode, fftAxis, fftMode, frequency, accelScale)) + }, + enabled = when(state){ + is AccelerometerSpectreContract.State.Display -> state.loadingHistoryState is ProgressState.Finished + AccelerometerSpectreContract.State.Exception -> true + } + ) { + Icon( + imageVector = Icons.Rounded.Refresh, + contentDescription = null + ) + } + + } + + Spacer(modifier = Modifier.height(16.dp)) + + Box(modifier = Modifier) { + + when (state) { + is AccelerometerSpectreContract.State.Display -> Display(state = state) + AccelerometerSpectreContract.State.Exception -> Exception() + } + + } + + } + +} + +val axisValueFormatter = AxisValueFormatter { value, chartValues -> + (chartValues.chartEntryModel.entries.firstOrNull() + ?.getOrNull(value.toInt()) as? AccelerometerEntry) + ?.frequency?.let { String.format("%.1f", (it.toFloat() / 256f)) } + .orEmpty() +} + + +@Composable +fun Display( + state: AccelerometerSpectreContract.State.Display +) { + + Box(modifier = Modifier + .padding(8.dp) + .fillMaxSize() + ) { + + val data = if(state.loadingHistoryState is ProgressState.Finished){ + state.loadingHistoryState.data + } else { + state.previousHistory + } + + val producer = remember { + ChartEntryModelProducer(listOf()) + } + + if(data != null){ + + if(data.isEmpty()){ + + Text( + modifier = Modifier.align(Alignment.Center), + text = "Нет данных" + ) + + } else { + + LaunchedEffect(data){ + producer.setEntries( + data.mapIndexed { index, measurePoint -> + AccelerometerEntry(measurePoint.frequency, index.toFloat(), measurePoint.value) + } + ) + } + + val lineChart = columnChart( + spacing = 1.5.dp + ) + + val scrollState = rememberChartScrollState() + + Chart( + diffAnimationSpec = tween(0), + isZoomEnabled = true, + chartScrollState = scrollState, + chart = lineChart, + chartModelProducer = producer, + startAxis = startAxis(), + bottomAxis = bottomAxis( + tickLength = 0.dp, + valueFormatter = axisValueFormatter, + labelRotationDegrees = -90f, + ), + modifier = Modifier.fillMaxSize() + ) + + + } + + } else { + when (state.loadingHistoryState) { + is ProgressState.Indeterminate -> { + + CircularProgressIndicator( + strokeCap = StrokeCap.Round, + modifier = Modifier.align(Alignment.Center) + ) + + } + is ProgressState.Progress -> { + + val progressAnimDuration = 1500 + val progressAnimation by animateFloatAsState( + targetValue = state.loadingHistoryState.value, + animationSpec = tween( + durationMillis = progressAnimDuration, + easing = FastOutSlowInEasing + ) + ) + + CircularProgressIndicator( + strokeCap = StrokeCap.Round, + progress = progressAnimation, + modifier = Modifier.align(Alignment.Center) + ) + + } + else -> {} + } + } + + } +} + +@Composable +private fun Exception( + +) { + Box( + modifier = Modifier + .padding(8.dp) + .fillMaxWidth() + .aspectRatio(2f), + ){ + + Text( + textAlign = TextAlign.Center, + text = "Во время загрузки произошла ошибка", + modifier = Modifier.align(Alignment.Center) + ) + + } + +} + +class AccelerometerSpectreContract { + + sealed class Event : ViewEvent { + + object StopMeasure : Event() + + data class OnStart( + val serial: String, + val accelMode: AccelViewMode, + val fftAxis: FftAxis, + val fftMode: FftViewMode, + val frequency: FftFrequency, + val accelScale: AccelScale + ) : Event() + + data class OnRefreshHistory( + val serial: String, + val accelMode: AccelViewMode, + val fftAxis: FftAxis, + val fftMode: FftViewMode, + val frequency: FftFrequency, + val accelScale: AccelScale + ) : Event() + + } + + sealed class State : ViewState { + + data class Display( + val previousHistory : List?, + val loadingHistoryState : ProgressState> + ) : State() + + object Exception : State() + + } + + sealed class Effect : ViewSideEffect { + + } + +} + +@HiltViewModel +class AccelerometerSpectreViewModel @Inject constructor( + private val getAccelerometerSpectreBySerial: GetAccelerometerSpectreBySerial +) : BaseViewModel() { + + private var job: Job? = null + private var lastSerial: String? = null + + override fun setInitialState() = AccelerometerSpectreContract.State.Display( + loadingHistoryState = ProgressState.Indeterminate, + previousHistory = null + ) + + override fun handleEvents(event: AccelerometerSpectreContract.Event) { + when(event){ + is AccelerometerSpectreContract.Event.OnStart -> reduce(viewState.value, event) + is AccelerometerSpectreContract.Event.OnRefreshHistory -> reduce(viewState.value, event) + is AccelerometerSpectreContract.Event.StopMeasure -> reduce(viewState.value, event) + } + } + + private fun reduce( + state: AccelerometerSpectreContract.State, + event: AccelerometerSpectreContract.Event.OnStart + ) { + + viewModelScope.launch { + + if(state is AccelerometerSpectreContract.State.Display) { + + //if(lastSerial != event.serial) { + + lastSerial = event.serial + + setState { + AccelerometerSpectreContract.State.Display( + loadingHistoryState = ProgressState.Indeterminate, + previousHistory = when(state.loadingHistoryState){ + is ProgressState.Finished -> state.loadingHistoryState.data + is ProgressState.Indeterminate -> null + is ProgressState.Progress -> null + } + ) + } + + } else { + setState { + AccelerometerSpectreContract.State.Display( + loadingHistoryState = ProgressState.Indeterminate, + previousHistory = null + ) + } + + } + + job?.cancel() + job = getAccelerometerSpectreBySerial( + serial = event.serial, + accelMode = event.accelMode, + fftAxis = event.fftAxis, + fftMode = event.fftMode, + frequency = event.frequency, + accelScale = event.accelScale + ).onEach { + + val currentState = viewState.value + + if(currentState is AccelerometerSpectreContract.State.Display) { + + it.fold( + onSuccess = { + setState { + + AccelerometerSpectreContract.State.Display( + loadingHistoryState = it, + previousHistory = when (it) { + is ProgressState.Finished -> { + it.data + } + is ProgressState.Indeterminate -> currentState.previousHistory + is ProgressState.Progress -> currentState.previousHistory + } + ) + + } + }, + onFailure = { + setState { + AccelerometerSpectreContract.State.Exception + } + } + ) + + } + + }.launchIn(this) + + } + + } + + private fun reduce( + state: AccelerometerSpectreContract.State, + event: AccelerometerSpectreContract.Event.OnRefreshHistory + ) { + /*viewModelScope.launch { + + setState { + AccelerometerHistoryContract.State.Display(ProgressState.Indeterminate) + } + + getAccelerometerSpectreBySerial( + serial = event.serial, + accelMode = event.accelMode, + fftAxis = event.fftAxis, + fftMode = event.fftMode, + frequency = event.frequency, + accelScale = event.accelScale + ).onEach { + it.fold( + onSuccess = { + setState { + AccelerometerHistoryContract.State.Display(it) + } + }, + onFailure = { + setState { + AccelerometerHistoryContract.State.Exception + } + } + ) + }.launchIn(this) + + }*/ + } + + private fun reduce( + state: AccelerometerSpectreContract.State, + event: AccelerometerSpectreContract.Event.StopMeasure + ) { + job?.cancel() + } + +} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterMeasure.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterAccel.kt similarity index 81% rename from app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterMeasure.kt rename to app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterAccel.kt index 5eaa648..76eb781 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterMeasure.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterAccel.kt @@ -1,6 +1,5 @@ package llc.arma.ble.app.ui.screen.inspection.accelerometer.view -import android.util.Log import androidx.compose.animation.core.tween import androidx.compose.foundation.layout.* import androidx.compose.material3.* @@ -45,7 +44,7 @@ import llc.arma.ble.domain.usecase.FftViewMode import llc.arma.ble.domain.usecase.GetAccelerometerMeasureBySerialFlow @Composable -fun AccelerometerMeasure( +fun AccelerometerAccel( ble: BleInfo, accelScale: AccelScale, accelMode: AccelViewMode, @@ -54,20 +53,15 @@ fun AccelerometerMeasure( frequency: FftFrequency ) { - val viewModel = hiltViewModel() + val viewModel = hiltViewModel() val state = viewModel.viewState.value - /*LaunchedEffect(ble.serial) { - viewModel.setEvent(AccelerometerMeasureContract.Event.OnStart(ble.serial)) - }*/ - - viewModel.setEvent(AccelerometerMeasureContract.Event.OnStart(ble.serial, accelScale, accelMode, fftAxis, fftMode, frequency)) + viewModel.setEvent(AccelerometerAccelContract.Event.OnStart(ble.serial, accelScale, accelMode, fftAxis, fftMode, frequency)) DisposableEffect(key1 = "ble", effect = { onDispose { - Log.d("measure", "dispose") - viewModel.setEvent(AccelerometerMeasureContract.Event.StopMeasure) + viewModel.setEvent(AccelerometerAccelContract.Event.StopMeasure) } }) @@ -89,7 +83,7 @@ fun AccelerometerMeasure( IconButton( onClick = { - viewModel.setEvent(AccelerometerMeasureContract.Event.OnRefreshHistory(ble.serial, accelScale, accelMode, fftAxis, fftMode, frequency)) + viewModel.setEvent(AccelerometerAccelContract.Event.OnRefreshHistory(ble.serial, accelScale, accelMode, fftAxis, fftMode, frequency)) }, enabled = true ) { @@ -106,8 +100,8 @@ fun AccelerometerMeasure( Box(modifier = Modifier) { when (state) { - is AccelerometerMeasureContract.State.Display -> Display(state = state) - AccelerometerMeasureContract.State.Exception -> Exception() + is AccelerometerAccelContract.State.Display -> Display(state = state) + is AccelerometerAccelContract.State.Exception -> Exception() } } @@ -119,7 +113,7 @@ fun AccelerometerMeasure( @Composable fun Display( - state: AccelerometerMeasureContract.State.Display + state: AccelerometerAccelContract.State.Display ) { Box(modifier = Modifier @@ -255,7 +249,7 @@ private fun Exception( } -class AccelerometerMeasureContract { +class AccelerometerAccelContract { sealed class Event : ViewEvent { @@ -300,29 +294,29 @@ class AccelerometerMeasureContract { @HiltViewModel -class AccelerometerMeasureViewModel @Inject constructor( +class AccelerometerAccelViewModel @Inject constructor( private val getAccelerometerMeasureBySerialFlow: GetAccelerometerMeasureBySerialFlow, -) : BaseViewModel() { +) : BaseViewModel() { var measureJob: Job? = null private var lastSerial: String? = null - override fun setInitialState() = AccelerometerMeasureContract.State.Display( + override fun setInitialState() = AccelerometerAccelContract.State.Display( emptyList() ) - override fun handleEvents(event: AccelerometerMeasureContract.Event) { + override fun handleEvents(event: AccelerometerAccelContract.Event) { when(event){ - is AccelerometerMeasureContract.Event.OnStart -> reduce(viewState.value, event) - is AccelerometerMeasureContract.Event.OnRefreshHistory -> reduce(viewState.value, event) - is AccelerometerMeasureContract.Event.StopMeasure -> reduce(viewState.value, event) + is AccelerometerAccelContract.Event.OnStart -> reduce(viewState.value, event) + is AccelerometerAccelContract.Event.OnRefreshHistory -> reduce(viewState.value, event) + is AccelerometerAccelContract.Event.StopMeasure -> reduce(viewState.value, event) } } private fun reduce( - state: AccelerometerMeasureContract.State, - event: AccelerometerMeasureContract.Event.StopMeasure + state: AccelerometerAccelContract.State, + event: AccelerometerAccelContract.Event.StopMeasure ) { @@ -330,14 +324,14 @@ class AccelerometerMeasureViewModel @Inject constructor( measureJob = null setState { - AccelerometerMeasureContract.State.Display(emptyList()) + AccelerometerAccelContract.State.Display(emptyList()) } } private fun reduce( - state: AccelerometerMeasureContract.State, - event: AccelerometerMeasureContract.Event.OnStart + state: AccelerometerAccelContract.State, + event: AccelerometerAccelContract.Event.OnStart ) { startReadMeasure(event.serial, event.accelScale, event.accelMode, event.fftAxis, event.fftMode, event.frequency, false) @@ -345,8 +339,8 @@ class AccelerometerMeasureViewModel @Inject constructor( } private fun reduce( - state: AccelerometerMeasureContract.State, - event: AccelerometerMeasureContract.Event.OnRefreshHistory + state: AccelerometerAccelContract.State, + event: AccelerometerAccelContract.Event.OnRefreshHistory ) { startReadMeasure(event.serial, event.accelScale, event.accelMode, event.fftAxis, event.fftMode, event.frequency, true) } @@ -367,7 +361,7 @@ class AccelerometerMeasureViewModel @Inject constructor( measureJob = viewModelScope.launch { setState { - AccelerometerMeasureContract.State.Display(emptyList()) + AccelerometerAccelContract.State.Display(emptyList()) } getAccelerometerMeasureBySerialFlow(serial, accelScale, accelMode, fftAxis, fftMode, frequency).onEach { @@ -375,7 +369,7 @@ class AccelerometerMeasureViewModel @Inject constructor( onSuccess = { setState { when (this) { - is AccelerometerMeasureContract.State.Display -> { + is AccelerometerAccelContract.State.Display -> { val dataList = this.measureHistory.toMutableList().apply { add( Accelerate( @@ -385,18 +379,18 @@ class AccelerometerMeasureViewModel @Inject constructor( ) ) }.takeLast(10) - AccelerometerMeasureContract.State.Display(dataList) + AccelerometerAccelContract.State.Display(dataList) } - AccelerometerMeasureContract.State.Exception -> { - AccelerometerMeasureContract.State.Display(listOf(it)) + AccelerometerAccelContract.State.Exception -> { + AccelerometerAccelContract.State.Display(listOf(it)) } } } }, onFailure = { setState { - AccelerometerMeasureContract.State.Exception + AccelerometerAccelContract.State.Exception } } ) diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterHistory.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterHistory.kt index 472e21c..ded9c76 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterHistory.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterHistory.kt @@ -1,9 +1,9 @@ package llc.arma.ble.app.ui.screen.inspection.accelerometer.view -import android.util.Log import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween +import androidx.compose.foundation.gestures.scrollBy import androidx.compose.foundation.layout.* import androidx.compose.material3.* import androidx.compose.runtime.* @@ -12,7 +12,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.viewModelScope -import com.patrykandpatrick.vico.compose.axis.horizontal.bottomAxis import com.patrykandpatrick.vico.compose.axis.vertical.startAxis import com.patrykandpatrick.vico.compose.chart.Chart import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer @@ -28,60 +27,68 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Refresh import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.text.style.TextAlign -import com.patrykandpatrick.vico.compose.chart.column.columnChart +import com.patrykandpatrick.vico.compose.axis.horizontal.bottomAxis +import com.patrykandpatrick.vico.compose.chart.line.lineChart +import com.patrykandpatrick.vico.compose.chart.scroll.rememberChartScrollSpec import com.patrykandpatrick.vico.compose.chart.scroll.rememberChartScrollState import com.patrykandpatrick.vico.core.axis.AxisPosition import com.patrykandpatrick.vico.core.axis.formatter.AxisValueFormatter +import com.patrykandpatrick.vico.core.chart.decoration.ThresholdLine +import com.patrykandpatrick.vico.core.chart.scale.AutoScaleUp import com.patrykandpatrick.vico.core.entry.ChartEntry import com.patrykandpatrick.vico.core.entry.FloatEntry -import com.patrykandpatrick.vico.core.extension.sumByFloat +import com.patrykandpatrick.vico.core.scroll.AutoScrollCondition +import com.patrykandpatrick.vico.core.scroll.InitialScroll import kotlinx.coroutines.Job import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import llc.arma.ble.app.ui.screen.inspection.thermometer.view.TemperatureEntry +import llc.arma.ble.app.ui.screen.inspection.thermometer.view.TemperatureHistoryContract +import llc.arma.ble.app.ui.screen.inspection.thermometer.view.formatter import llc.arma.ble.domain.common.ProgressState import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.usecase.AccelScale import llc.arma.ble.domain.usecase.AccelViewMode +import llc.arma.ble.domain.usecase.Accelerate import llc.arma.ble.domain.usecase.FftAxis import llc.arma.ble.domain.usecase.FftFrequency import llc.arma.ble.domain.usecase.FftViewMode -import llc.arma.ble.domain.usecase.GetAccelerometerSpectreBySerial +import llc.arma.ble.domain.usecase.GetAccelerometerHistoryBySerial +import llc.arma.ble.domain.usecase.GetAccelerometerMeasureBySerialFlow +import java.util.Date -class AccelerometerEntry( - val frequency: Long, +class AccelEntry( + val localDate: Long, override val x: Float, override val y: Float, ) : ChartEntry { - override fun withY(y: Float) = AccelerometerEntry(frequency, x, y) + override fun withY(y: Float) = AccelEntry(localDate, x, y) } @Composable fun AccelerometerHistory( ble: BleInfo, + accelScale: AccelScale, accelMode: AccelViewMode, fftAxis: FftAxis, fftMode: FftViewMode, - frequency: FftFrequency, - accelScale: AccelScale + frequency: FftFrequency ) { val viewModel = hiltViewModel() val state = viewModel.viewState.value - LaunchedEffect(ble.serial, accelMode, fftAxis, fftMode, frequency) { - viewModel.setEvent(AccelerometerHistoryContract.Event.OnStart(ble.serial, accelMode, fftAxis, fftMode, frequency, accelScale)) + LaunchedEffect(ble.serial) { + viewModel.setEvent(AccelerometerHistoryContract.Event.OnStart(ble.serial, accelScale, accelMode, fftAxis, fftMode, frequency)) } - DisposableEffect(key1 = "ble", effect = { - + DisposableEffect("ble") { onDispose { - Log.d("history", "dispose") viewModel.setEvent(AccelerometerHistoryContract.Event.StopMeasure) } - - }) + } Column( modifier = Modifier.fillMaxHeight(0.9f) @@ -94,69 +101,26 @@ fun AccelerometerHistory( val title = when(state){ is AccelerometerHistoryContract.State.Display -> { - if (state.previousHistory !== null) { - "${fftMode.localized} (${state.previousHistory.size})" - }else { - fftMode.localized - } - } - AccelerometerHistoryContract.State.Exception -> fftMode.localized - } - - Row( - modifier = Modifier.weight(1f), - verticalAlignment = Alignment.CenterVertically - ) { - - Text( - modifier = Modifier.padding(end = 16.dp), - text = title, - style = MaterialTheme.typography.titleLarge - ) - - if(state is AccelerometerHistoryContract.State.Display) { - when (state.loadingHistoryState) { - is ProgressState.Indeterminate -> { - - CircularProgressIndicator( - modifier = Modifier.size(16.dp), - strokeWidth = 2.dp, - strokeCap = StrokeCap.Round, - ) - - } - is ProgressState.Progress -> { - - val progressAnimDuration = 1500 - val progressAnimation by animateFloatAsState( - targetValue = state.loadingHistoryState.value, - animationSpec = tween( - durationMillis = progressAnimDuration, - easing = FastOutSlowInEasing - ) - ) - - CircularProgressIndicator( - modifier = Modifier.size(16.dp), - strokeWidth = 2.dp, - strokeCap = StrokeCap.Round, - progress = progressAnimation, - ) - - } - else -> {} + is ProgressState.Finished -> "График измерений (${state.loadingHistoryState.data.size})" + is ProgressState.Indeterminate -> "График измерений" + is ProgressState.Progress -> "График измерений" } - } - + AccelerometerHistoryContract.State.Exception -> "График измерений" } + Text( + modifier = Modifier.weight(1f), + text = title, + style = MaterialTheme.typography.titleLarge + ) + IconButton( onClick = { - viewModel.setEvent(AccelerometerHistoryContract.Event.OnStart(ble.serial, accelMode, fftAxis, fftMode, frequency, accelScale)) + viewModel.setEvent(AccelerometerHistoryContract.Event.OnRefreshHistory(ble.serial, accelScale, accelMode, fftAxis, fftMode, frequency)) }, - enabled = when(state){ + enabled = when(state){ is AccelerometerHistoryContract.State.Display -> state.loadingHistoryState is ProgressState.Finished AccelerometerHistoryContract.State.Exception -> true } @@ -175,7 +139,7 @@ fun AccelerometerHistory( when (state) { is AccelerometerHistoryContract.State.Display -> Display(state = state) - AccelerometerHistoryContract.State.Exception -> Exception() + is AccelerometerHistoryContract.State.Exception -> Exception() } } @@ -183,14 +147,7 @@ fun AccelerometerHistory( } } - -val axisValueFormatter = AxisValueFormatter { value, chartValues -> - (chartValues.chartEntryModel.entries.firstOrNull() - ?.getOrNull(value.toInt()) as? AccelerometerEntry) - ?.frequency?.let { String.format("%.1f", (it.toFloat() / 256f)) } - .orEmpty() -} - + @Composable fun Display( @@ -202,98 +159,93 @@ fun Display( .fillMaxSize() ) { - val data = if(state.loadingHistoryState is ProgressState.Finished){ - state.loadingHistoryState.data - } else { - state.previousHistory - } + when (state.loadingHistoryState) { - val producer = remember { - ChartEntryModelProducer(listOf()) - } + is ProgressState.Finished -> { - if(data != null){ + if(state.loadingHistoryState.data.isEmpty()){ - if(data.isEmpty()){ + Text( + modifier = Modifier.align(Alignment.Center), + text = "Нет данных" + ) - Text( - modifier = Modifier.align(Alignment.Center), - text = "Нет данных" - ) + } else { - } else { + val producer = remember { + ChartEntryModelProducer(listOf()) + } - LaunchedEffect(data){ - producer.setEntries( - data.mapIndexed { index, measurePoint -> - AccelerometerEntry(measurePoint.frequency, index.toFloat(), measurePoint.value) + producer.setEntries(state.loadingHistoryState.data.mapIndexed { index, measurePoint -> + AccelEntry(measurePoint.frequency, index.toFloat(), measurePoint.value ) + }) + + val axisValueFormatter = + AxisValueFormatter { value, chartValues -> + (chartValues.chartEntryModel.entries.firstOrNull() + ?.getOrNull(value.toInt()) as? AccelEntry) + ?.localDate + ?.let { formatter.format(Date(it)) } + .orEmpty() } - ) - } - val lineChart = columnChart( - spacing = 1.5.dp - ) + val lineChart = lineChart() - val scrollState = rememberChartScrollState() - - Chart( - diffAnimationSpec = tween(0), - isZoomEnabled = true, - chartScrollState = scrollState, - chart = lineChart, - chartModelProducer = producer, - startAxis = startAxis(), - bottomAxis = bottomAxis( - tickLength = 0.dp, - valueFormatter = axisValueFormatter, - labelRotationDegrees = -90f, - ), - modifier = Modifier.fillMaxSize() - ) - - - } - - } else { - when (state.loadingHistoryState) { - is ProgressState.Indeterminate -> { - - CircularProgressIndicator( - strokeCap = StrokeCap.Round, - modifier = Modifier.align(Alignment.Center) - ) - - } - is ProgressState.Progress -> { - - val progressAnimDuration = 1500 - val progressAnimation by animateFloatAsState( - targetValue = state.loadingHistoryState.value, - animationSpec = tween( - durationMillis = progressAnimDuration, - easing = FastOutSlowInEasing + Chart( + chart = lineChart, + chartModelProducer = producer, + startAxis = startAxis(), + bottomAxis = bottomAxis( + tickLength = 0.dp, + valueFormatter = axisValueFormatter, + labelRotationDegrees = -90f, + ), + modifier = Modifier.fillMaxSize(), + chartScrollSpec = rememberChartScrollSpec( + initialScroll = InitialScroll.End, + autoScrollCondition = AutoScrollCondition.OnModelSizeIncreased, + autoScrollAnimationSpec = tween(0) ) ) - CircularProgressIndicator( - strokeCap = StrokeCap.Round, - progress = progressAnimation, - modifier = Modifier.align(Alignment.Center) - ) - } - else -> {} + } + is ProgressState.Indeterminate -> { + + CircularProgressIndicator( + strokeCap = StrokeCap.Round, + modifier = Modifier.align(Alignment.Center) + ) + + } + is ProgressState.Progress -> { + + val progressAnimDuration = 1500 + val progressAnimation by animateFloatAsState( + targetValue = state.loadingHistoryState.value, + animationSpec = tween( + durationMillis = progressAnimDuration, + easing = FastOutSlowInEasing + ), label = "" + ) + + CircularProgressIndicator( + strokeCap = StrokeCap.Round, + progress = progressAnimation, + modifier = Modifier.align(Alignment.Center) + ) + + } + } } + } @Composable -private fun Exception( - -) { +private fun Exception() { Box( modifier = Modifier .padding(8.dp) @@ -319,20 +271,20 @@ class AccelerometerHistoryContract { data class OnStart( val serial: String, + val accelScale: AccelScale, val accelMode: AccelViewMode, val fftAxis: FftAxis, val fftMode: FftViewMode, - val frequency: FftFrequency, - val accelScale: AccelScale + val frequency: FftFrequency ) : Event() data class OnRefreshHistory( val serial: String, + val accelScale: AccelScale, val accelMode: AccelViewMode, val fftAxis: FftAxis, val fftMode: FftViewMode, - val frequency: FftFrequency, - val accelScale: AccelScale + val frequency: FftFrequency ) : Event() } @@ -340,7 +292,6 @@ class AccelerometerHistoryContract { sealed class State : ViewState { data class Display( - val previousHistory : List?, val loadingHistoryState : ProgressState> ) : State() @@ -358,15 +309,15 @@ class AccelerometerHistoryContract { @HiltViewModel class AccelerometerHistoryViewModel @Inject constructor( - private val getAccelerometerSpectreBySerial: GetAccelerometerSpectreBySerial + private val getAccelerometerHistoryBySerial: GetAccelerometerHistoryBySerial, ) : BaseViewModel() { - private var job: Job? = null + var measureJob: Job? = null + private var lastSerial: String? = null override fun setInitialState() = AccelerometerHistoryContract.State.Display( - loadingHistoryState = ProgressState.Indeterminate, - previousHistory = null + ProgressState.Indeterminate ) override fun handleEvents(event: AccelerometerHistoryContract.Event) { @@ -377,6 +328,21 @@ class AccelerometerHistoryViewModel @Inject constructor( } } + private fun reduce( + state: AccelerometerHistoryContract.State, + event: AccelerometerHistoryContract.Event.StopMeasure + ) { + + + measureJob?.cancel() + measureJob = null + + setState { + AccelerometerHistoryContract.State.Exception + } + + } + private fun reduce( state: AccelerometerHistoryContract.State, event: AccelerometerHistoryContract.Event.OnStart @@ -386,73 +352,36 @@ class AccelerometerHistoryViewModel @Inject constructor( if(state is AccelerometerHistoryContract.State.Display) { - //if(lastSerial != event.serial) { + if(lastSerial != event.serial) { - lastSerial = event.serial + lastSerial = event.serial - setState { - AccelerometerHistoryContract.State.Display( - loadingHistoryState = ProgressState.Indeterminate, - previousHistory = when(state.loadingHistoryState){ - is ProgressState.Finished -> state.loadingHistoryState.data - is ProgressState.Indeterminate -> null - is ProgressState.Progress -> null - } - ) - } + setState { + AccelerometerHistoryContract.State.Display(ProgressState.Indeterminate) + } + + measureJob?.cancel() + measureJob = null + + measureJob = getAccelerometerHistoryBySerial(event.serial).onEach { + it.fold( + onSuccess = { + setState { + AccelerometerHistoryContract.State.Display(it) + } + }, + onFailure = { + setState { + AccelerometerHistoryContract.State.Exception + } + } + ) + }.launchIn(this) - } else { - setState { - AccelerometerHistoryContract.State.Display( - loadingHistoryState = ProgressState.Indeterminate, - previousHistory = null - ) } } - job?.cancel() - job = getAccelerometerSpectreBySerial( - serial = event.serial, - accelMode = event.accelMode, - fftAxis = event.fftAxis, - fftMode = event.fftMode, - frequency = event.frequency, - accelScale = event.accelScale - ).onEach { - - val currentState = viewState.value - - if(currentState is AccelerometerHistoryContract.State.Display) { - - it.fold( - onSuccess = { - setState { - - AccelerometerHistoryContract.State.Display( - loadingHistoryState = it, - previousHistory = when (it) { - is ProgressState.Finished -> { - it.data - } - is ProgressState.Indeterminate -> currentState.previousHistory - is ProgressState.Progress -> currentState.previousHistory - } - ) - - } - }, - onFailure = { - setState { - AccelerometerHistoryContract.State.Exception - } - } - ) - - } - - }.launchIn(this) - } } @@ -461,20 +390,16 @@ class AccelerometerHistoryViewModel @Inject constructor( state: AccelerometerHistoryContract.State, event: AccelerometerHistoryContract.Event.OnRefreshHistory ) { - /*viewModelScope.launch { + viewModelScope.launch { setState { AccelerometerHistoryContract.State.Display(ProgressState.Indeterminate) } - getAccelerometerSpectreBySerial( - serial = event.serial, - accelMode = event.accelMode, - fftAxis = event.fftAxis, - fftMode = event.fftMode, - frequency = event.frequency, - accelScale = event.accelScale - ).onEach { + measureJob?.cancel() + measureJob = null + + measureJob = getAccelerometerHistoryBySerial(event.serial).onEach { it.fold( onSuccess = { setState { @@ -489,14 +414,7 @@ class AccelerometerHistoryViewModel @Inject constructor( ) }.launchIn(this) - }*/ - } - - private fun reduce( - state: AccelerometerHistoryContract.State, - event: AccelerometerHistoryContract.Event.StopMeasure - ) { - job?.cancel() + } } } \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/DisplayState.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/DisplayState.kt index 4fd3a38..a1bbdbd 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/DisplayState.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/DisplayState.kt @@ -18,6 +18,7 @@ import androidx.compose.ui.unit.dp import llc.arma.ble.app.ui.model.BleView import llc.arma.ble.app.ui.screen.BleInfoView import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract +import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract import llc.arma.ble.domain.model.Ble @Composable @@ -89,6 +90,123 @@ fun DisplayState( } + Box( + modifier = Modifier.padding( + vertical = 8.dp, + horizontal = 8.dp + ) + ) { + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .clip(RoundedCornerShape(16.dp)) + .clickable { } + .padding(8.dp) + ) { + + Column( + modifier = Modifier.weight(1f) + ) { + + Text( + text = "Сохранять историю измерений" + ) + + } + + Switch( + checked = ble.accelerometerState.saveHistory, + onCheckedChange = { + onEvent(AccelerometerContract.Event.OnSaveHistoryChanged(it)) + } + ) + + } + + } + + Box( + modifier = Modifier.padding( + vertical = 8.dp, + horizontal = 8.dp + ) + ) { + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .clip(RoundedCornerShape(16.dp)) + .clickable { + onEvent(AccelerometerContract.Event.OnSaveIntervalEdit) + } + .padding(8.dp) + ) { + + Column( + modifier = Modifier.weight(1f) + ) { + + Text( + text = "Интервал измерений" + ) + + val hours = ble.accelerometerState.historyInterval / 1000 / 60 / 60 + val minutes = (ble.accelerometerState.historyInterval - ( hours * 1000 * 60 * 60 )) / 1000 / 60 + + Text( + color = MaterialTheme.colorScheme.secondary, + style = MaterialTheme.typography.bodyMedium, + text = "$hours ч. $minutes мин." + ) + + } + + Icon( + imageVector = Icons.Rounded.KeyboardArrowDown, + contentDescription = null + ) + + } + + } + + Box( + modifier = Modifier.padding( + vertical = 8.dp, + horizontal = 8.dp + ) + ) { + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .clip(RoundedCornerShape(16.dp)) + .clickable { + onEvent(AccelerometerContract.Event.OnShowAccelerometerHistory) + } + .padding(8.dp) + ) { + + Column( + modifier = Modifier.weight(1f) + ) { + + Text( + text = "График измерений" + ) + + } + + Icon( + imageVector = Icons.Rounded.KeyboardArrowRight, + contentDescription = null + ) + + } + + } + Box( modifier = Modifier.padding( vertical = 8.dp, @@ -159,6 +277,42 @@ fun DisplayState( } + Box( + modifier = Modifier.padding( + vertical = 8.dp, + horizontal = 8.dp + ) + ) { + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .clip(RoundedCornerShape(16.dp)) + .clickable { + onEvent(AccelerometerContract.Event.OnChangePassword) + } + .padding(8.dp) + ) { + + Column( + modifier = Modifier.weight(1f) + ) { + + Text( + text = "Изменить пароль" + ) + + } + + Icon( + imageVector = Icons.Rounded.KeyboardArrowRight, + contentDescription = null + ) + + } + + } + } ) diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/IntervalEdit.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/IntervalEdit.kt new file mode 100644 index 0000000..6a268d4 --- /dev/null +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/IntervalEdit.kt @@ -0,0 +1,202 @@ +package llc.arma.ble.app.ui.screen.inspection.accelerometer.view + +import androidx.compose.animation.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.KeyboardArrowDown +import androidx.compose.material.icons.rounded.KeyboardArrowUp +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import llc.arma.ble.app.ui.model.BleView +import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract +import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract + +@Composable +fun IntervalEdit( + state: BleView.Accelerometer, + onEvent: (AccelerometerContract.Event) -> Unit, +){ + + var value by remember(state.accelerometerState.historyInterval) { + mutableStateOf((state.accelerometerState.historyInterval / 1000 / 60 / 60).toInt()) + } + + val maxInterval = 240 + + if(value > maxInterval){ + value = maxInterval + } + + if(value < 1){ + value = 1 + } + + val maxHours = maxInterval + val maxDays = maxInterval / 24 + + val dayValue = value / 24 + val hourValue = value - (24 * dayValue) + + Column( + modifier = Modifier + ) { + + Text( + modifier = Modifier.padding(horizontal = 12.dp), + text = "Интервал измерений", + style = MaterialTheme.typography.titleLarge + ) + + Spacer(modifier = Modifier.height(16.dp)) + + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.align(Alignment.CenterHorizontally) + ) { + + NumberPicker( + range = -1..maxDays, + value = dayValue, + onValueChanged = { value = (it * 24) + hourValue } + ) + + Spacer(modifier = Modifier.width(8.dp)) + + Text(text = "Дни") + + Spacer(modifier = Modifier.width(16.dp)) + + NumberPicker( + range = -1..maxHours, + value = hourValue, + onValueChanged = { + value = it + (dayValue * 24) + } + ) + + Spacer(modifier = Modifier.width(8.dp)) + + Text(text = "Часы") + + } + + Spacer(modifier = Modifier.height(16.dp)) + + Surface( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp) + .height(50.dp), + shape = CircleShape, + color = MaterialTheme.colorScheme.primaryContainer, + onClick = { + onEvent( + AccelerometerContract.Event.OnSaveIntervalChanged( + value.toLong() * 1000 * 60 * 60 + ) + ) + } + ) { + + Box(modifier = Modifier.fillMaxSize()) { + + Text( + modifier = Modifier.align(Alignment.Center), + color = MaterialTheme.colorScheme.background, + style = MaterialTheme.typography.labelLarge, + text = "Применить" + ) + + } + + } + + } + +} + +@Composable +fun NumberPicker( + modifier: Modifier = Modifier, + range: IntRange, + value: Int, + onValueChanged: (Int) -> Unit +) { + + LaunchedEffect(range){ + + if(value > range.last){ + + onValueChanged(range.last) + + } + + if(value < range.first){ + + onValueChanged(range.first) + + } + + } + + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + ){ + + FilledIconButton( + onClick = { + if(value < range.last) onValueChanged(value + 1) + } + ) { + Icon( + imageVector = Icons.Rounded.KeyboardArrowUp, + contentDescription = null + ) + } + + Spacer(modifier = Modifier.height(36.dp)) + + AnimatedContent( + targetState = value, + transitionSpec = { + if (targetState > initialState) { + (slideInVertically { height -> height } + fadeIn()).togetherWith( + slideOutVertically { height -> -height } + fadeOut()) + } else { + (slideInVertically { height -> -height } + fadeIn()).togetherWith( + slideOutVertically { height -> height } + fadeOut()) + }.using( + SizeTransform(clip = false) + ) + } + ) { targetCount -> + Text( + style = MaterialTheme.typography.displaySmall, + text = "$targetCount" + ) + } + + Spacer(modifier = Modifier.height(36.dp)) + + FilledIconButton( + onClick = { + if(value > range.first) onValueChanged(value - 1) + + } + ) { + Icon( + imageVector = Icons.Rounded.KeyboardArrowDown, + contentDescription = null + ) + } + + } + + +} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/Write.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/Write.kt index 333e666..2d2e464 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/Write.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/Write.kt @@ -44,7 +44,7 @@ fun Write( when (state) { is AccelerometerContract.State.Display.WriteState.DisplayPreview -> { - if(state.writeRequest.tx != null) { + if(state.writeRequest.tx != null || state.writeRequest.saveHistory != null || state.writeRequest.historyInterval != null) { state.writeRequest.tx?.let { Box( @@ -81,6 +81,80 @@ fun Write( } } + state.writeRequest.saveHistory?.let { + + Box( + modifier = Modifier.padding( + vertical = 0.dp, + horizontal = 8.dp + ) + ) { + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .clip(RoundedCornerShape(16.dp)) + .padding(8.dp) + ) { + + Column( + modifier = Modifier.weight(1f) + ) { + + Text( + text = "Сохранять историю измерений" + ) + Text( + color = MaterialTheme.colorScheme.secondary, + style = MaterialTheme.typography.bodyMedium, + text = it.localizedName + ) + + } + + } + + } + + } + + state.writeRequest.historyInterval?.let { + + Box( + modifier = Modifier.padding( + vertical = 0.dp, + horizontal = 8.dp + ) + ) { + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .clip(RoundedCornerShape(16.dp)) + .padding(8.dp) + ) { + + Column( + modifier = Modifier.weight(1f) + ) { + + Text( + text = "Интервал измерний" + ) + Text( + color = MaterialTheme.colorScheme.secondary, + style = MaterialTheme.typography.bodyMedium, + text = "${it / 1000 / 60 / 60} ч." + ) + + } + + } + + } + + } + Spacer(modifier = Modifier.height(20.dp)) Surface( diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/thermometer/view/TemperatureHistory.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/thermometer/view/TemperatureHistory.kt index 1cb4737..cc72e9e 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/thermometer/view/TemperatureHistory.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/thermometer/view/TemperatureHistory.kt @@ -204,7 +204,7 @@ fun Display( animationSpec = tween( durationMillis = progressAnimDuration, easing = FastOutSlowInEasing - ) + ), label = "" ) CircularProgressIndicator( @@ -221,9 +221,7 @@ fun Display( } @Composable -private fun Exception( - -) { +private fun Exception() { Box( modifier = Modifier .padding(8.dp) diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/thermometer/view/Write.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/thermometer/view/Write.kt index 7bb8f81..9f87084 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/thermometer/view/Write.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/thermometer/view/Write.kt @@ -106,7 +106,7 @@ fun Write( Text( color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.bodyMedium, - text = "${it.localizedName}" + text = it.localizedName ) } diff --git a/app/src/main/java/llc/arma/ble/data/BleRepositoryImpl.kt b/app/src/main/java/llc/arma/ble/data/BleRepositoryImpl.kt index 9260682..11c630b 100644 --- a/app/src/main/java/llc/arma/ble/data/BleRepositoryImpl.kt +++ b/app/src/main/java/llc/arma/ble/data/BleRepositoryImpl.kt @@ -126,6 +126,11 @@ class BleRepositoryImpl @Inject constructor( private val app: Application ) : BleRepository { + private val ScanResult.timerEnabled: Boolean + get() { + return scanRecord?.manufacturerSpecificData?.get(89)?.get(2) == 1.toByte() + } + private val ScanResult.info: BleInfo get() { return BleInfo( @@ -135,15 +140,11 @@ class BleRepositoryImpl @Inject constructor( rssi = rssi, type = type, scanTime = timestampNanos / 1_000_000, - tx = scanRecord?.txPowerLevel ?: 0 + tx = scanRecord?.txPowerLevel ?: 0, + recordEnabled = timerEnabled ) } - private val ScanResult.timerEnabled: Boolean - get() { - return scanRecord?.manufacturerSpecificData?.get(89)?.get(2) == 1.toByte() - } - private val ScanResult.batteryLevel: Int? get() { return scanRecord?.manufacturerSpecificData?.get(89)?.get(1) @@ -300,6 +301,24 @@ class BleRepositoryImpl @Inject constructor( return when(result.info.type) { BleInfo.Type.ACCELEROMETER -> { + + val tState = suspendCancellableCoroutine { + + CoroutineScope(Dispatchers.IO).launch { + + it.resume(readAccelState(result)) + + } + + }.fold( + onFailure = { + return Result.failure(it) + }, + onSuccess = { + it + } + ) + Result.success( flow { @@ -329,7 +348,8 @@ class BleRepositoryImpl @Inject constructor( 4 -> Ble.BleState.TX.PLUS_4 else -> Ble.BleState.TX.ZERO } - ) + ), + accelerometerState = tState ) ) @@ -457,10 +477,6 @@ class BleRepositoryImpl @Inject constructor( ) } - - BleInfo.Type.ACCELEROMETER -> { - TODO() - } } } @@ -497,6 +513,26 @@ class BleRepositoryImpl @Inject constructor( } + private suspend fun readAccelState( + record: ScanResult + ): Result { + + val history = readHistoryInterval(record).fold( + onFailure = { + return Result.failure(it) + }, + onSuccess = { return@fold it } + ) + + return Result.success( + Ble.Accelerometer.AccelerometerState( + saveHistory = record.timerEnabled, + historyInterval = history + ) + ) + + } + @OptIn(ExperimentalUnsignedTypes::class) private suspend fun readTemperature( record: ScanResult @@ -620,7 +656,45 @@ class BleRepositoryImpl @Inject constructor( if (checkPermission()) { - gatt = it.connectGatt(app, false, ReadHistoryCallback(app) { + gatt = it.connectGatt(app, false, ReadTemperatureHistoryCallback(app) { + CoroutineScope(Dispatchers.IO).launch { + send(it) + } + }) + + } else { + + CoroutineScope(Dispatchers.IO).launch { + send(Result.failure(BleException.PermissionDenied)) + } + + return@callbackFlow + + } + + } + + awaitClose { + gatt?.close() + } + + } + + } + + override suspend fun getAccelerometerHistoryBySerial( + serial: String + ): Flow>, BleException>> { + + var gatt: BluetoothGatt? = null + + return callbackFlow { + + deviceCache[serial]?.device?.let { + + if (checkPermission()) { + + gatt = it.connectGatt(app, false, ReadAccelerometerHistoryCallback(app) { CoroutineScope(Dispatchers.IO).launch { send(it) } diff --git a/app/src/main/java/llc/arma/ble/data/ReadAccelerometerCallback.kt b/app/src/main/java/llc/arma/ble/data/ReadAccelerometerCallback.kt index 8e5624c..d5b40d6 100644 --- a/app/src/main/java/llc/arma/ble/data/ReadAccelerometerCallback.kt +++ b/app/src/main/java/llc/arma/ble/data/ReadAccelerometerCallback.kt @@ -165,7 +165,7 @@ class ReadAccelerometerCallback( Log.d("accel", "notification") val data = value.toList().chunked(2).map { - it.toByteArray().get2byteShortAt(0) + it.toByteArray().get2byteShortAt() } onResult( diff --git a/app/src/main/java/llc/arma/ble/data/ReadAccelerometerHistoryCallback.kt b/app/src/main/java/llc/arma/ble/data/ReadAccelerometerHistoryCallback.kt new file mode 100644 index 0000000..41ed797 --- /dev/null +++ b/app/src/main/java/llc/arma/ble/data/ReadAccelerometerHistoryCallback.kt @@ -0,0 +1,337 @@ +package llc.arma.ble.data + +import android.Manifest +import android.app.Application +import android.bluetooth.BluetoothGatt +import android.bluetooth.BluetoothGattCallback +import android.bluetooth.BluetoothGattCharacteristic +import android.content.pm.PackageManager +import android.os.Build +import android.util.Log +import androidx.core.app.ActivityCompat +import llc.arma.ble.domain.Result +import llc.arma.ble.domain.common.BleException +import llc.arma.ble.domain.common.ProgressState +import llc.arma.ble.domain.model.Ble + +class ReadAccelerometerHistoryCallback( + private val app: Application, + private val onResult: (Result>, BleException>) -> Unit +) : BluetoothGattCallback() { + + enum class Property { + DATA_SIZE, PACKAGE + } + + private fun ByteArray.get4byteUIntAt(idx: Int) = + ((this[idx + 3].toUInt() and 0xFFu) shl 24) or + ((this[idx + 2].toUInt() and 0xFFu) shl 16) or + ((this[idx + 1].toUInt() and 0xFFu) shl 8) or + (this[idx].toUInt() and 0xFFu) + + private fun ByteArray.get2byteUIntAt(idx: Int) = + ((this[idx + 1].toUInt() and 0xFFu) shl 8) or + (this[idx].toUInt() and 0xFFu) + + private var readProperty: Property? = null + + init { + onResult(Result.success(ProgressState.Indeterminate)) + } + + override fun onConnectionStateChange( + gatt: BluetoothGatt, + status: Int, + newState: Int + ) { + super.onConnectionStateChange(gatt, status, newState) + + if(status == BluetoothGatt.GATT_SUCCESS){ + + if(newState == BluetoothGatt.STATE_CONNECTED){ + + if (checkPermission()) { + gatt.discoverServices() + + } else { + onResult(Result.failure(BleException.UnexpectedResponse)) + gatt.close() + } + } + + } else { + + onResult(Result.failure(BleException.UnexpectedResponse)) + gatt.close() + + } + + } + + override fun onServicesDiscovered( + gatt: BluetoothGatt, + status: Int + ) { + super.onServicesDiscovered(gatt, status) + if(status == BluetoothGatt.GATT_SUCCESS){ + gatt.getService(serviceUUID)?.getCharacteristic(accelerometerHistoryReadUUID)?.let { + + if (checkPermission()) { + + readProperty = Property.DATA_SIZE + gatt.writeCharacteristic(it, byteArrayOf(2)) + + } else { + + onResult(Result.failure(BleException.PermissionDenied)) + gatt.close() + + } + + } + + } + + } + + private var lastMeasureSystemTime: Long? = null + + private var bleMeasureInterval: Long? = null + private var bleRealTime: Long? = null + private var bleLastMeasureTime: Long? = null + + private val resultTemperaturePackage: MutableList = mutableListOf() + + var expectedDataSize: Int? = null + + override fun onCharacteristicRead( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic, + status: Int + ) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { + super.onCharacteristicRead(gatt, characteristic, status) + onCommonCharacteristicRead(gatt, characteristic, characteristic.value, status) + } + } + + override fun onCharacteristicRead( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic, + value: ByteArray, + status: Int + ) { + super.onCharacteristicRead(gatt, characteristic, value, status) + onCommonCharacteristicRead(gatt, characteristic, value, status) + } + + @OptIn(ExperimentalUnsignedTypes::class) + private fun onCommonCharacteristicRead( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic, + value: ByteArray, + status: Int + ){ + + if(status == BluetoothGatt.GATT_SUCCESS){ + when(readProperty){ + Property.DATA_SIZE -> { + + if(value.contentEquals(byteArrayOf(0, 0))) { + onResult( + Result.success( + ProgressState.Finished( + emptyList() + ) + ) + ) + gatt.close() + + } else { + + val writeData = mutableListOf( + 1.toByte(), + 0.toByte(), + 0.toByte() + ).apply { + addAll(value.toList()) + }.toByteArray() + + readProperty = Property.PACKAGE + gatt.writeCharacteristic(characteristic, writeData) + + } + } + Property.PACKAGE -> { + + if(value[0] == 250.toByte()){ + + bleMeasureInterval = value.get4byteUIntAt(4).toLong() + bleLastMeasureTime = value.get4byteUIntAt(8).toLong() + bleRealTime = value.get4byteUIntAt(12).toLong() + + lastMeasureSystemTime = System.currentTimeMillis() - ((bleRealTime!! - bleLastMeasureTime!!) * 1_000) + + val temperatureDataArray = value.toUByteArray().asList().subList(16, value.size) + + resultTemperaturePackage.addAll( + temperatureDataArray.chunked(2).map { + it.toUByteArray().toByteArray().get2byteShortAt().toFloat() + }.toMutableList() + ) + + val nextPackageDataCount = value.get2byteUIntAt(2) + expectedDataSize = nextPackageDataCount.toInt() + resultTemperaturePackage.size + + onResult(Result.success(ProgressState.Progress(0f / expectedDataSize!!.toFloat()))) + onResult(Result.success(ProgressState.Progress(resultTemperaturePackage.size.toFloat() / expectedDataSize!!.toFloat()))) + + if(nextPackageDataCount != 0.toUInt()){ + + if (checkPermission()) { + + gatt.writeCharacteristic(characteristic, byteArrayOf(5)) + gatt.readCharacteristic(characteristic) + + } else { + + onResult(Result.failure(BleException.PermissionDenied)) + gatt.close() + + } + + } else { + onResult( + Result.success( + ProgressState.Finished( + resultTemperaturePackage.withIndex().map { + Ble.Accelerometer.MeasurePoint( + frequency = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), + value = it.value + ) + } + ) + ) + ) + gatt.close() + } + + } else { + + if (value[0] == 251.toByte()) { + + val nextPackageDataCount = value.get2byteUIntAt(2) + val temperatureDataArray = value.toUByteArray().toList().subList(4, value.size) + + resultTemperaturePackage.addAll( + temperatureDataArray.chunked(2).map { + it.toUByteArray().toByteArray().get2byteShortAt().toFloat() + } + ) + + onResult(Result.success(ProgressState.Progress(resultTemperaturePackage.size.toFloat() / expectedDataSize!!.toFloat()))) + + if (nextPackageDataCount != 0.toUInt()) { + + val writeData = byteArrayOf(5) + + gatt.writeCharacteristic(characteristic, writeData) + gatt.readCharacteristic(characteristic) + + } else { + onResult( + Result.success( + ProgressState.Finished( + resultTemperaturePackage.withIndex().map { + Ble.Accelerometer.MeasurePoint( + frequency = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), + value = it.value + ) + } + ) + ) + ) + gatt.close() + } + } else { + onResult(Result.failure(BleException.UnexpectedResponse)) + gatt.close() + } + + } + } + else -> { + onResult(Result.failure(BleException.UnexpectedResponse)) + gatt.close() + + } + + } + + } + } + + override fun onCharacteristicWrite( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic, + status: Int + ) { + super.onCharacteristicWrite(gatt, characteristic, status) + if(status == BluetoothGatt.GATT_SUCCESS){ + + if (checkPermission()) { + + gatt.readCharacteristic(characteristic) + + } else { + + onResult(Result.failure(BleException.PermissionDenied)) + gatt.close() + + } + + } else { + onResult(Result.failure(BleException.UnexpectedResponse)) + gatt.close() + } + + } + + fun checkPermission(): Boolean { + + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_CONNECT) == + PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_SCAN) == + PackageManager.PERMISSION_GRANTED + } else { + return ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_FINE_LOCATION) == + PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_COARSE_LOCATION) == + PackageManager.PERMISSION_GRANTED + } + } + + fun BluetoothGatt.writeCharacteristic( + characteristic: BluetoothGattCharacteristic, + data: ByteArray + ): Result{ + + return if(checkPermission()){ + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + writeCharacteristic(characteristic, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT) + }else{ + characteristic.value = data + writeCharacteristic(characteristic) + } + + Result.success(Unit) + + } else { + Result.failure(BleException.PermissionDenied) + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/data/ReadAccelerometerSpectreCallback.kt b/app/src/main/java/llc/arma/ble/data/ReadAccelerometerSpectreCallback.kt index b278d3d..0efb84f 100644 --- a/app/src/main/java/llc/arma/ble/data/ReadAccelerometerSpectreCallback.kt +++ b/app/src/main/java/llc/arma/ble/data/ReadAccelerometerSpectreCallback.kt @@ -23,13 +23,12 @@ import java.nio.ByteBuffer import java.nio.ByteOrder.LITTLE_ENDIAN import java.util.UUID -fun ByteArray.get2byteShortAt(idx: Int): Int { +fun ByteArray.get2byteShortAt(): Int { val shorts = ShortArray(1) ByteBuffer.wrap(this).order(LITTLE_ENDIAN).asShortBuffer()[shorts] - return shorts[0].toInt()//(this[0].toInt() + (this[1].toInt() shl 8)).toShort() + return shorts[0].toInt() } - class ReadAccelerometerSpectreCallback( private val app: Application, private val accelScale: AccelScale, @@ -67,11 +66,6 @@ class ReadAccelerometerSpectreCallback( ) { super.onConnectionStateChange(gatt, status, newState) - Log.d("spectre", "onConnectionStateChange") - - - - if(status == BluetoothGatt.GATT_SUCCESS){ if(newState == BluetoothGatt.STATE_CONNECTED){ @@ -98,10 +92,9 @@ class ReadAccelerometerSpectreCallback( status: Int ) { super.onServicesDiscovered(gatt, status) - Log.d("spectre", "onServicesDiscovered") + if(status == BluetoothGatt.GATT_SUCCESS){ enableNotifications(gatt) - } } @@ -148,7 +141,7 @@ class ReadAccelerometerSpectreCallback( private val resultAccelerometerPackage: MutableList = mutableListOf() - var expectedDataSize: Int? = null + private var expectedDataSize: Int? = null @Deprecated("Deprecated in Java") override fun onCharacteristicRead( @@ -189,7 +182,7 @@ class ReadAccelerometerSpectreCallback( ){ if(characteristic.uuid == accelerometerReadUUID) { - Log.d("spectre", "changed") + readProperty = Property.DATA_SIZE gatt.getService(serviceUUID).getCharacteristic(accelerometerHistoryReadUUID)?.let { gatt.writeCharacteristic(it, byteArrayOf(2)) @@ -215,8 +208,6 @@ class ReadAccelerometerSpectreCallback( status: Int ){ - Log.d("spectre", "onCharacteristicRead") - if(status == BluetoothGatt.GATT_SUCCESS){ when(readProperty){ Property.DATA_SIZE -> { @@ -257,7 +248,7 @@ class ReadAccelerometerSpectreCallback( resultAccelerometerPackage.addAll( accelerometerDataArray.chunked(2).map { - it.toByteArray().get2byteShortAt(0).toFloat() + it.toByteArray().get2byteShortAt().toFloat() }.toMutableList() ) @@ -309,7 +300,7 @@ class ReadAccelerometerSpectreCallback( resultAccelerometerPackage.addAll( temperatureDataArray.chunked(2).map { - it.toByteArray().get2byteShortAt(0).toFloat() + it.toByteArray().get2byteShortAt().toFloat() } ) @@ -365,8 +356,6 @@ class ReadAccelerometerSpectreCallback( ) { super.onCharacteristicWrite(gatt, characteristic, status) - Log.d("spectre", "request written $readProperty") - if(readProperty !== null) { if (status == BluetoothGatt.GATT_SUCCESS) { @@ -390,29 +379,19 @@ class ReadAccelerometerSpectreCallback( } - override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) { - super.onMtuChanged(gatt, mtu, status) - Log.d("spectre", "mtu $mtu") - } - override fun onDescriptorWrite( gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int ) { - - Log.d("spectre", "descriptor written") super.onDescriptorWrite(gatt, descriptor, status) start(gatt) - } private fun start( gatt: BluetoothGatt, ){ - Log.d("spectre", "start") - gatt.getService(serviceUUID)?.getCharacteristic(accelerometerReadUUID)?.let { if (checkPermission()) { diff --git a/app/src/main/java/llc/arma/ble/data/ReadHistoryCallback.kt b/app/src/main/java/llc/arma/ble/data/ReadTemperatureHistoryCallback.kt similarity index 99% rename from app/src/main/java/llc/arma/ble/data/ReadHistoryCallback.kt rename to app/src/main/java/llc/arma/ble/data/ReadTemperatureHistoryCallback.kt index 8fd0a37..8c7ef9d 100644 --- a/app/src/main/java/llc/arma/ble/data/ReadHistoryCallback.kt +++ b/app/src/main/java/llc/arma/ble/data/ReadTemperatureHistoryCallback.kt @@ -13,17 +13,16 @@ import llc.arma.ble.domain.Result import llc.arma.ble.domain.common.BleException import llc.arma.ble.domain.common.ProgressState import llc.arma.ble.domain.model.Ble -import java.util.stream.Collectors -enum class Property { - DATA_SIZE, PACKAGE -} - -class ReadHistoryCallback( +class ReadTemperatureHistoryCallback( private val app: Application, private val onResult: (Result>, BleException>) -> Unit ) : BluetoothGattCallback() { + enum class Property { + DATA_SIZE, PACKAGE + } + private fun ByteArray.get4byteUIntAt(idx: Int) = ((this[idx + 3].toUInt() and 0xFFu) shl 24) or ((this[idx + 2].toUInt() and 0xFFu) shl 16) or diff --git a/app/src/main/java/llc/arma/ble/data/WriteAccelerometerCallback.kt b/app/src/main/java/llc/arma/ble/data/WriteAccelerometerCallback.kt index e87fbc8..fa8bba4 100644 --- a/app/src/main/java/llc/arma/ble/data/WriteAccelerometerCallback.kt +++ b/app/src/main/java/llc/arma/ble/data/WriteAccelerometerCallback.kt @@ -64,10 +64,43 @@ class WriteAccelerometerCallback( status: Int ){ - if(request.tx != null) { + if(request.tx != null || request.saveHistory != null) { + + fun UInt.to4ByteArrayInLittleEndian(): ByteArray = + (3 downTo 0).map { + (this shr (it * Byte.SIZE_BITS)).toByte() + }.toByteArray() var uuid: Pair? = null + uuid = request.historyInterval?.let { + + this.request = request.copy( + historyInterval = null + ) + + Pair( + intervalWriteUUID, + mutableListOf(3).apply { + addAll((it).toUInt().to4ByteArrayInLittleEndian().reversed().toList()) + }.toByteArray() + ) + } + + uuid = request.saveHistory?.let { + + this.request = request.copy( + saveHistory = null + ) + + Pair( + saveEnabledWriteUUID, + mutableListOf(4).apply { + add(if (it) 1 else 0) + }.toByteArray() + ) + } ?: uuid + uuid = request.tx?.let { this.request = request.copy( @@ -143,8 +176,6 @@ class WriteAccelerometerCallback( status: Int ) { - Log.d("beacon", "onCharacteristicWrite $status") - super.onCharacteristicWrite(gatt, characteristic, status) if(checkPermission()) { diff --git a/app/src/main/java/llc/arma/ble/data/WriteThermometerCallback.kt b/app/src/main/java/llc/arma/ble/data/WriteThermometerCallback.kt index d12574b..833d6b3 100644 --- a/app/src/main/java/llc/arma/ble/data/WriteThermometerCallback.kt +++ b/app/src/main/java/llc/arma/ble/data/WriteThermometerCallback.kt @@ -34,8 +34,6 @@ class WriteThermometerCallback( ) { super.onConnectionStateChange(gatt, status, newState) - Log.d("th", "onConnectionStateChange $status $newState") - if(checkPermission()) { if(status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) { @@ -60,7 +58,6 @@ class WriteThermometerCallback( gatt: BluetoothGatt, status: Int ) { - Log.d("th", "onServicesDiscovered $status") super.onServicesDiscovered(gatt, status) onCycle(gatt, status) @@ -183,8 +180,6 @@ class WriteThermometerCallback( status: Int ) { - Log.d("th", "onCharacteristicWrite $status") - super.onCharacteristicWrite(gatt, characteristic, status) if(checkPermission()) { diff --git a/app/src/main/java/llc/arma/ble/domain/model/Ble.kt b/app/src/main/java/llc/arma/ble/domain/model/Ble.kt index 4542051..9afce19 100644 --- a/app/src/main/java/llc/arma/ble/domain/model/Ble.kt +++ b/app/src/main/java/llc/arma/ble/domain/model/Ble.kt @@ -6,11 +6,14 @@ sealed class Ble( class Accelerometer( info: BleInfo, - val state: BleState + val state: BleState, + val accelerometerState: AccelerometerState ): Ble(info) { data class WriteRequest( val tx: BleState.TX?, + val saveHistory: Boolean?, + val historyInterval: Long? ) class MeasurePoint ( @@ -18,6 +21,11 @@ sealed class Ble( val value: Float ) + data class AccelerometerState( + val saveHistory: Boolean, + val historyInterval: Long + ) + } class Beacon( diff --git a/app/src/main/java/llc/arma/ble/domain/model/BleInfo.kt b/app/src/main/java/llc/arma/ble/domain/model/BleInfo.kt index cfa058a..c3e5166 100644 --- a/app/src/main/java/llc/arma/ble/domain/model/BleInfo.kt +++ b/app/src/main/java/llc/arma/ble/domain/model/BleInfo.kt @@ -7,7 +7,8 @@ data class BleInfo( val rssi: Int?, val type: Type, val scanTime: Long, - val tx: Int + val tx: Int, + val recordEnabled: Boolean ){ enum class Type { diff --git a/app/src/main/java/llc/arma/ble/domain/repository/BleRepository.kt b/app/src/main/java/llc/arma/ble/domain/repository/BleRepository.kt index c563ece..8713ccf 100644 --- a/app/src/main/java/llc/arma/ble/domain/repository/BleRepository.kt +++ b/app/src/main/java/llc/arma/ble/domain/repository/BleRepository.kt @@ -50,4 +50,6 @@ interface BleRepository { frequency: FftFrequency ): Flow>, BleException>> + suspend fun getAccelerometerHistoryBySerial(serial: String): Flow>, BleException>> + } \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/domain/usecase/GetAccelerometerHistoryBySerial.kt b/app/src/main/java/llc/arma/ble/domain/usecase/GetAccelerometerHistoryBySerial.kt new file mode 100644 index 0000000..e7496b2 --- /dev/null +++ b/app/src/main/java/llc/arma/ble/domain/usecase/GetAccelerometerHistoryBySerial.kt @@ -0,0 +1,21 @@ +package llc.arma.ble.domain.usecase + +import kotlinx.coroutines.flow.Flow +import llc.arma.ble.domain.Result +import llc.arma.ble.domain.common.BleException +import llc.arma.ble.domain.common.ProgressState +import llc.arma.ble.domain.model.Ble +import llc.arma.ble.domain.repository.BleRepository +import javax.inject.Inject + +class GetAccelerometerHistoryBySerial @Inject constructor( + private val bleRepository: BleRepository +) { + + suspend operator fun invoke(serial: String): Flow>, BleException>> { + + return bleRepository.getAccelerometerHistoryBySerial(serial) + + } + +} \ No newline at end of file