История изменений акселерометра
This commit is contained in:
parent
df30906056
commit
c59c929f35
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<String>("serial")!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun reduce(
|
||||
state: ConnectionContract.State,
|
||||
event: ConnectionContract.Event.OnNavigateUp
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<AccelerometerViewModel>()
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<AccelerometerSpectreViewModel>()
|
||||
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<AxisPosition.Horizontal.Bottom> { 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<FloatEntry>())
|
||||
}
|
||||
|
||||
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<Ble.Accelerometer.MeasurePoint>?,
|
||||
val loadingHistoryState : ProgressState<List<Ble.Accelerometer.MeasurePoint>>
|
||||
) : State()
|
||||
|
||||
object Exception : State()
|
||||
|
||||
}
|
||||
|
||||
sealed class Effect : ViewSideEffect {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@HiltViewModel
|
||||
class AccelerometerSpectreViewModel @Inject constructor(
|
||||
private val getAccelerometerSpectreBySerial: GetAccelerometerSpectreBySerial
|
||||
) : BaseViewModel<AccelerometerSpectreContract.State, AccelerometerSpectreContract.Event, AccelerometerSpectreContract.Effect>() {
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<AccelerometerMeasureViewModel>()
|
||||
val viewModel = hiltViewModel<AccelerometerAccelViewModel>()
|
||||
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<AccelerometerMeasureContract.State, AccelerometerMeasureContract.Event, AccelerometerMeasureContract.Effect>() {
|
||||
) : BaseViewModel<AccelerometerAccelContract.State, AccelerometerAccelContract.Event, AccelerometerAccelContract.Effect>() {
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
@ -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<AccelerometerHistoryViewModel>()
|
||||
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<AxisPosition.Horizontal.Bottom> { 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<FloatEntry>())
|
||||
}
|
||||
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<FloatEntry>())
|
||||
}
|
||||
|
||||
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<AxisPosition.Horizontal.Bottom> { 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<Ble.Accelerometer.MeasurePoint>?,
|
||||
val loadingHistoryState : ProgressState<List<Ble.Accelerometer.MeasurePoint>>
|
||||
) : State()
|
||||
|
||||
|
|
@ -358,15 +309,15 @@ class AccelerometerHistoryContract {
|
|||
|
||||
@HiltViewModel
|
||||
class AccelerometerHistoryViewModel @Inject constructor(
|
||||
private val getAccelerometerSpectreBySerial: GetAccelerometerSpectreBySerial
|
||||
private val getAccelerometerHistoryBySerial: GetAccelerometerHistoryBySerial,
|
||||
) : BaseViewModel<AccelerometerHistoryContract.State, AccelerometerHistoryContract.Event, AccelerometerHistoryContract.Effect>() {
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ fun Write(
|
|||
Text(
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
text = "${it.localizedName}"
|
||||
text = it.localizedName
|
||||
)
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Ble.Accelerometer.AccelerometerState, BleException> {
|
||||
|
||||
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<Result<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, 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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, 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<Float> = 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<Unit, BleException>{
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<Float> = 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()) {
|
||||
|
|
|
|||
|
|
@ -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<ProgressState<List<Ble.Thermometer.MeasurePoint>>, 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
|
||||
|
|
@ -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<UUID, ByteArray>? = null
|
||||
|
||||
uuid = request.historyInterval?.let {
|
||||
|
||||
this.request = request.copy(
|
||||
historyInterval = null
|
||||
)
|
||||
|
||||
Pair(
|
||||
intervalWriteUUID,
|
||||
mutableListOf<Byte>(3).apply {
|
||||
addAll((it).toUInt().to4ByteArrayInLittleEndian().reversed().toList())
|
||||
}.toByteArray()
|
||||
)
|
||||
}
|
||||
|
||||
uuid = request.saveHistory?.let {
|
||||
|
||||
this.request = request.copy(
|
||||
saveHistory = null
|
||||
)
|
||||
|
||||
Pair(
|
||||
saveEnabledWriteUUID,
|
||||
mutableListOf<Byte>(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()) {
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -50,4 +50,6 @@ interface BleRepository {
|
|||
frequency: FftFrequency
|
||||
): Flow<Result<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, BleException>>
|
||||
|
||||
suspend fun getAccelerometerHistoryBySerial(serial: String): Flow<Result<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, BleException>>
|
||||
|
||||
}
|
||||
|
|
@ -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<Result<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, BleException>> {
|
||||
|
||||
return bleRepository.getAccelerometerHistoryBySerial(serial)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue