From ab1046fd61471a96ce39d9b8dab05f289b31748d Mon Sep 17 00:00:00 2001 From: Vineyro Date: Fri, 14 Jun 2024 17:01:41 +0700 Subject: [PATCH] kotlin ble migration --- app/build.gradle | 8 +- app/src/main/AndroidManifest.xml | 10 +- .../llc/arma/ble/app/ui/mapper/BleMapper.kt | 2 +- .../arma/ble/app/ui/mapper/BleViewMapper.kt | 2 +- .../java/llc/arma/ble/app/ui/model/BleView.kt | 6 +- .../ble/app/ui/screen/ble/BleListViewModel.kt | 20 +- .../accelerometer/AccelerometerViewModel.kt | 16 +- .../accelerometer/view/AccelScaleEdit.kt | 11 +- .../view/AccelerometerSpectre.kt | 5 +- .../accelerometer/view/AcceleromterAccel.kt | 58 +- .../accelerometer/view/AcceleromterHistory.kt | 108 ++- .../accelerometer/view/DisplayState.kt | 12 +- .../accelerometer/view/HistoryEdit.kt | 14 +- .../accelerometer/view/IntervalEdit.kt | 37 +- .../inspection/accelerometer/view/Write.kt | 20 +- .../thermometer/ThermometerScreen.kt | 18 +- .../thermometer/view/TemperatureHistory.kt | 2 +- .../llc/arma/ble/data/BleRepositoryImpl.kt | 902 ++++-------------- .../llc/arma/ble/data/EmailRepositoryImpl.kt | 12 - ...rHistory.kt => GetAccelerometerHistory.kt} | 106 +- .../ble/data/GetAccelerometerRealtimeData.kt | 136 +++ .../data/ReadAccelerometerSpectreCallback.kt | 512 +--------- .../data/ReadTemperatureHistoryCallback.kt | 330 +------ .../llc/arma/ble/data/XlsxRepositoryImpl.kt | 16 +- .../data/extensions/ApplicationExtension.kt | 22 + .../ble/data/extensions/BleEnumExtensions.kt | 93 ++ .../extensions/BleScanResultExtensions.kt | 40 + .../data/extensions/ByteArrayExtensions.kt | 35 + .../java/llc/arma/ble/domain/model/Ble.kt | 51 +- .../ble/domain/repository/BleRepository.kt | 11 +- .../ble/domain/repository/XlsxRepository.kt | 1 - .../GetAccelerometerMeasureBySerialFlow.kt | 38 +- .../GetAccelerometerSpectreBySerial.kt | 14 +- .../ble/domain/usecase/GetBleAroundFlow.kt | 2 +- .../domain/usecase/GetConnectedBleDevices.kt | 15 - .../usecase/GetTemperatureHistoryBySerial.kt | 2 +- 36 files changed, 859 insertions(+), 1828 deletions(-) rename app/src/main/java/llc/arma/ble/data/{ReadAccelerometerHistory.kt => GetAccelerometerHistory.kt} (69%) create mode 100644 app/src/main/java/llc/arma/ble/data/GetAccelerometerRealtimeData.kt create mode 100644 app/src/main/java/llc/arma/ble/data/extensions/ApplicationExtension.kt create mode 100644 app/src/main/java/llc/arma/ble/data/extensions/BleEnumExtensions.kt create mode 100644 app/src/main/java/llc/arma/ble/data/extensions/BleScanResultExtensions.kt create mode 100644 app/src/main/java/llc/arma/ble/data/extensions/ByteArrayExtensions.kt delete mode 100644 app/src/main/java/llc/arma/ble/domain/usecase/GetConnectedBleDevices.kt diff --git a/app/build.gradle b/app/build.gradle index 959d359..e132dfb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId "llc.arma.ble" minSdk 26 targetSdk 34 - versionCode 18 - versionName "1.2.18" + versionCode 22 + versionName "1.3.2" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { @@ -86,8 +86,8 @@ dependencies { kapt('com.google.dagger:hilt-android-compiler:2.45') kapt("androidx.hilt:hilt-compiler:1.0.0") - implementation 'no.nordicsemi.android.kotlin.ble:scanner:1.0.14' - implementation 'no.nordicsemi.android.kotlin.ble:client:1.0.14' + implementation 'no.nordicsemi.android.kotlin.ble:scanner:1.0.19' + implementation 'no.nordicsemi.android.kotlin.ble:client:1.0.19' implementation "com.google.accompanist:accompanist-permissions:0.26.3-beta" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f9ed2e5..7041d30 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,8 +7,10 @@ - - + + - + + diff --git a/app/src/main/java/llc/arma/ble/app/ui/mapper/BleMapper.kt b/app/src/main/java/llc/arma/ble/app/ui/mapper/BleMapper.kt index aaed0c2..d1c295d 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/mapper/BleMapper.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/mapper/BleMapper.kt @@ -39,7 +39,7 @@ class BleMapper @Inject constructor( tx = txMapper.map(input.state.tx) ), accelerometerState = BleView.Accelerometer.AccelerometerState( - saveHistory = input.accelerometerState.saveHistory, + saveHistorySettings = input.accelerometerState.saveHistorySettings, historyInterval = input.accelerometerState.historyInterval ) ) diff --git a/app/src/main/java/llc/arma/ble/app/ui/mapper/BleViewMapper.kt b/app/src/main/java/llc/arma/ble/app/ui/mapper/BleViewMapper.kt index 012e920..b13ea1c 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/mapper/BleViewMapper.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/mapper/BleViewMapper.kt @@ -39,7 +39,7 @@ class BleViewMapper @Inject constructor( tx = txMapper.map(input.state.tx) ), accelerometerState = Ble.Accelerometer.AccelerometerState( - saveHistory = input.accelerometerState.saveHistory, + saveHistorySettings = input.accelerometerState.saveHistory, historyInterval = input.accelerometerState.historyInterval, ) ) diff --git a/app/src/main/java/llc/arma/ble/app/ui/model/BleView.kt b/app/src/main/java/llc/arma/ble/app/ui/model/BleView.kt index 7f134cb..747a881 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/model/BleView.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/model/BleView.kt @@ -5,8 +5,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.model.BleInfo -import llc.arma.ble.domain.usecase.AccelScale -import llc.arma.ble.domain.usecase.AccelViewMode sealed class BleView( val info: BleInfo @@ -19,10 +17,10 @@ sealed class BleView( ) : BleView(info) { class AccelerometerState( - saveHistory: Ble.Accelerometer.History, + saveHistorySettings: Ble.Accelerometer.HistorySettings, historyInterval: Long, ) { - var saveHistory by mutableStateOf(saveHistory) + var saveHistory by mutableStateOf(saveHistorySettings) var historyInterval by mutableStateOf(historyInterval) } diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/ble/BleListViewModel.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/ble/BleListViewModel.kt index 85b5bff..d7484bd 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/ble/BleListViewModel.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/ble/BleListViewModel.kt @@ -24,7 +24,23 @@ class BleListViewModel @Inject constructor( var job: Job? = null - while (true) { + getBleAroundFlow().fold( + onSuccess = { + it.onEach { + setState { + copy( + connectedBleList = emptyList(), + bleList = it + ) + } + }.launchIn(viewModelScope) + }, + onFailure = { + throw IllegalStateException() + } + ) + + /*while (true) { job?.cancel() job = getBleAroundFlow().onEach { @@ -46,7 +62,7 @@ class BleListViewModel @Inject constructor( delay(30_000) - } + }*/ } diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/AccelerometerViewModel.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/AccelerometerViewModel.kt index 44c40fd..d248f72 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/AccelerometerViewModel.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/AccelerometerViewModel.kt @@ -82,8 +82,8 @@ class AccelerometerViewModel @Inject constructor( var saveHistory = state.accelerometer.accelerometerState.saveHistory - if(saveHistory is Ble.Accelerometer.History.Enabled){ - saveHistory = Ble.Accelerometer.History.Enabled( + if(saveHistory is Ble.Accelerometer.HistorySettings.Enabled){ + saveHistory = Ble.Accelerometer.HistorySettings.Enabled( mode = event.mode, scale = saveHistory.scale, detailed = saveHistory.detailed @@ -105,7 +105,7 @@ class AccelerometerViewModel @Inject constructor( var saveHistory = state.accelerometer.accelerometerState.saveHistory - if(saveHistory is Ble.Accelerometer.History.Enabled){ + if(saveHistory is Ble.Accelerometer.HistorySettings.Enabled){ saveHistory = saveHistory.copy(scale = event.scale) } @@ -199,7 +199,7 @@ class AccelerometerViewModel @Inject constructor( if(event.save){ - state.accelerometer.accelerometerState.saveHistory = Ble.Accelerometer.History.Enabled( + state.accelerometer.accelerometerState.saveHistory = Ble.Accelerometer.HistorySettings.Enabled( scale = AccelScale.S_2, mode = AccelViewMode.ACCELERATION, detailed = true @@ -211,7 +211,7 @@ class AccelerometerViewModel @Inject constructor( } else { - state.accelerometer.accelerometerState.saveHistory = Ble.Accelerometer.History.Disabled + state.accelerometer.accelerometerState.saveHistory = Ble.Accelerometer.HistorySettings.Disabled } @@ -397,7 +397,7 @@ class AccelerometerViewModel @Inject constructor( val writeRequest = Ble.Accelerometer.WriteRequest( 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, + saveHistorySettings = if(newBle.accelerometerState.saveHistorySettings == state.origin.accelerometerState.saveHistorySettings) null else newBle.accelerometerState.saveHistorySettings, historyInterval = if(newBle.accelerometerState.historyInterval == state.origin.accelerometerState.historyInterval) null else newBle.accelerometerState.historyInterval, ) @@ -565,8 +565,8 @@ class AccelerometerViewModel @Inject constructor( tx = request.writeRequest.tx ?: state.origin.state.tx ), accelerometerState = currentState.origin.accelerometerState.copy( - saveHistory = request.writeRequest.saveHistory - ?: currentState.origin.accelerometerState.saveHistory + saveHistorySettings = request.writeRequest.saveHistorySettings + ?: currentState.origin.accelerometerState.saveHistorySettings ) ) diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelScaleEdit.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelScaleEdit.kt index 1402fc0..8bcd84b 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelScaleEdit.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelScaleEdit.kt @@ -2,13 +2,8 @@ package llc.arma.ble.app.ui.screen.inspection.accelerometer.view import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* -import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.KeyboardArrowDown -import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.RadioButton import androidx.compose.material3.Surface @@ -21,10 +16,6 @@ import androidx.compose.ui.unit.dp import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract 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 @Composable fun AccelScaleEdit( @@ -39,7 +30,7 @@ fun AccelScaleEdit( state.accelScale AccelerometerContract.Event.Next.HISTORY -> { val history = state.accelerometer.accelerometerState.saveHistory - if (history is Ble.Accelerometer.History.Enabled) + if (history is Ble.Accelerometer.HistorySettings.Enabled) history.scale else { state.accelScale diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelerometerSpectre.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelerometerSpectre.kt index 7de1d1c..2c9821d 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelerometerSpectre.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelerometerSpectre.kt @@ -76,7 +76,6 @@ fun AccelerometerSpectre( DisposableEffect(key1 = "ble", effect = { onDispose { - Log.d("history", "dispose") viewModel.setEvent(AccelerometerSpectreContract.Event.StopMeasure) } @@ -340,8 +339,8 @@ class AccelerometerSpectreContract { sealed class State : ViewState { data class Display( - val previousHistory : List?, - val loadingHistoryState : ProgressState> + val previousHistory : List?, + val loadingHistoryState : ProgressState> ) : State() object Exception : State() diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterAccel.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterAccel.kt index f8400c2..4f7d225 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterAccel.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterAccel.kt @@ -25,8 +25,6 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Refresh import androidx.compose.ui.draw.rotate import androidx.compose.ui.graphics.StrokeCap -import androidx.compose.ui.graphics.TransformOrigin -import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.text.style.TextAlign import com.patrykandpatrick.vico.compose.axis.horizontal.bottomAxis import com.patrykandpatrick.vico.compose.chart.line.lineChart @@ -39,10 +37,10 @@ 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.domain.model.Ble import llc.arma.ble.domain.usecase.AccelScale import llc.arma.ble.domain.usecase.AccelViewMode import llc.arma.ble.domain.usecase.AccelViewMode.* -import llc.arma.ble.domain.usecase.MeasureData import llc.arma.ble.domain.usecase.FftAxis import llc.arma.ble.domain.usecase.FftFrequency import llc.arma.ble.domain.usecase.FftViewMode @@ -151,37 +149,40 @@ fun Display( xProducer.setEntries(state.measureHistory.mapIndexed { index, measurePoint -> when(measurePoint){ - is MeasureData.Accelerate -> { + is Ble.Accelerometer.RealtimePoint.Common -> FloatEntry(index.toFloat(), measurePoint.x ) - } - is MeasureData.Vibration -> FloatEntry(index.toFloat(), measurePoint.value) - is MeasureData.Angle -> { - FloatEntry(index.toFloat(), measurePoint.xAngle ) - } + is Ble.Accelerometer.RealtimePoint.Vibration -> + FloatEntry(index.toFloat(), measurePoint.value) + is Ble.Accelerometer.RealtimePoint.Angle -> + FloatEntry(index.toFloat(), measurePoint.x ) + is Ble.Accelerometer.RealtimePoint.Rotation -> + FloatEntry(index.toFloat(), measurePoint.angle ) } }) yProducer.setEntries(state.measureHistory.mapIndexed { index, measurePoint -> when(measurePoint){ - is MeasureData.Accelerate -> { + is Ble.Accelerometer.RealtimePoint.Common -> FloatEntry(index.toFloat(), measurePoint.y ) - } - is MeasureData.Vibration -> FloatEntry(index.toFloat(), measurePoint.value) - is MeasureData.Angle -> { - FloatEntry(index.toFloat(), measurePoint.yAngle ) - } + is Ble.Accelerometer.RealtimePoint.Vibration -> + FloatEntry(index.toFloat(), measurePoint.value) + is Ble.Accelerometer.RealtimePoint.Angle -> + FloatEntry(index.toFloat(), measurePoint.y) + is Ble.Accelerometer.RealtimePoint.Rotation -> + FloatEntry(index.toFloat(), measurePoint.tmp) } }) zProducer.setEntries(state.measureHistory.mapIndexed { index, measurePoint -> when(measurePoint){ - is MeasureData.Accelerate -> { - FloatEntry(index.toFloat(), measurePoint.z ) - } - is MeasureData.Vibration -> FloatEntry(index.toFloat(), measurePoint.value) - is MeasureData.Angle -> { - FloatEntry(index.toFloat(), measurePoint.zAngle ) - } + is Ble.Accelerometer.RealtimePoint.Common -> + FloatEntry(index.toFloat(), measurePoint.z) + is Ble.Accelerometer.RealtimePoint.Vibration -> + FloatEntry(index.toFloat(), measurePoint.value) + is Ble.Accelerometer.RealtimePoint.Angle -> + FloatEntry(index.toFloat(), measurePoint.z) + is Ble.Accelerometer.RealtimePoint.Rotation -> + FloatEntry(index.toFloat(), measurePoint.turnovers.toFloat()) } }) @@ -195,16 +196,7 @@ fun Display( val lastMeasure = state.measureHistory.lastOrNull() - /*when(lastMeasure){ - is MeasureData.Accelerate -> TODO() - is MeasureData.Angle -> TODO() - is MeasureData.Vibration -> { - - } - null -> {} - }*/ - - if(lastMeasure is MeasureData.Accelerate) { + if(lastMeasure is Ble.Accelerometer.RealtimePoint.Common) { when(state.mode){ ROTATIONS -> { @@ -524,7 +516,7 @@ class AccelerometerAccelContract { data class Display( val mode: AccelViewMode, - val measureHistory : List + val measureHistory : List ) : State() object Exception : State() diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterHistory.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterHistory.kt index 5d80831..8533c72 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterHistory.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterHistory.kt @@ -52,7 +52,6 @@ import llc.arma.ble.domain.usecase.FftFrequency import llc.arma.ble.domain.usecase.FftViewMode import llc.arma.ble.domain.usecase.GetAccelerometerHistoryBySerial import llc.arma.ble.domain.usecase.GetBleBySerial -import llc.arma.ble.domain.usecase.MeasureData import java.util.Date class AccelEntry( @@ -208,7 +207,7 @@ fun Display( xProducer.setEntries(state.loadingHistoryState.data.mapIndexed { index, measurePoint -> when(measurePoint){ - is Ble.Accelerometer.HistoryPoint.Accelerate -> { + is Ble.Accelerometer.HistoryPoint.Acceleration -> { AccelEntry(measurePoint.date, index.toFloat(), measurePoint.x ) } is Ble.Accelerometer.HistoryPoint.Vibration -> { @@ -217,12 +216,16 @@ fun Display( is Ble.Accelerometer.HistoryPoint.Angle -> { AccelEntry(measurePoint.date, index.toFloat(), measurePoint.x ) } + is Ble.Accelerometer.HistoryPoint.Rotation -> { + AccelEntry(measurePoint.date, index.toFloat(), measurePoint.value.toFloat() ) + } } + }) yProducer.setEntries(state.loadingHistoryState.data.mapIndexed { index, measurePoint -> when(measurePoint){ - is Ble.Accelerometer.HistoryPoint.Accelerate -> { + is Ble.Accelerometer.HistoryPoint.Acceleration -> { AccelEntry(measurePoint.date, index.toFloat(), measurePoint.y ) } is Ble.Accelerometer.HistoryPoint.Vibration -> { @@ -231,12 +234,15 @@ fun Display( is Ble.Accelerometer.HistoryPoint.Angle -> { AccelEntry(measurePoint.date, index.toFloat(), measurePoint.y ) } + is Ble.Accelerometer.HistoryPoint.Rotation -> { + AccelEntry(measurePoint.date, index.toFloat(), measurePoint.value.toFloat() ) + } } }) zProducer.setEntries(state.loadingHistoryState.data.mapIndexed { index, measurePoint -> when(measurePoint){ - is Ble.Accelerometer.HistoryPoint.Accelerate -> { + is Ble.Accelerometer.HistoryPoint.Acceleration -> { AccelEntry(measurePoint.date, index.toFloat(), measurePoint.z ) } is Ble.Accelerometer.HistoryPoint.Vibration -> { @@ -245,6 +251,9 @@ fun Display( is Ble.Accelerometer.HistoryPoint.Angle -> { AccelEntry(measurePoint.date, index.toFloat(), measurePoint.z ) } + is Ble.Accelerometer.HistoryPoint.Rotation -> { + AccelEntry(measurePoint.date, index.toFloat(), measurePoint.value.toFloat() ) + } } }) @@ -258,9 +267,10 @@ fun Display( val lastMeasure = state.loadingHistoryState.data.lastOrNull() - if((lastMeasure is Ble.Accelerometer.HistoryPoint.Vibration).not()) { - - Column() { + when(lastMeasure){ + is Ble.Accelerometer.HistoryPoint.Acceleration, + is Ble.Accelerometer.HistoryPoint.Angle -> { + Column { Text(text = "Ось X:") @@ -330,36 +340,64 @@ fun Display( ) } - - } else { - - Column { - - Text(text = "Вибрация:") - - Chart( - chart = lineChart, - chartModelProducer = xProducer, - startAxis = startAxis(), - bottomAxis = bottomAxis( - tickLength = 0.dp, - valueFormatter = axisValueFormatter, - labelRotationDegrees = -90f, - ), - modifier = Modifier - .fillMaxWidth() - .weight(1f), - autoScaleUp = AutoScaleUp.None, - diffAnimationSpec = tween(0), - chartScrollSpec = rememberChartScrollSpec( - initialScroll = InitialScroll.End, - autoScrollCondition = AutoScrollCondition.OnModelSizeIncreased, - autoScrollAnimationSpec = tween(0) - ) - ) - } + is Ble.Accelerometer.HistoryPoint.Rotation -> { + Column { + Text(text = "Обороты:") + + Chart( + chart = lineChart, + chartModelProducer = xProducer, + startAxis = startAxis(), + bottomAxis = bottomAxis( + tickLength = 0.dp, + valueFormatter = axisValueFormatter, + labelRotationDegrees = -90f, + ), + modifier = Modifier + .fillMaxWidth() + .weight(1f), + autoScaleUp = AutoScaleUp.None, + diffAnimationSpec = tween(0), + chartScrollSpec = rememberChartScrollSpec( + initialScroll = InitialScroll.End, + autoScrollCondition = AutoScrollCondition.OnModelSizeIncreased, + autoScrollAnimationSpec = tween(0) + ) + ) + + } + } + is Ble.Accelerometer.HistoryPoint.Vibration -> { + Column { + + Text(text = "Вибрация:") + + Chart( + chart = lineChart, + chartModelProducer = xProducer, + startAxis = startAxis(), + bottomAxis = bottomAxis( + tickLength = 0.dp, + valueFormatter = axisValueFormatter, + labelRotationDegrees = -90f, + ), + modifier = Modifier + .fillMaxWidth() + .weight(1f), + autoScaleUp = AutoScaleUp.None, + diffAnimationSpec = tween(0), + chartScrollSpec = rememberChartScrollSpec( + initialScroll = InitialScroll.End, + autoScrollCondition = AutoScrollCondition.OnModelSizeIncreased, + autoScrollAnimationSpec = tween(0) + ) + ) + + } + } + null -> {} } } diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/DisplayState.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/DisplayState.kt index 5fc7039..776a7a3 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/DisplayState.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/DisplayState.kt @@ -18,7 +18,6 @@ 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 @@ -115,7 +114,7 @@ fun DisplayState( val history = ble.accelerometerState.saveHistory - if(history is Ble.Accelerometer.History.Enabled){ + if(history is Ble.Accelerometer.HistorySettings.Enabled){ Text( color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.bodyMedium, @@ -133,7 +132,7 @@ fun DisplayState( } Switch( - checked = ble.accelerometerState.saveHistory is Ble.Accelerometer.History.Enabled, + checked = ble.accelerometerState.saveHistory is Ble.Accelerometer.HistorySettings.Enabled, onCheckedChange = { onEvent(AccelerometerContract.Event.OnSaveHistoryChanged(it)) } @@ -168,13 +167,14 @@ fun DisplayState( text = "Интервал измерений" ) - val hours = ble.accelerometerState.historyInterval / 1000 / 60 / 60 - val minutes = (ble.accelerometerState.historyInterval - ( hours * 1000 * 60 * 60 )) / 1000 / 60 + val hours = ble.accelerometerState.historyInterval / millisInHour + val minutes = (ble.accelerometerState.historyInterval - (hours * millisInHour)) / millisInMinute + val seconds = (ble.accelerometerState.historyInterval - (hours * millisInHour) - (minutes * millisInMinute)) / millisInSecond Text( color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.bodyMedium, - text = "$hours ч. $minutes мин." + text = "$hours ч. $minutes мин. $seconds сек." ) } diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/HistoryEdit.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/HistoryEdit.kt index 6d6aa6e..3add635 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/HistoryEdit.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/HistoryEdit.kt @@ -2,17 +2,13 @@ package llc.arma.ble.app.ui.screen.inspection.accelerometer.view import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* -import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.KeyboardArrowDown import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.RadioButton import androidx.compose.material3.Surface -import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -21,10 +17,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract import llc.arma.ble.domain.model.Ble -import llc.arma.ble.domain.usecase.AccelScale -import llc.arma.ble.domain.usecase.FftAxis -import llc.arma.ble.domain.usecase.FftFrequency -import llc.arma.ble.domain.usecase.FftViewMode @Composable fun HistoryEdit( @@ -34,9 +26,9 @@ fun HistoryEdit( val history = state.accelerometer.accelerometerState.saveHistory - val detailed = if (history is Ble.Accelerometer.History.Enabled) history.detailed else false - val accelMode = if (history is Ble.Accelerometer.History.Enabled) history.mode else state.accelViewMode - val accelScale = if (history is Ble.Accelerometer.History.Enabled) history.scale else state.accelScale + val detailed = if (history is Ble.Accelerometer.HistorySettings.Enabled) history.detailed else false + val accelMode = if (history is Ble.Accelerometer.HistorySettings.Enabled) history.mode else state.accelViewMode + val accelScale = if (history is Ble.Accelerometer.HistorySettings.Enabled) history.scale else state.accelScale Column( modifier = Modifier diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/IntervalEdit.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/IntervalEdit.kt index e58a4c8..20eafe7 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/IntervalEdit.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/IntervalEdit.kt @@ -1,5 +1,6 @@ package llc.arma.ble.app.ui.screen.inspection.accelerometer.view +import android.util.Log import androidx.compose.animation.* import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape @@ -26,15 +27,17 @@ fun IntervalEdit( } val maxInterval = 10 * 24 * 60 * 60 * 1000 + val minInterval = 10_000 if(value > maxInterval){ value = maxInterval } - if(value < 1){ - value = 1 * 60 * 1000 + if(value < minInterval){ + value = minInterval } + val maxSeconds = maxInterval / millisInSecond val maxMinutes = maxInterval / millisInMinute val maxHours = maxInterval / millisInHour val maxDays = maxInterval / millisInDay @@ -42,6 +45,7 @@ fun IntervalEdit( val dayValue = value / millisInDay val hourValue = (value - (dayValue * millisInDay)) / millisInHour val minutesValue = (value - (dayValue * millisInDay) - (hourValue * millisInHour)) / millisInMinute + val secondsValue = (value - (dayValue * millisInDay) - (hourValue * millisInHour) - (minutesValue * millisInMinute)) / millisInSecond Column( modifier = Modifier @@ -65,13 +69,13 @@ fun IntervalEdit( range = -1..maxDays, value = dayValue, onValueChanged = { - value = (it * millisInDay) + (hourValue * millisInHour) + (minutesValue * millisInMinute) + value = (it * millisInDay) + (hourValue * millisInHour) + (minutesValue * millisInMinute) + (secondsValue * millisInSecond) } ) Spacer(modifier = Modifier.width(8.dp)) - Text(text = "Дни") + Text(text = "Д.") Spacer(modifier = Modifier.width(16.dp)) @@ -79,13 +83,13 @@ fun IntervalEdit( range = -1..maxHours, value = hourValue, onValueChanged = { - value = (it * millisInHour) + (dayValue * millisInDay) + (minutesValue * millisInMinute) + value = (it * millisInHour) + (dayValue * millisInDay) + (minutesValue * millisInMinute) + (secondsValue * millisInSecond) } ) Spacer(modifier = Modifier.width(8.dp)) - Text(text = "Часы") + Text(text = "Ч.") Spacer(modifier = Modifier.width(16.dp)) @@ -93,13 +97,27 @@ fun IntervalEdit( range = -1..maxMinutes, value = minutesValue, onValueChanged = { - value = (it * millisInMinute) + (dayValue * millisInDay) + (hourValue * millisInHour) + value = (secondsValue * millisInSecond) + (it * millisInMinute) + (dayValue * millisInDay) + (hourValue * millisInHour) } ) Spacer(modifier = Modifier.width(8.dp)) - Text(text = "Минуты") + Text(text = "М.") + + Spacer(modifier = Modifier.width(16.dp)) + + NumberPicker( + range = -1..maxSeconds, + value = secondsValue, + onValueChanged = { + value = (it * millisInSecond) + (minutesValue * millisInMinute) + (dayValue * millisInDay) + (hourValue * millisInHour) + } + ) + + Spacer(modifier = Modifier.width(8.dp)) + + Text(text = "С.") } @@ -138,7 +156,8 @@ fun IntervalEdit( } -const val millisInMinute = 1000 * 60 +const val millisInSecond = 1000 +const val millisInMinute = millisInSecond * 60 const val millisInHour = millisInMinute * 60 const val millisInDay = millisInHour * 24 diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/Write.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/Write.kt index c8f88bd..287f519 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/Write.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/Write.kt @@ -10,17 +10,14 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp -import kotlinx.coroutines.launch import llc.arma.ble.R import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract -import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract import llc.arma.ble.app.ui.screen.inspection.thermometer.localizedName import llc.arma.ble.domain.model.Ble @@ -45,7 +42,7 @@ fun Write( when (state) { is AccelerometerContract.State.Display.WriteState.DisplayPreview -> { - if(state.writeRequest.tx != null || state.writeRequest.saveHistory != null || state.writeRequest.historyInterval != null) { + if(state.writeRequest.tx != null || state.writeRequest.saveHistorySettings != null || state.writeRequest.historyInterval != null) { state.writeRequest.tx?.let { Box( @@ -82,7 +79,7 @@ fun Write( } } - state.writeRequest.saveHistory?.let { + state.writeRequest.saveHistorySettings?.let { Box( modifier = Modifier.padding( @@ -109,8 +106,8 @@ fun Write( color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.bodyMedium, text = when(it){ - Ble.Accelerometer.History.Disabled -> "Выключено" - is Ble.Accelerometer.History.Enabled -> "Включено" + Ble.Accelerometer.HistorySettings.Disabled -> "Выключено" + is Ble.Accelerometer.HistorySettings.Enabled -> "Включено" } ) @@ -142,17 +139,18 @@ fun Write( modifier = Modifier.weight(1f) ) { - val hours = it / 1000 / 60 / 60 - val minutes = (it - ( hours * 1000 * 60 * 60 )) / 1000 / 60 - Text( text = "Интервал измерений" ) + val hours = it / millisInHour + val minutes = (it - (hours * millisInHour)) / millisInMinute + val seconds = (it - (hours * millisInHour) - (minutes * millisInMinute)) / millisInSecond + Text( color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.bodyMedium, - text = "$hours ч. $minutes мин." + text = "$hours ч. $minutes мин. $seconds сек." ) } diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/thermometer/ThermometerScreen.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/thermometer/ThermometerScreen.kt index da5ccc7..c9a71c3 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/thermometer/ThermometerScreen.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/thermometer/ThermometerScreen.kt @@ -39,15 +39,15 @@ val Boolean.localizedName: String val Ble.BleState.TX.localizedName: String get() { return when(this){ - Ble.BleState.TX.MINUS_40 -> -40 - Ble.BleState.TX.MINUS_20 -> -20 - Ble.BleState.TX.MINUS_16 -> -16 - Ble.BleState.TX.MINUS_12 -> -12 - Ble.BleState.TX.MINUS_8 -> -8 - Ble.BleState.TX.MINUS_4 -> -4 - Ble.BleState.TX.ZERO -> 0 - Ble.BleState.TX.PLUS_3 -> 3 - Ble.BleState.TX.PLUS_4 -> 4 + Ble.BleState.TX.MINUS_40 -> "-40" + Ble.BleState.TX.MINUS_20 -> "-20" + Ble.BleState.TX.MINUS_16 -> "-16" + Ble.BleState.TX.MINUS_12 -> "-12" + Ble.BleState.TX.MINUS_8 -> "-8" + Ble.BleState.TX.MINUS_4 -> "-4" + Ble.BleState.TX.ZERO -> "0" + Ble.BleState.TX.PLUS_3 -> "3" + Ble.BleState.TX.PLUS_4 -> "4" }.toString() } diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/thermometer/view/TemperatureHistory.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/thermometer/view/TemperatureHistory.kt index cc72e9e..1713a3a 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/thermometer/view/TemperatureHistory.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/thermometer/view/TemperatureHistory.kt @@ -256,7 +256,7 @@ class TemperatureHistoryContract { sealed class State : ViewState { data class Display( - val loadingHistoryState : ProgressState> + val loadingHistoryState : ProgressState> ) : State() object Exception : State() diff --git a/app/src/main/java/llc/arma/ble/data/BleRepositoryImpl.kt b/app/src/main/java/llc/arma/ble/data/BleRepositoryImpl.kt index e92e17e..444ecbc 100644 --- a/app/src/main/java/llc/arma/ble/data/BleRepositoryImpl.kt +++ b/app/src/main/java/llc/arma/ble/data/BleRepositoryImpl.kt @@ -1,100 +1,53 @@ package llc.arma.ble.data import android.app.Application -import android.bluetooth.* -import android.bluetooth.le.ScanCallback -import android.bluetooth.le.ScanResult -import android.bluetooth.le.ScanSettings import android.os.SystemClock -import android.util.Log -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import llc.arma.ble.data.extensions.checkPermission +import llc.arma.ble.data.extensions.fromByte +import llc.arma.ble.data.extensions.get4byteUIntAt +import llc.arma.ble.data.extensions.info +import llc.arma.ble.data.extensions.sendData +import llc.arma.ble.data.extensions.toTemperature 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.model.BleInfo -import llc.arma.ble.domain.model.ConnectedBleInfo import llc.arma.ble.domain.repository.BleRepository import llc.arma.ble.domain.usecase.AccelScale import llc.arma.ble.domain.usecase.AccelViewMode -import llc.arma.ble.domain.usecase.AccelViewMode.* -import llc.arma.ble.domain.usecase.MeasureData import llc.arma.ble.domain.usecase.FftAxis import llc.arma.ble.domain.usecase.FftFrequency import llc.arma.ble.domain.usecase.FftViewMode -import no.nordicsemi.android.common.core.DataByteArray import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt -import no.nordicsemi.android.kotlin.ble.core.scanner.BleScanResult -import no.nordicsemi.android.kotlin.ble.scanner.aggregator.BleScanResultAggregator -import java.util.* +import no.nordicsemi.android.kotlin.ble.core.data.util.DataByteArray +import no.nordicsemi.android.kotlin.ble.core.scanner.BleNumOfMatches +import no.nordicsemi.android.kotlin.ble.core.scanner.BleScanMode +import no.nordicsemi.android.kotlin.ble.core.scanner.BleScannerCallbackType +import no.nordicsemi.android.kotlin.ble.core.scanner.BleScannerMatchMode +import no.nordicsemi.android.kotlin.ble.core.scanner.BleScannerSettings +import no.nordicsemi.android.kotlin.ble.scanner.BleScanner +import java.util.Collections +import java.util.Timer +import java.util.TimerTask +import java.util.UUID import javax.inject.Inject import javax.inject.Singleton import kotlin.math.PI import kotlin.math.atan -val FftFrequency.sendData: Byte - get() { - return when(this){ - FftFrequency.OFF -> 0 - FftFrequency.F_1 -> 1 - FftFrequency.F_10 -> 2 - FftFrequency.F_25 -> 3 - FftFrequency.F_50 -> 4 - FftFrequency.F_100 -> 5 - FftFrequency.F_200 -> 6 - FftFrequency.F_400 -> 7 - FftFrequency.F_1620 -> 8 - FftFrequency.F_1344 -> 9 - } - } - -val FftAxis.sendData: Byte - get() { - return when(this){ - FftAxis.AUTO -> 0 - FftAxis.X -> 1 - FftAxis.Y -> 2 - FftAxis.Z -> 3 - } - } - -val FftViewMode.sendData: Byte - get() { - return when(this){ - FftViewMode.SPECTRE -> 0 - FftViewMode.X -> 1 - FftViewMode.Y -> 2 - FftViewMode.Z -> 3 - } - } - -val AccelViewMode.sendData: Byte - get() { - return when(this){ - ACCELERATION -> 0 - PEAK_ACCELERATION -> 1 - RMS -> 2 - VIBRATION -> 3 - ANGLE -> 0 - ROTATIONS -> 4 - } - } - -val AccelScale.sendData: Byte - get() { - return when(this){ - AccelScale.S_2 -> 0 - AccelScale.S_4 -> 1 - AccelScale.S_8 -> 2 - AccelScale.S_16 -> 3 - } - } - val serviceUUID: UUID = UUID.fromString("a77db03a-9bc4-11ed-a8fc-0242ac120002") val accelerometerReadUUID: UUID = UUID.fromString("00002713-0000-1000-8000-00805f9b34fb") @@ -108,270 +61,62 @@ val passwordWriteUUID: UUID = UUID.fromString("a77db6f2-9bc4-11ed-a8fc-0242ac120 val txWriteUUID: UUID = UUID.fromString("00002a07-0000-1000-8000-00805f9b34fb") val flashWriteUUID: UUID = UUID.fromString("a77db6f2-9bc4-11ed-a8fc-0242ac120002") -@OptIn(ExperimentalUnsignedTypes::class) -fun UByteArray.toTemperature(): Float { - val uShort = (this[0] + this[1] * 256u).toUShort() - val result = if (uShort > Short.MAX_VALUE.toUShort()) { - ((uShort.inv() + 1u).toFloat().unaryMinus()) / 100f - } else { - uShort.toFloat() / 100f - } - return result -} - -fun ByteArray.get2byteUIntAt(idx: Int) = - ((this[idx + 1].toUInt() and 0xFFu) shl 8) or - (this[idx].toUInt() and 0xFFu) @Singleton 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 BleScanResult.timerEnabled: Boolean - get() { - return data?.scanRecord?.manufacturerSpecificData?.get(89)?.getByte(2) == 1.toByte() - } - - private val BleScanResult.info: BleInfo - get() { - this.device.name - return BleInfo( - name = this.device.name ?: "", - serial = device.address, - batteryLevel = batteryLevel ?: 0, - rssi = data?.rssi, - type = type, - scanTime = (data?.timestampNanos ?: 0) / 1_000_000, - tx = data?.txPower ?: 0, - recordEnabled = timerEnabled - ) - } - - private val ScanResult.info: BleInfo - get() { - return BleInfo( - name = scanRecord?.deviceName ?: "", - serial = device.address, - batteryLevel = batteryLevel ?: 0, - rssi = rssi, - type = type, - scanTime = timestampNanos / 1_000_000, - tx = scanRecord?.txPowerLevel ?: 0, - recordEnabled = timerEnabled - ) - } - - private val BleScanResult.batteryLevel: Int? - get() { - return data?.scanRecord?.manufacturerSpecificData?.get(89)?.getByte(1) - ?.toUByte()?.toInt() - } - - private val ScanResult.batteryLevel: Int? - get() { - return scanRecord?.manufacturerSpecificData?.get(89)?.get(1) - ?.toUByte()?.toInt() - } - - private val BleScanResult.type: BleInfo.Type - get() { - return when(data?.scanRecord?.manufacturerSpecificData?.get(89)?.getByte(0)?.toUByte()?.toInt()){ - 1 -> BleInfo.Type.BEACON - 2 -> BleInfo.Type.THERMOMETER - else -> BleInfo.Type.ACCELEROMETER - } - } - - private val ScanResult.type: BleInfo.Type - get() { - return when(scanRecord?.manufacturerSpecificData?.get(89)?.get(0)?.toUByte()?.toInt()){ - 1 -> BleInfo.Type.BEACON - 2 -> BleInfo.Type.THERMOMETER - else -> BleInfo.Type.ACCELEROMETER - } - } - - private fun ByteArray.getUIntAt(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 val deviceCache = mutableMapOf() val resultList: MutableMap = Collections.synchronizedMap(mutableMapOf()) - override fun getConnectedBle(): List { - - if(app.checkPermission()) { - - return app.getSystemService(BluetoothManager::class.java) - .getConnectedDevices(BluetoothProfile.GATT) - .filter { - it.name.contains("arma", true) - } - .map { - ConnectedBleInfo( - name = it.name, - serial = it.address - ) - } - - } else { - return emptyList() - } - - } - - private val aggregator = BleScanResultAggregator() - - override fun getBleAroundFlow(): Flow, BleException>> { - - /*return if(checkPermission()){ - - callbackFlow { - - BleScanner(app).scan( - settings = BleScannerSettings( - includeStoredBondedDevices = false, - scanMode = BleScanMode.SCAN_MODE_LOW_LATENCY, - callbackType = BleScannerCallbackType.CALLBACK_TYPE_ALL_MATCHES, - matchMode = BleScannerMatchMode.MATCH_MODE_AGGRESSIVE, - numOfMatches = BleNumOfMatches.MATCH_NUM_ONE_ADVERTISEMENT, - reportDelay = 0L - ), - - ).filter { it.device.name?.contains("ArmA") == true } - .onEach { aggregator.aggregateDevices(it) } - .onEach { - resultList[it.device.address] = it.info - //deviceCache[it.device.address] = it - }.launchIn(CoroutineScope(Dispatchers.IO)) - - val timer = Timer().apply { - schedule(object : TimerTask() { - override fun run() { - CoroutineScope(Dispatchers.IO).launch { - send(Result.success(resultList.values.toList())) - } - } - }, 500, 500) - } - - awaitClose { - timer.cancel() - } - - } - - } else { - - flow { emit(Result.failure(BleException.PermissionDenied)) } - - }*/ + override fun getBleAroundFlow(): Result>, BleException> { return if(app.checkPermission()){ - callbackFlow { + Result.success( - val bleCallback = object : ScanCallback() { + callbackFlow { - override fun onScanResult( - callbackType: Int, - result: ScanResult - ) { + val job = BleScanner(app) + .scan( + settings = BleScannerSettings( + includeStoredBondedDevices = false, + scanMode = BleScanMode.SCAN_MODE_LOW_LATENCY, + callbackType = BleScannerCallbackType.CALLBACK_TYPE_ALL_MATCHES, + matchMode = BleScannerMatchMode.MATCH_MODE_AGGRESSIVE, + numOfMatches = BleNumOfMatches.MATCH_NUM_ONE_ADVERTISEMENT, + reportDelay = 0L + ) + ).filter { it.device.name?.contains("ArmA") == true } + .onEach { + resultList[it.device.address] = it.info + }.launchIn(CoroutineScope(Dispatchers.IO)) - super.onScanResult(callbackType, result) - - if (app.checkPermission()) { - - if (result.scanRecord?.deviceName?.contains("ArmA") == true) { - - resultList[result.device.address] = result.info - - deviceCache[result.device.address] = result - - } - - } else { - CoroutineScope(Dispatchers.IO).launch { - send( - Result.failure(BleException.PermissionDenied) - ) - } - } - - } - - override fun onBatchScanResults(results: MutableList) { - super.onBatchScanResults(results) - - if (app.checkPermission()) { - - results.forEach { result -> - - if (result.scanRecord?.deviceName?.contains("ArmA") == true) { - - resultList[result.device.address] = result.info - - deviceCache[result.device.address] = result + val timer = Timer().apply { + schedule(object : TimerTask() { + override fun run() { + CoroutineScope(Dispatchers.IO).launch { + send(resultList.values.toList()) } - } - - } else { - CoroutineScope(Dispatchers.IO).launch { - send( - Result.failure(BleException.PermissionDenied) - ) - } - } - + }, 500, 500) } + + awaitClose { + job.cancel() + timer.cancel() + } + } - val bleScanner = - app.getSystemService(BluetoothManager::class.java).adapter.bluetoothLeScanner - - bleScanner.startScan( - listOf(), - ScanSettings.Builder() - .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) - .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) - .setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE) - .setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT) - .setReportDelay(0L) - .build(), - bleCallback) - - val timer = Timer().apply { - schedule(object : TimerTask() { - override fun run() { - CoroutineScope(Dispatchers.IO).launch { - send(Result.success(resultList.values.toList())) - } - } - }, 500, 500) - } - - awaitClose { - bleScanner.stopScan(bleCallback) - timer.cancel() - } - - } + ) } else { - flow { emit(Result.failure(BleException.PermissionDenied)) } + Result.failure(BleException.PermissionDenied) } @@ -381,12 +126,15 @@ class BleRepositoryImpl @Inject constructor( serial: String ): Result, BleException> { - deviceCache[serial]?.let { result -> + resultList[serial]?.let { result -> - return when(result.info.type) { + return when(result.type) { BleInfo.Type.ACCELEROMETER -> { - val tState = readAccelState(result).fold( + val tState = readAccelState( + result.serial, + result.recordEnabled + ).fold( onFailure = { return Result.failure(it) }, @@ -400,30 +148,21 @@ class BleRepositoryImpl @Inject constructor( while (true) { - deviceCache[serial]?.let { newResult -> + resultList[serial]?.let { newResult -> emit( Ble.Accelerometer( - info = newResult.info.copy( - rssi = if((SystemClock.elapsedRealtimeNanos() - newResult.timestampNanos) > 15_000_000_000) { + info = newResult.copy( + rssi = if((SystemClock.elapsedRealtimeNanos() - newResult.scanTime) > 15_000_000_000) { null } else { newResult.rssi } ), state = Ble.BleState( - tx = when (result.scanRecord?.txPowerLevel) { - -40 -> Ble.BleState.TX.MINUS_40 - -20 -> Ble.BleState.TX.MINUS_20 - -16 -> Ble.BleState.TX.MINUS_16 - -12 -> Ble.BleState.TX.MINUS_12 - -8 -> Ble.BleState.TX.MINUS_8 - -4 -> Ble.BleState.TX.MINUS_4 - 3 -> Ble.BleState.TX.PLUS_3 - 4 -> Ble.BleState.TX.PLUS_4 - else -> Ble.BleState.TX.ZERO - } + tx = Ble.BleState.TX.fromByte(result.tx.toByte()) + ?: Ble.BleState.TX.ZERO ), accelerometerState = tState ) @@ -445,27 +184,18 @@ class BleRepositoryImpl @Inject constructor( while (true) { - deviceCache[serial]?.let { newResult -> + resultList[serial]?.let { newResult -> val state = Ble.BleState( - tx = when (result.scanRecord?.txPowerLevel) { - -40 -> Ble.BleState.TX.MINUS_40 - -20 -> Ble.BleState.TX.MINUS_20 - -16 -> Ble.BleState.TX.MINUS_16 - -12 -> Ble.BleState.TX.MINUS_12 - -8 -> Ble.BleState.TX.MINUS_8 - -4 -> Ble.BleState.TX.MINUS_4 - 3 -> Ble.BleState.TX.PLUS_3 - 4 -> Ble.BleState.TX.PLUS_4 - else -> Ble.BleState.TX.ZERO - } + tx = Ble.BleState.TX.fromByte(result.tx.toByte()) + ?: Ble.BleState.TX.ZERO ) emit( Ble.Beacon( - info = newResult.info.copy( - rssi = if((SystemClock.elapsedRealtimeNanos() - newResult.timestampNanos) > 15_000_000_000) { + info = newResult.copy( + rssi = if((SystemClock.elapsedRealtimeNanos() - newResult.scanTime) > 15_000_000_000) { null } else { newResult.rssi @@ -488,7 +218,7 @@ class BleRepositoryImpl @Inject constructor( } BleInfo.Type.THERMOMETER -> { - val tState = readThermometerState(result).fold( + val tState = readThermometerState(result.serial, result.recordEnabled).fold( onFailure = { return Result.failure(it) }, @@ -502,27 +232,18 @@ class BleRepositoryImpl @Inject constructor( while (true) { - deviceCache[serial]?.let { newResult -> + resultList[serial]?.let { newResult -> val state = Ble.BleState( - tx = when (result.scanRecord?.txPowerLevel) { - -40 -> Ble.BleState.TX.MINUS_40 - -20 -> Ble.BleState.TX.MINUS_20 - -16 -> Ble.BleState.TX.MINUS_16 - -12 -> Ble.BleState.TX.MINUS_12 - -8 -> Ble.BleState.TX.MINUS_8 - -4 -> Ble.BleState.TX.MINUS_4 - 3 -> Ble.BleState.TX.PLUS_3 - 4 -> Ble.BleState.TX.PLUS_4 - else -> Ble.BleState.TX.ZERO - } + tx = Ble.BleState.TX.fromByte(result.tx.toByte()) + ?: Ble.BleState.TX.ZERO ) emit( Ble.Thermometer( - info = newResult.info.copy( - rssi = if((SystemClock.elapsedRealtimeNanos() - newResult.timestampNanos) > 15_000_000_000) { + info = newResult.copy( + rssi = if((SystemClock.elapsedRealtimeNanos() - newResult.scanTime) > 15_000_000_000) { null } else { newResult.rssi @@ -555,15 +276,16 @@ class BleRepositoryImpl @Inject constructor( @OptIn(ExperimentalUnsignedTypes::class) private suspend fun readThermometerState( - record: ScanResult + address: String, + timer: Boolean ): Result { return if(app.checkPermission()) { - try { + val connection = + ClientBleGatt.connect(app, address, CoroutineScope(Dispatchers.IO)) - val connection = - ClientBleGatt.connect(app, record.device.address, CoroutineScope(Dispatchers.IO)) + try { val service = connection.discoverServices() .findService(serviceUUID) ?: return Result.failure(BleException.UnexpectedResponse) @@ -582,7 +304,7 @@ class BleRepositoryImpl @Inject constructor( val interval = characteristic.read().value.let { if(it.size == 4){ - it.getUIntAt(0).toLong() + it.get4byteUIntAt(0).toLong() }else{ 0 } @@ -593,7 +315,7 @@ class BleRepositoryImpl @Inject constructor( return Result.success( Ble.Thermometer.ThermometerState( temperature = temperature, - saveHistory = record.timerEnabled, + saveHistory = timer, historyInterval = interval ) ) @@ -601,6 +323,10 @@ class BleRepositoryImpl @Inject constructor( } catch (err: Throwable){ err.printStackTrace() return Result.failure(BleException.UnexpectedResponse) + } finally { + + connection.close() + } } else { @@ -612,15 +338,16 @@ class BleRepositoryImpl @Inject constructor( } private suspend fun readAccelState( - record: ScanResult + address: String, + timer: Boolean ): Result { return if(app.checkPermission()) { - try { + val connection = + ClientBleGatt.connect(app, address, CoroutineScope(Dispatchers.IO)) - val connection = - ClientBleGatt.connect(app, record.device.address, CoroutineScope(Dispatchers.IO)) + try { val service = connection.discoverServices() .findService(serviceUUID) ?: return Result.failure(BleException.UnexpectedResponse) @@ -632,13 +359,13 @@ class BleRepositoryImpl @Inject constructor( val interval = characteristic.read().value.let { if(it.size == 4){ - it.getUIntAt(0).toLong() + it.get4byteUIntAt(0).toLong() }else{ 0 } } - val historyParams = when(record.timerEnabled){ + val historySettingsParams = when(timer){ true -> { characteristic = service.findCharacteristic(accelerometerReadUUID) @@ -649,40 +376,24 @@ class BleRepositoryImpl @Inject constructor( val data = it.value - val scale = when(data[1].toInt()){ - 0 -> AccelScale.S_2 - 1 -> AccelScale.S_4 - 2 -> AccelScale.S_8 - 3 -> AccelScale.S_16 - else -> { - return Result.failure(BleException.UnexpectedResponse) - } - } - val mode = when(data[0].toInt()){ - 0 -> ACCELERATION - 1 -> PEAK_ACCELERATION - 2 -> RMS - 3 -> VIBRATION - 4 -> ANGLE - else -> { - return Result.failure(BleException.UnexpectedResponse) - } - } - Ble.Accelerometer.History.Enabled( + val scale = AccelScale.fromByte(data[1]) ?: return Result.failure(BleException.UnexpectedResponse) + val mode = AccelViewMode.fromByte(data[0]) ?: return Result.failure(BleException.UnexpectedResponse) + + Ble.Accelerometer.HistorySettings.Enabled( scale = scale, mode = mode, - detailed = false //TODO + detailed = false ) } } - false -> Ble.Accelerometer.History.Disabled + false -> Ble.Accelerometer.HistorySettings.Disabled } connection.close() return Result.success( Ble.Accelerometer.AccelerometerState( - saveHistory = historyParams, + saveHistorySettings = historySettingsParams, historyInterval = interval ) ) @@ -691,6 +402,10 @@ class BleRepositoryImpl @Inject constructor( return Result.failure(BleException.UnexpectedResponse) + } finally { + + connection.close() + } } else { @@ -708,158 +423,25 @@ class BleRepositoryImpl @Inject constructor( fftAxis: FftAxis, fftMode: FftViewMode, frequency: FftFrequency - ): Flow>, BleException>> { + ): Flow>, BleException>> { - //return readAccelerometerSpectre(serial, app, accelScale, accelMode, fftAxis, fftMode, frequency) - - var gatt: BluetoothGatt? = null - - return callbackFlow { - - deviceCache[serial]?.device?.let { - - if (app.checkPermission()) { - - gatt = it.connectGatt(app, false, ReadAccelerometerSpectreCallback( - app, accelScale, accelMode, fftAxis, fftMode, frequency - ) { - CoroutineScope(Dispatchers.IO).launch { - send(it) - } - }) - - } else { - - CoroutineScope(Dispatchers.IO).launch { - send(Result.failure(BleException.PermissionDenied)) - } - - return@callbackFlow - - } - - } - - awaitClose { - Log.d("measure", "close") - gatt?.close() - } - - } + return readAccelerometerSpectre(serial, app, accelScale, accelMode, fftAxis, fftMode, frequency) } override suspend fun getTemperatureHistoryBySerial( serial: String - ): Flow>, BleException>> { + ): Flow>, BleException>> { return readThermometerHistory(serial, app) - /*var gatt: BluetoothGatt? = null - - return callbackFlow { - - deviceCache[serial]?.device?.let { - - if (app.checkPermission()) { - - gatt = it.connectGatt(app, false, ReadTemperatureHistoryCallback(app) { - CoroutineScope(Dispatchers.IO).launch { - send(it) - } - }) - - } else { - - CoroutineScope(Dispatchers.IO).launch { - send(Result.failure(BleException.PermissionDenied)) - } - - return@callbackFlow - - } - - } - - awaitClose { - gatt?.close() - } - - }*/ - } override suspend fun getAccelerometerHistoryBySerial( serial: String ): Flow>, BleException>> { - return flow { - - if (app.checkPermission()) { - - try { - - var scale: AccelScale? = null - var mode: AccelViewMode? = null - - val connection = ClientBleGatt.connect( - app, - serial, - CoroutineScope(Dispatchers.Default) - ) - - val characteristic = connection.discoverServices() - .findService(serviceUUID) - ?.findCharacteristic(accelerometerReadUUID) - - characteristic?.write(DataByteArray.from(4)) - characteristic?.read()?.let { - val data = it.value - scale = when (data[1].toInt()) { - 0 -> AccelScale.S_2 - 1 -> AccelScale.S_4 - 2 -> AccelScale.S_8 - 3 -> AccelScale.S_16 - else -> null - } - mode = when (data[0].toInt()) { - 0 -> ACCELERATION - 1 -> PEAK_ACCELERATION - 2 -> RMS - 3 -> VIBRATION - 4 -> ANGLE - else -> null - } - } - - if (scale != null && mode != null) { - - readAccelerometerHistory(serial, mode!!, scale!!, app).collect { - - emit(it) - - } - - } else { - - emit(Result.failure(BleException.UnexpectedResponse)) - - } - - } catch (err: Throwable) { - err.printStackTrace() - - emit(Result.failure(BleException.UnexpectedResponse)) - - } - - } else { - - emit(Result.failure(BleException.PermissionDenied)) - - } - - } + return getAccelerometerHistory(serial, app) } @@ -875,28 +457,17 @@ class BleRepositoryImpl @Inject constructor( return if(app.checkPermission()) { + val connection = + ClientBleGatt.connect(app, serial, CoroutineScope(Dispatchers.Default)) + try { - val connection = - ClientBleGatt.connect(app, serial, CoroutineScope(Dispatchers.Default)) val services = connection.discoverServices() val service = services.findService(serviceUUID) ?: return Result.failure(BleException.UnexpectedResponse) request.tx?.let { service.findCharacteristic(txWriteUUID)!!.write( - DataByteArray.from( - when (it) { - Ble.BleState.TX.MINUS_40 -> -40 - Ble.BleState.TX.MINUS_20 -> -20 - Ble.BleState.TX.MINUS_16 -> -16 - Ble.BleState.TX.MINUS_12 -> -12 - Ble.BleState.TX.MINUS_8 -> -8 - Ble.BleState.TX.MINUS_4 -> -4 - Ble.BleState.TX.ZERO -> 0 - Ble.BleState.TX.PLUS_3 -> 3 - Ble.BleState.TX.PLUS_4 -> 4 - } - ) + DataByteArray.from(it.sendData) ) } @@ -933,6 +504,10 @@ class BleRepositoryImpl @Inject constructor( err.printStackTrace() Result.failure(BleException.UnexpectedResponse) + } finally { + + connection.close() + } } else { @@ -950,31 +525,19 @@ class BleRepositoryImpl @Inject constructor( return if(app.checkPermission()) { - try { + val connection = ClientBleGatt.connect(app, serial, CoroutineScope(Dispatchers.Default)) - val connect = ClientBleGatt.connect(app, serial, scope = CoroutineScope(Dispatchers.Default)) + try { request.tx?.let { - connect.discoverServices().findService(serviceUUID)?.findCharacteristic(txWriteUUID)?.write( - DataByteArray.from( - when (it) { - Ble.BleState.TX.MINUS_40 -> -40 - Ble.BleState.TX.MINUS_20 -> -20 - Ble.BleState.TX.MINUS_16 -> -16 - Ble.BleState.TX.MINUS_12 -> -12 - Ble.BleState.TX.MINUS_8 -> -8 - Ble.BleState.TX.MINUS_4 -> -4 - Ble.BleState.TX.ZERO -> 0 - Ble.BleState.TX.PLUS_3 -> 3 - Ble.BleState.TX.PLUS_4 -> 4 - } - ) + connection.discoverServices().findService(serviceUUID)?.findCharacteristic(txWriteUUID)?.write( + DataByteArray.from(it.sendData) ) ?: return Result.failure(BleException.UnexpectedResponse) } - connect.close() + connection.close() Result.success(Unit) @@ -983,6 +546,10 @@ class BleRepositoryImpl @Inject constructor( err.printStackTrace() Result.failure(BleException.UnexpectedResponse) + } finally { + + connection.close() + } } else { @@ -1005,10 +572,11 @@ class BleRepositoryImpl @Inject constructor( return if(app.checkPermission()) { + val connection = + ClientBleGatt.connect(app, serial, CoroutineScope(Dispatchers.Default)) + try { - val connection = - ClientBleGatt.connect(app, serial, CoroutineScope(Dispatchers.Default)) val services = connection.discoverServices() val service = services.findService(serviceUUID) ?: return Result.failure( BleException.UnexpectedResponse @@ -1016,29 +584,17 @@ class BleRepositoryImpl @Inject constructor( request.tx?.let { service.findCharacteristic(txWriteUUID)!!.write( - DataByteArray.from( - when (it) { - Ble.BleState.TX.MINUS_40 -> -40 - Ble.BleState.TX.MINUS_20 -> -20 - Ble.BleState.TX.MINUS_16 -> -16 - Ble.BleState.TX.MINUS_12 -> -12 - Ble.BleState.TX.MINUS_8 -> -8 - Ble.BleState.TX.MINUS_4 -> -4 - Ble.BleState.TX.ZERO -> 0 - Ble.BleState.TX.PLUS_3 -> 3 - Ble.BleState.TX.PLUS_4 -> 4 - } - ) + DataByteArray.from(it.sendData) ) } - request.saveHistory?.let { + request.saveHistorySettings?.let { service.findCharacteristic(saveEnabledWriteUUID)!!.write( DataByteArray.from( *mutableListOf(4).apply { - add(if (it is Ble.Accelerometer.History.Enabled) 1 else 0) - if (it is Ble.Accelerometer.History.Enabled) { + add(if (it is Ble.Accelerometer.HistorySettings.Enabled) 1 else 0) + if (it is Ble.Accelerometer.HistorySettings.Enabled) { add(it.mode.sendData) add(it.scale.sendData) } @@ -1071,6 +627,10 @@ class BleRepositoryImpl @Inject constructor( err.printStackTrace() Result.failure(BleException.UnexpectedResponse) + } finally { + + connection.close() + } } else { @@ -1088,10 +648,11 @@ class BleRepositoryImpl @Inject constructor( return if(app.checkPermission()) { + val connection = + ClientBleGatt.connect(app, serial, CoroutineScope(Dispatchers.Default)) + try { - val connection = - ClientBleGatt.connect(app, serial, CoroutineScope(Dispatchers.Default)) val services = connection.discoverServices() val service = services.findService(serviceUUID) ?: return Result.failure(BleException.UnexpectedResponse) @@ -1113,6 +674,10 @@ class BleRepositoryImpl @Inject constructor( err.printStackTrace() Result.failure(BleException.UnexpectedResponse) + } finally { + + connection.close() + } } else { @@ -1128,181 +693,34 @@ class BleRepositoryImpl @Inject constructor( fftAxis: FftAxis, fftMode: FftViewMode, frequency: FftFrequency, - ): Flow> { - - return if (app.checkPermission()) { - - flow { - - try { - - val connection = - ClientBleGatt.connect(app, serial, CoroutineScope(Dispatchers.Default)) - - val services = connection.discoverServices() - - val characteristic = - services.findService(serviceUUID) - ?.findCharacteristic(accelerometerReadUUID) - - val payload = DataByteArray.from( - 4, - accelMode.sendData, - accelScale.sendData, - fftMode.sendData, - fftAxis.sendData, - frequency.sendData, - 1 - ) - - characteristic?.write(payload) - - characteristic?.getNotifications()?.collect { - - val value = it.value - - val result = if (accelMode == VIBRATION) { - - MeasureData.Vibration( - (value.get2byteShortAt() - .toFloat() * accelScale.k) / Short.MAX_VALUE - ) - - } else { - - val data = value.toList().chunked(2).map { - it.toByteArray().get2byteShortAt() - } - - Log.d( - "accel", - "x: ${data[0]} y: ${data[1]} z: ${data[2]} bytes: ${value.toUByteArray().joinToString { it.toString(16).padStart(2, '0') }}" - ) - - val x: Float - val y: Float - val z: Float - - when(accelMode){ - ANGLE -> { - x = calculateAngle( - data[2].toFloat(), - data[1].toFloat() - ) * 180f / Math.PI.toFloat() - y = calculateAngle( - data[2].toFloat(), - data[0].toFloat() - ) * 180f / Math.PI.toFloat() - z = calculateAngle( - data[0].toFloat(), - data[1].toFloat() - ) * 180f / Math.PI.toFloat() - } - ROTATIONS -> { - x = ((360f / 8f) * ((data[0] / 100f) + 1f)) - 45f - y = data[1].toFloat() - z = data[2].toFloat() - } - else -> { - x = (data[0].toFloat() * accelScale.k) / Short.MAX_VALUE - y = (data[1].toFloat() * accelScale.k) / Short.MAX_VALUE - z = (data[2].toFloat() * accelScale.k) / Short.MAX_VALUE - } - } - - /*if (accelMode == ANGLE) { - - x = calculateZAngle( - data[2].toFloat(), - data[1].toFloat() - ) * 180f / Math.PI.toFloat() - y = calculateZAngle( - data[2].toFloat(), - data[0].toFloat() - ) * 180f / Math.PI.toFloat() - z = calculateZAngle( - data[0].toFloat(), - data[1].toFloat() - ) * 180f / Math.PI.toFloat() - - } else { - - x = (data[0].toFloat() * accelScale.k) / Short.MAX_VALUE - y = (data[1].toFloat() * accelScale.k) / Short.MAX_VALUE - z = (data[2].toFloat() * accelScale.k) / Short.MAX_VALUE - - }*/ - - val state = MeasureData.Accelerate( - x = x, - y = y, - z = z - ) - - state - - - } - - emit(Result.success(result)) - - } - - connection.close() - - } catch (err: Exception) { - - err.printStackTrace() - - emit(Result.failure(BleException.UnexpectedResponse)) - - } - - } - - - } else { - - flow { } - - } + ): Flow> { + return getAccelerometerRealtimeData(app, serial, accelScale, accelMode, fftAxis, fftMode, frequency) } } fun calculateAngle( - x: Float, - y: Float + first: Float, + second: Float ): Float { - var x = x + var first = first - if(x == 0f && y == 0f){ - x = 0.0001f + if(first == 0f && second == 0f){ + first = 0.0001f } - if(x > 0){ - return atan(y/x) + val result = when { + first > 0 -> atan(second / first) + first < 0 && second >= 0 -> atan(second / first) + PI.toFloat() + first < 0 && second < 0 -> atan(second/first) - PI.toFloat() + first == 0f && second > 0 -> PI.toFloat() / 2f + first == 0f && second < 0 -> -PI.toFloat() / 2f + else -> 0f } - if(x < 0 && y >= 0){ - return atan(y/x) + PI.toFloat() - } - - if(x < 0 && y < 0){ - return atan(y/x) - PI.toFloat() - } - - if(x == 0f && y > 0){ - return PI.toFloat() / 2f - } - - if(x == 0f && y < 0){ - return -PI.toFloat() / 2f - } - - return 0f + return result * 180f / Math.PI.toFloat() } \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/data/EmailRepositoryImpl.kt b/app/src/main/java/llc/arma/ble/data/EmailRepositoryImpl.kt index fab9f76..6da8e60 100644 --- a/app/src/main/java/llc/arma/ble/data/EmailRepositoryImpl.kt +++ b/app/src/main/java/llc/arma/ble/data/EmailRepositoryImpl.kt @@ -3,20 +3,8 @@ package llc.arma.ble.data import android.app.Application import android.content.Intent import androidx.core.content.FileProvider -import llc.arma.ble.R import llc.arma.ble.domain.repository.EmailRepository -import llc.arma.ble.domain.repository.XlsxRepository -import llc.arma.ble.domain.usecase.MeasureData -import org.apache.poi.ss.SpreadsheetVersion -import org.apache.poi.ss.usermodel.WorkbookFactory -import org.apache.poi.ss.util.AreaReference -import org.apache.poi.ss.util.CellReference -import org.apache.poi.util.IOUtils -import org.apache.poi.xssf.usermodel.XSSFSheet import java.io.File -import java.io.FileInputStream -import java.io.FileOutputStream -import java.util.UUID import javax.inject.Inject class EmailRepositoryImpl @Inject constructor( diff --git a/app/src/main/java/llc/arma/ble/data/ReadAccelerometerHistory.kt b/app/src/main/java/llc/arma/ble/data/GetAccelerometerHistory.kt similarity index 69% rename from app/src/main/java/llc/arma/ble/data/ReadAccelerometerHistory.kt rename to app/src/main/java/llc/arma/ble/data/GetAccelerometerHistory.kt index e7905be..33f1c25 100644 --- a/app/src/main/java/llc/arma/ble/data/ReadAccelerometerHistory.kt +++ b/app/src/main/java/llc/arma/ble/data/GetAccelerometerHistory.kt @@ -10,56 +10,36 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow +import llc.arma.ble.data.extensions.checkPermission +import llc.arma.ble.data.extensions.fromByte +import llc.arma.ble.data.extensions.get2byteShortAt +import llc.arma.ble.data.extensions.get2byteUIntAt +import llc.arma.ble.data.extensions.get4byteUIntAt 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.usecase.AccelScale import llc.arma.ble.domain.usecase.AccelViewMode -import no.nordicsemi.android.common.core.DataByteArray import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt +import no.nordicsemi.android.kotlin.ble.core.data.util.DataByteArray -fun Application.checkPermission(): Boolean { - - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) == - PackageManager.PERMISSION_GRANTED && - ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) == - PackageManager.PERMISSION_GRANTED - } else { - return ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == - PackageManager.PERMISSION_GRANTED && - ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == - PackageManager.PERMISSION_GRANTED - } -} - -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) - -/*fun ByteArray.get2byteUIntAt(idx: Int) = - ((this[idx + 1].toUInt() and 0xFFu) shl 8) or - (this[idx].toUInt() and 0xFFu)*/ @OptIn(ExperimentalUnsignedTypes::class) -fun readAccelerometerHistory( +fun getAccelerometerHistory( address: String, - mode: AccelViewMode, - scale: AccelScale, app: Application, ): Flow>, BleException>> { return flow { + var lastMeasureSystemTime: Long? = null var bleMeasureInterval: Long? = null var bleRealTime: Long? = null var bleLastMeasureTime: Long? = null - val resultTemperaturePackage: MutableList = mutableListOf() + val resultPackage: MutableList = mutableListOf() val result = mutableListOf>() @@ -67,10 +47,21 @@ fun readAccelerometerHistory( if(app.checkPermission()) { + val connection = + ClientBleGatt.connect(app, address, CoroutineScope(Dispatchers.Default)) + try { - val connection = - ClientBleGatt.connect(app, address, CoroutineScope(Dispatchers.Default)) + val specData = connection.discoverServices() + .findService(serviceUUID) + ?.findCharacteristic(accelerometerReadUUID) + ?.let { + it.write(DataByteArray.from(4)) + it.read() + } ?: throw IllegalStateException() + + val scale = AccelScale.fromByte(specData.value[1]) ?: throw IllegalStateException() + val mode = AccelViewMode.fromByte(specData.value[0]) ?: throw IllegalStateException() val characteristic = connection.discoverServices() .findService(serviceUUID) @@ -78,7 +69,6 @@ fun readAccelerometerHistory( if(characteristic != null) { - characteristic.write(DataByteArray.from(2)) var value = characteristic.read().value @@ -89,7 +79,7 @@ fun readAccelerometerHistory( } else { - Log.d("expected data size", value.get2byteUIntAt(0).toString()) + var nextPackageDataCount = value.get2byteUIntAt(0) val writeData = mutableListOf( 1.toByte(), @@ -101,7 +91,7 @@ fun readAccelerometerHistory( characteristic.write(DataByteArray(writeData)) value = characteristic.read().value - var nextPackageDataCount = value.get2byteUIntAt(2) + while (nextPackageDataCount.toInt() != 0) { @@ -112,7 +102,7 @@ fun readAccelerometerHistory( bleRealTime = value.get4byteUIntAt(12).toLong() lastMeasureSystemTime = - System.currentTimeMillis() - ((bleRealTime!! - bleLastMeasureTime!!) * 1_000) + System.currentTimeMillis() - ((bleRealTime - bleLastMeasureTime) * 1_000) value.toUByteArray().asList().subList(16, value.size) @@ -125,23 +115,17 @@ fun readAccelerometerHistory( result.add(value.toUByteArray().toList()) nextPackageDataCount = value.get2byteUIntAt(2) - resultTemperaturePackage.addAll( + resultPackage.addAll( temperatureDataArray.chunked(2).map { it.toUByteArray().toByteArray().get2byteShortAt().toFloat() }.toMutableList() ) - Log.d( - "received data size", - (temperatureDataArray.chunked(2).size).toString() - ) - Log.d("next data size", nextPackageDataCount.toString()) - expectedDataSize = - nextPackageDataCount.toInt() + resultTemperaturePackage.size + nextPackageDataCount.toInt() + resultPackage.size emit(Result.success(ProgressState.Progress(0f / expectedDataSize.toFloat()))) - emit(Result.success(ProgressState.Progress(resultTemperaturePackage.size.toFloat() / expectedDataSize.toFloat()))) + emit(Result.success(ProgressState.Progress(resultPackage.size.toFloat() / expectedDataSize.toFloat()))) characteristic.write(DataByteArray.from(5)) value = characteristic.read().value @@ -152,13 +136,20 @@ fun readAccelerometerHistory( Result.success( ProgressState.Finished( when (mode) { - AccelViewMode.ROTATIONS, + AccelViewMode.ROTATIONS -> { + resultPackage.withIndex().map { + Ble.Accelerometer.HistoryPoint.Rotation( + date = lastMeasureSystemTime!! - (((resultPackage.size - 1) - it.index) * bleMeasureInterval!!), + value = it.value.toLong() + ) + } + } AccelViewMode.ACCELERATION, AccelViewMode.PEAK_ACCELERATION, AccelViewMode.RMS -> { - resultTemperaturePackage.chunked(3).withIndex().map { + resultPackage.chunked(3).withIndex().map { Ble.Accelerometer.HistoryPoint.Angle( - date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), + date = lastMeasureSystemTime!! - (((resultPackage.size - 1) - it.index) * bleMeasureInterval!!), x = (it.value[0] * scale.k) / Short.MAX_VALUE, y = (it.value[1] * scale.k) / Short.MAX_VALUE, z = (it.value[2] * scale.k) / Short.MAX_VALUE @@ -167,29 +158,29 @@ fun readAccelerometerHistory( } AccelViewMode.ANGLE -> { - resultTemperaturePackage.chunked(3).withIndex().map { + resultPackage.chunked(3).withIndex().map { Ble.Accelerometer.HistoryPoint.Angle( - date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), + date = lastMeasureSystemTime!! - (((resultPackage.size - 1) - it.index) * bleMeasureInterval!!), x = calculateAngle( it.value[2], it.value[1] - ) * 180f / Math.PI.toFloat(), + ), y = calculateAngle( it.value[2], it.value[0] - ) * 180f / Math.PI.toFloat(), + ), z = calculateAngle( it.value[0], it.value[1] - ) * 180f / Math.PI.toFloat() + ) ) } } AccelViewMode.VIBRATION -> { - resultTemperaturePackage.withIndex().map { + resultPackage.withIndex().map { Ble.Accelerometer.HistoryPoint.Vibration( - date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), + date = lastMeasureSystemTime!! - (((resultPackage.size - 1) - it.index) * bleMeasureInterval!!), value = (it.value * scale.k) / Short.MAX_VALUE ) } @@ -210,13 +201,16 @@ fun readAccelerometerHistory( } } catch (err: Throwable) { - + err.printStackTrace() emit(Result.failure(BleException.UnexpectedResponse)) + } finally { + + connection.close() + } } else { - emit(Result.failure(BleException.PermissionDenied)) } diff --git a/app/src/main/java/llc/arma/ble/data/GetAccelerometerRealtimeData.kt b/app/src/main/java/llc/arma/ble/data/GetAccelerometerRealtimeData.kt new file mode 100644 index 0000000..e8d68b1 --- /dev/null +++ b/app/src/main/java/llc/arma/ble/data/GetAccelerometerRealtimeData.kt @@ -0,0 +1,136 @@ +package llc.arma.ble.data + +import android.app.Application +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import llc.arma.ble.data.extensions.checkPermission +import llc.arma.ble.data.extensions.get2byteShortAt +import llc.arma.ble.data.extensions.sendData +import llc.arma.ble.domain.Result +import llc.arma.ble.domain.common.BleException +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.AccelViewMode.* +import llc.arma.ble.domain.usecase.FftAxis +import llc.arma.ble.domain.usecase.FftFrequency +import llc.arma.ble.domain.usecase.FftViewMode +import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt +import no.nordicsemi.android.kotlin.ble.core.data.util.DataByteArray + +fun getAccelerometerRealtimeData( + app: Application, + serial: String, + accelScale: AccelScale, + accelMode: AccelViewMode, + fftAxis: FftAxis, + fftMode: FftViewMode, + frequency: FftFrequency, +): Flow> { + + return flow { + + if(app.checkPermission()) { + + val connection = + ClientBleGatt.connect(app, serial, CoroutineScope(Dispatchers.Default)) + + try { + + val services = connection.discoverServices() + + val characteristic = + services.findService(serviceUUID) + ?.findCharacteristic(accelerometerReadUUID) + ?: throw IllegalStateException() + + characteristic.write( + DataByteArray.from( + 4, + accelMode.sendData, + accelScale.sendData, + fftMode.sendData, + fftAxis.sendData, + frequency.sendData, + 1 + ) + ) + + characteristic.getNotifications().collect { + + val value = it.value + + val data = value.toList().chunked(2).map { + it.toByteArray().get2byteShortAt() + } + + val result = when(accelMode){ + VIBRATION -> { + Ble.Accelerometer.RealtimePoint.Vibration( + (value.get2byteShortAt() + .toFloat() * accelScale.k) / Short.MAX_VALUE + ) + } + ANGLE -> { + Ble.Accelerometer.RealtimePoint.Angle( + x = calculateAngle( + data[2].toFloat(), + data[1].toFloat() + ), + y = calculateAngle( + data[2].toFloat(), + data[0].toFloat() + ), + z = calculateAngle( + data[0].toFloat(), + data[1].toFloat() + ) + ) + } + ROTATIONS -> { + Ble.Accelerometer.RealtimePoint.Rotation( + angle = ((360f / 8f) * ((data[0] / 100f) + 1f)) - 45f, + tmp = data[1].toFloat(), + turnovers = data[2] + ) + } + ACCELERATION, + PEAK_ACCELERATION, + RMS -> { + Ble.Accelerometer.RealtimePoint.Common( + x = (data[0].toFloat() * accelScale.k) / Short.MAX_VALUE, + y = (data[1].toFloat() * accelScale.k) / Short.MAX_VALUE, + z = (data[2].toFloat() * accelScale.k) / Short.MAX_VALUE, + ) + } + } + + emit(Result.success(result)) + + } + + + } catch (err: Exception) { + + err.printStackTrace() + + emit(Result.failure(BleException.UnexpectedResponse)) + + } finally { + + connection.disconnect() + connection.close() + + } + + } else { + + emit(Result.failure(BleException.PermissionDenied)) + + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/data/ReadAccelerometerSpectreCallback.kt b/app/src/main/java/llc/arma/ble/data/ReadAccelerometerSpectreCallback.kt index 067afa7..8acc858 100644 --- a/app/src/main/java/llc/arma/ble/data/ReadAccelerometerSpectreCallback.kt +++ b/app/src/main/java/llc/arma/ble/data/ReadAccelerometerSpectreCallback.kt @@ -1,19 +1,21 @@ 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.bluetooth.BluetoothGattDescriptor -import android.content.pm.PackageManager import android.os.Build import android.util.Log -import androidx.core.app.ActivityCompat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow +import llc.arma.ble.data.extensions.checkPermission +import llc.arma.ble.data.extensions.get2byteShortAt +import llc.arma.ble.data.extensions.get2byteUIntAt +import llc.arma.ble.data.extensions.get4byteUIntAt +import llc.arma.ble.data.extensions.sendData import llc.arma.ble.domain.Result import llc.arma.ble.domain.common.BleException import llc.arma.ble.domain.common.ProgressState @@ -23,441 +25,14 @@ 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 no.nordicsemi.android.common.core.DataByteArray import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt +import no.nordicsemi.android.kotlin.ble.core.data.util.DataByteArray import java.nio.ByteBuffer import java.nio.ByteOrder.LITTLE_ENDIAN import java.util.UUID -fun ByteArray.get2byteShortAt(): Int { - val shorts = ShortArray(1) - ByteBuffer.wrap(this).order(LITTLE_ENDIAN).asShortBuffer()[shorts] - return shorts[0].toInt() -} -class ReadAccelerometerSpectreCallback( - private val app: Application, - private val accelScale: AccelScale, - private val accelMode: AccelViewMode, - private val fftAxis: FftAxis, - private val fftMode: FftViewMode, - private val frequency: FftFrequency, - private val onResult: (Result>, BleException>) -> Unit -) : BluetoothGattCallback() { - enum class Property { - DATA_SIZE, PACKAGE - } - - private fun ByteArray.get4byteUIntAt(idx: Int) = - ((this[idx + 3].toUInt() and 0xFFu) shl 24) or - ((this[idx + 2].toUInt() and 0xFFu) shl 16) or - ((this[idx + 1].toUInt() and 0xFFu) shl 8) or - (this[idx].toUInt() and 0xFFu) - - private fun ByteArray.get2byteUIntAt(idx: Int) = - ((this[idx + 1].toUInt() and 0xFFu) shl 8) or - (this[idx].toUInt() and 0xFFu) - - private var readProperty: Property? = null - - init { - onResult(Result.success(ProgressState.Indeterminate)) - } - - override fun onConnectionStateChange( - gatt: BluetoothGatt, - status: Int, - newState: Int - ) { - super.onConnectionStateChange(gatt, status, newState) - - if(status == BluetoothGatt.GATT_SUCCESS){ - - if(newState == BluetoothGatt.STATE_CONNECTED){ - - if (app.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){ - enableNotifications(gatt) - } - - } - - private fun enableNotifications( - gatt: BluetoothGatt - ){ - - gatt.getService(serviceUUID)?.getCharacteristic(accelerometerReadUUID)?.let { - - if (app.checkPermission()) { - - gatt.setCharacteristicNotification(it, true) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - gatt.writeDescriptor(it.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")), - BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) - } else { - val descriptor = it.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")) - descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE - gatt.writeDescriptor(descriptor) - } - - Log.d("spectre", "enable notification") - - onResult(Result.success(ProgressState.Indeterminate)) - resultAccelerometerPackage.clear() - - } else { - onResult(Result.failure(BleException.PermissionDenied)) - gatt.close() - } - - return - - } - - onResult(Result.failure(BleException.UnexpectedResponse)) - - } - - private var initialValue: Long? = null - private var frequencyInterval: Long? = null - - private val resultAccelerometerPackage: MutableList = mutableListOf() - - private var expectedDataSize: Int? = null - - @Deprecated("Deprecated in Java") - 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) - } - } - - @Deprecated("Deprecated in Java") - override fun onCharacteristicChanged( - gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic - ) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { - super.onCharacteristicChanged(gatt, characteristic) - onCommonCharacteristicChanged(gatt, characteristic, characteristic.value) - } - } - - override fun onCharacteristicChanged( - gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - value: ByteArray - ) { - super.onCharacteristicChanged(gatt, characteristic, value) - onCommonCharacteristicChanged(gatt, characteristic, value) - } - - private fun onCommonCharacteristicChanged( - gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - value: ByteArray, - ){ - - if(characteristic.uuid == accelerometerReadUUID) { - - readProperty = Property.DATA_SIZE - gatt.getService(serviceUUID).getCharacteristic(accelerometerHistoryReadUUID)?.let { - gatt.writeCharacteristic(it, byteArrayOf(2)) - } - - } - } - - override fun onCharacteristicRead( - gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - value: ByteArray, - status: Int - ) { - super.onCharacteristicRead(gatt, characteristic, value, status) - onCommonCharacteristicRead(gatt, characteristic, value, status) - } - - private fun onCommonCharacteristicRead( - gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - value: ByteArray, - status: Int - ){ - - Log.d("value", value.joinToString(separator = "")) - - 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()){ - - initialValue = value.get4byteUIntAt(8).toLong() - frequencyInterval = value.get4byteUIntAt(4).toLong() - - val accelerometerDataArray = value.asList().subList(16, value.size) - - resultAccelerometerPackage.addAll( - accelerometerDataArray.chunked(2).map { - it.toByteArray().get2byteShortAt().toFloat() - }.toMutableList() - ) - - val nextPackageDataCount = value.get2byteUIntAt(2) - expectedDataSize = nextPackageDataCount.toInt() + resultAccelerometerPackage.size - - onResult(Result.success(ProgressState.Progress(0f / expectedDataSize!!.toFloat()))) - onResult(Result.success(ProgressState.Progress(resultAccelerometerPackage.size.toFloat() / expectedDataSize!!.toFloat()))) - - if(nextPackageDataCount != 0.toUInt()){ - - if (app.checkPermission()) { - - gatt.writeCharacteristic(characteristic, byteArrayOf(5)) - gatt.readCharacteristic(characteristic) - - } else { - - onResult(Result.failure(BleException.PermissionDenied)) - gatt.close() - - } - - } else { - - onResult( - Result.success( - ProgressState.Finished( - resultAccelerometerPackage.withIndex().map { - Ble.Accelerometer.MeasurePoint( - frequency = frequencyInterval!! * it.index + initialValue!!, - value = (it.value * accelScale.k) / Short.MAX_VALUE - ) - } - ) - ) - ) - - start(gatt) - - } - - } else { - - if (value[0] == 251.toByte()) { - - val nextPackageDataCount = value.get2byteUIntAt(2) - val temperatureDataArray = value.toList().subList(4, value.size) - - resultAccelerometerPackage.addAll( - temperatureDataArray.chunked(2).map { - it.toByteArray().get2byteShortAt().toFloat() - } - ) - - onResult(Result.success(ProgressState.Progress(resultAccelerometerPackage.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( - resultAccelerometerPackage.withIndex().map { - Ble.Accelerometer.MeasurePoint( - frequency = frequencyInterval!! * it.index + initialValue!!, - value = (it.value * accelScale.k) / Short.MAX_VALUE - ) - } - ) - ) - ) - - start(gatt) - - } - } 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(readProperty !== null) { - - if (status == BluetoothGatt.GATT_SUCCESS) { - - if (app.checkPermission()) { - - gatt.readCharacteristic(characteristic) - - } else { - - onResult(Result.failure(BleException.PermissionDenied)) - gatt.close() - - } - - } else { - onResult(Result.failure(BleException.UnexpectedResponse)) - gatt.close() - } - } - - } - - override fun onDescriptorWrite( - gatt: BluetoothGatt, - descriptor: BluetoothGattDescriptor, - status: Int - ) { - super.onDescriptorWrite(gatt, descriptor, status) - start(gatt) - } - - private fun start( - gatt: BluetoothGatt, - ){ - - gatt.getService(serviceUUID)?.getCharacteristic(accelerometerReadUUID)?.let { - - if (app.checkPermission()) { - - val payload = byteArrayOf( - 4, - accelMode.sendData, - accelScale.sendData, - fftMode.sendData, - fftAxis.sendData, - frequency.sendData, - 2 - ) - readProperty = null - gatt.writeCharacteristic(it, payload) - - onResult(Result.success(ProgressState.Indeterminate)) - resultAccelerometerPackage.clear() - - } else { - - onResult(Result.failure(BleException.PermissionDenied)) - gatt.close() - - } - - return - - } - - } - - private fun BluetoothGatt.writeCharacteristic( - characteristic: BluetoothGattCharacteristic, - data: ByteArray - ): Result{ - - return if(app.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) - } - - } - -} - - -@OptIn(ExperimentalUnsignedTypes::class) fun readAccelerometerSpectre( address: String, app: Application, @@ -466,72 +41,65 @@ fun readAccelerometerSpectre( fftAxis: FftAxis, fftMode: FftViewMode, frequency: FftFrequency, -): Flow>, BleException>> { +): Flow>, BleException>> { return flow { - var initialValue: Long? = null - var frequencyInterval: Long? = null - - val resultAccelerometerPackage: MutableList = mutableListOf() - - var expectedDataSize: Int? = null - if(app.checkPermission()) { + val connection = + ClientBleGatt.connect(app, address, CoroutineScope(Dispatchers.Default)) + try { - val connection = - ClientBleGatt.connect(app, address, CoroutineScope(Dispatchers.Default)) + val service = connection.discoverServices() + .findService(serviceUUID) ?: throw IllegalStateException() - val characteristic = connection.discoverServices() - .findService(serviceUUID) - ?.findCharacteristic(accelerometerReadUUID) + val characteristic = service.findCharacteristic(accelerometerReadUUID) ?: throw IllegalStateException() - val historyCharacteristic = connection.discoverServices() - .findService(serviceUUID) - ?.findCharacteristic(accelerometerHistoryReadUUID) + val historyCharacteristic = service.findCharacteristic(accelerometerHistoryReadUUID) ?: throw IllegalStateException() - Log.d("notification", "-1") + characteristic.findDescriptor( + UUID.fromString("00002902-0000-1000-8000-00805f9b34fb") + )?.write(DataByteArray(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)) + ?: throw IllegalStateException() - Log.d("notification", "0") - - characteristic.write(DataByteArray(byteArrayOf( - 4, - accelMode.sendData, - accelScale.sendData, - fftMode.sendData, - fftAxis.sendData, - frequency.sendData, - 2 - ))) - - Log.d("notification", "1") + characteristic.write( + DataByteArray(byteArrayOf( + 4, + accelMode.sendData, + accelScale.sendData, + fftMode.sendData, + fftAxis.sendData, + frequency.sendData, + 2 + )) + ) val notifications = characteristic.getNotifications() notifications.collect { + var initialValue: Long? = null + var frequencyInterval: Long? = null + val resultAccelerometerPackage: MutableList = mutableListOf() - Log.d("notification", "0") + var expectedDataSize: Int? = null historyCharacteristic.write(DataByteArray.from(2)) var value = historyCharacteristic.read().value - Log.d("value", value.joinToString(separator = "")) - Log.d("value", value.get2byteUIntAt(0).toString()) - if (value.contentEquals(byteArrayOf(0, 0))) { emit(Result.success(ProgressState.Finished(emptyList()))) } else { - Log.d("expected data size", value.get2byteUIntAt(0).toString()) + var nextPackageDataCount = value.get2byteUIntAt(0) val writeData = mutableListOf( 1.toByte(), @@ -543,7 +111,7 @@ fun readAccelerometerSpectre( historyCharacteristic.write(DataByteArray(writeData)) value = historyCharacteristic.read().value - var nextPackageDataCount = value.get2byteUIntAt(2) + while (nextPackageDataCount.toInt() != 0) { @@ -569,8 +137,8 @@ fun readAccelerometerSpectre( expectedDataSize = nextPackageDataCount.toInt() + resultAccelerometerPackage.size - emit(Result.success(ProgressState.Progress(0f / expectedDataSize!!.toFloat()))) - emit(Result.success(ProgressState.Progress(resultAccelerometerPackage.size.toFloat() / expectedDataSize!!.toFloat()))) + emit(Result.success(ProgressState.Progress(0f / expectedDataSize.toFloat()))) + emit(Result.success(ProgressState.Progress(resultAccelerometerPackage.size.toFloat() / expectedDataSize.toFloat()))) historyCharacteristic.write(DataByteArray.from(5)) value = historyCharacteristic.read().value @@ -581,7 +149,7 @@ fun readAccelerometerSpectre( Result.success( ProgressState.Finished( resultAccelerometerPackage.withIndex().map { - Ble.Accelerometer.MeasurePoint( + Ble.Accelerometer.SpectrePoint( frequency = frequencyInterval!! * it.index + initialValue!!, value = (it.value * accelScale.k) / Short.MAX_VALUE ) @@ -611,6 +179,10 @@ fun readAccelerometerSpectre( err.printStackTrace() emit(Result.failure(BleException.UnexpectedResponse)) + } finally { + + connection.close() + } } else { diff --git a/app/src/main/java/llc/arma/ble/data/ReadTemperatureHistoryCallback.kt b/app/src/main/java/llc/arma/ble/data/ReadTemperatureHistoryCallback.kt index 256554d..625b47c 100644 --- a/app/src/main/java/llc/arma/ble/data/ReadTemperatureHistoryCallback.kt +++ b/app/src/main/java/llc/arma/ble/data/ReadTemperatureHistoryCallback.kt @@ -1,339 +1,31 @@ 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 kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow +import llc.arma.ble.data.extensions.checkPermission +import llc.arma.ble.data.extensions.get2byteUIntAt +import llc.arma.ble.data.extensions.get4byteUIntAt +import llc.arma.ble.data.extensions.toTemperature 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.usecase.AccelScale -import llc.arma.ble.domain.usecase.AccelViewMode -import no.nordicsemi.android.common.core.DataByteArray import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt - -class ReadTemperatureHistoryCallback( - private val app: Application, - private val onResult: (Result>, BleException>) -> Unit -) : BluetoothGattCallback() { - - enum class Property { - DATA_SIZE, PACKAGE - } - - private fun ByteArray.get4byteUIntAt(idx: Int) = - ((this[idx + 3].toUInt() and 0xFFu) shl 24) or - ((this[idx + 2].toUInt() and 0xFFu) shl 16) or - ((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 (app.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(temperatureHistoryReadUUID)?.let { - - if (app.checkPermission()) { - - readProperty = Property.DATA_SIZE - gatt.writeCharacteristic(it, byteArrayOf(2)) - - } else { - - onResult(Result.failure(BleException.PermissionDenied)) - gatt.close() - - } - - } - - } - - } - - private var lastMeasureSystemTime: Long? = null - - private var bleMeasureInterval: Long? = null - private var bleRealTime: Long? = null - private var bleLastMeasureTime: Long? = null - - private val resultTemperaturePackage: MutableList = mutableListOf() - - var expectedDataSize: Int? = null - - override fun onCharacteristicRead( - gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - status: Int - ) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { - super.onCharacteristicRead(gatt, characteristic, status) - onCommonCharacteristicRead(gatt, characteristic, characteristic.value, status) - } - } - - override fun onCharacteristicRead( - gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - value: ByteArray, - status: Int - ) { - super.onCharacteristicRead(gatt, characteristic, value, status) - onCommonCharacteristicRead(gatt, characteristic, value, status) - } - - @OptIn(ExperimentalUnsignedTypes::class) - private fun onCommonCharacteristicRead( - gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - value: ByteArray, - status: Int - ){ - - if(status == BluetoothGatt.GATT_SUCCESS){ - when(readProperty){ - Property.DATA_SIZE -> { - - if(value.contentEquals(byteArrayOf(0, 0))) { - onResult( - Result.success( - ProgressState.Finished( - emptyList() - ) - ) - ) - gatt.close() - - } else { - - val writeData = mutableListOf( - 1.toByte(), - 0.toByte(), - 0.toByte() - ).apply { - addAll(value.toList()) - }.toByteArray() - - readProperty = Property.PACKAGE - gatt.writeCharacteristic(characteristic, writeData) - - } - } - Property.PACKAGE -> { - - if(value[0] == 250.toByte()){ - - bleMeasureInterval = value.get4byteUIntAt(4).toLong() - bleLastMeasureTime = value.get4byteUIntAt(8).toLong() - bleRealTime = value.get4byteUIntAt(12).toLong() - - lastMeasureSystemTime = System.currentTimeMillis() - ((bleRealTime!! - bleLastMeasureTime!!) * 1_000) - - val temperatureDataArray = value.toUByteArray().asList().subList(16, value.size) - - resultTemperaturePackage.addAll( - temperatureDataArray.chunked(2).map { - it.toUByteArray().toTemperature() - }.toMutableList() - ) - - val nextPackageDataCount = value.get2byteUIntAt(2) - expectedDataSize = nextPackageDataCount.toInt() + resultTemperaturePackage.size - Log.d("read", expectedDataSize.toString()) - onResult(Result.success(ProgressState.Progress(0f / expectedDataSize!!.toFloat()))) - onResult(Result.success(ProgressState.Progress(resultTemperaturePackage.size.toFloat() / expectedDataSize!!.toFloat()))) - - if(nextPackageDataCount != 0.toUInt()){ - - if (app.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.Thermometer.MeasurePoint( - date = 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().toTemperature() - } - ) - - 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.Thermometer.MeasurePoint( - date = 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 (app.checkPermission()) { - - gatt.readCharacteristic(characteristic) - - } else { - - onResult(Result.failure(BleException.PermissionDenied)) - gatt.close() - - } - - } else { - onResult(Result.failure(BleException.UnexpectedResponse)) - gatt.close() - } - - } - - fun BluetoothGatt.writeCharacteristic( - characteristic: BluetoothGattCharacteristic, - data: ByteArray - ): Result{ - - return if(app.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) - } - - } - -} +import no.nordicsemi.android.kotlin.ble.core.data.util.DataByteArray @OptIn(ExperimentalUnsignedTypes::class) fun readThermometerHistory( address: String, app: Application, -): Flow>, BleException>> { +): Flow>, BleException>> { return flow { @@ -369,8 +61,6 @@ fun readThermometerHistory( } else { - Log.d("expected data size", value.get2byteUIntAt(0).toString()) - val writeData = mutableListOf( 1.toByte(), 0.toByte(), @@ -410,12 +100,6 @@ fun readThermometerHistory( }.toMutableList() ) - Log.d( - "received data size", - (temperatureDataArray.chunked(2).size).toString() - ) - Log.d("next data size", nextPackageDataCount.toString()) - expectedDataSize = nextPackageDataCount.toInt() + resultTemperaturePackage.size @@ -431,7 +115,7 @@ fun readThermometerHistory( Result.success( ProgressState.Finished( resultTemperaturePackage.withIndex().map { - Ble.Thermometer.MeasurePoint( + Ble.Thermometer.HistoryPoint( date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), value = it.value ) diff --git a/app/src/main/java/llc/arma/ble/data/XlsxRepositoryImpl.kt b/app/src/main/java/llc/arma/ble/data/XlsxRepositoryImpl.kt index 453907a..616d892 100644 --- a/app/src/main/java/llc/arma/ble/data/XlsxRepositoryImpl.kt +++ b/app/src/main/java/llc/arma/ble/data/XlsxRepositoryImpl.kt @@ -1,10 +1,9 @@ package llc.arma.ble.data -import android.R.attr.src import android.app.Application import android.icu.text.SimpleDateFormat import android.os.Environment -import android.os.FileUtils +import android.util.Log import llc.arma.ble.R import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.repository.XlsxRepository @@ -15,7 +14,6 @@ import java.io.File import java.io.FileInputStream import java.io.FileOutputStream import java.util.Date -import java.util.UUID import javax.inject.Inject @@ -28,12 +26,14 @@ class XlsxRepositoryImpl @Inject constructor( data: List ): File { - val fileNameDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS") - val fileName = "$bleName ${fileNameDateFormat.format(Date(System.currentTimeMillis()))}.xlsx" + val fileNameDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm") + val fileName = "${fileNameDateFormat.format(Date(System.currentTimeMillis()))}.xlsx".replace(' ', '_') val formatter = SimpleDateFormat("dd.MM.yyyy HH:mm") + File(application.filesDir.absolutePath).mkdirs() val mailFile = File(application.filesDir, fileName) + mailFile.createNewFile() when(data.firstOrNull()){ @@ -73,7 +73,7 @@ class XlsxRepositoryImpl @Inject constructor( x.setCellValue(value.value.toDouble()) } - is Ble.Accelerometer.HistoryPoint.Accelerate -> { + is Ble.Accelerometer.HistoryPoint.Acceleration -> { dateX.setCellValue(formatter.format(Date(value.date))) dateY.setCellValue(formatter.format(Date(value.date))) dateZ.setCellValue(formatter.format(Date(value.date))) @@ -82,6 +82,10 @@ class XlsxRepositoryImpl @Inject constructor( z.setCellValue(value.z.toDouble()) } + is Ble.Accelerometer.HistoryPoint.Rotation -> { + dateX.setCellValue(formatter.format(Date(value.date))) + x.setCellValue(value.value.toDouble()) + } } } diff --git a/app/src/main/java/llc/arma/ble/data/extensions/ApplicationExtension.kt b/app/src/main/java/llc/arma/ble/data/extensions/ApplicationExtension.kt new file mode 100644 index 0000000..3ad29b7 --- /dev/null +++ b/app/src/main/java/llc/arma/ble/data/extensions/ApplicationExtension.kt @@ -0,0 +1,22 @@ +package llc.arma.ble.data.extensions + +import android.Manifest +import android.app.Application +import android.content.pm.PackageManager +import android.os.Build +import androidx.core.app.ActivityCompat + +fun Application.checkPermission(): Boolean { + + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) == + PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) == + PackageManager.PERMISSION_GRANTED + } else { + return ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == + PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == + PackageManager.PERMISSION_GRANTED + } +} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/data/extensions/BleEnumExtensions.kt b/app/src/main/java/llc/arma/ble/data/extensions/BleEnumExtensions.kt new file mode 100644 index 0000000..3c965e2 --- /dev/null +++ b/app/src/main/java/llc/arma/ble/data/extensions/BleEnumExtensions.kt @@ -0,0 +1,93 @@ +package llc.arma.ble.data.extensions + +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 + +fun Ble.BleState.TX.Companion.fromByte(byte: Byte): Ble.BleState.TX? { + return Ble.BleState.TX.values().associateBy { it.sendData }[byte] +} + +val Ble.BleState.TX.sendData: Byte + get() { + return when (this) { + Ble.BleState.TX.MINUS_40 -> -40 + Ble.BleState.TX.MINUS_20 -> -20 + Ble.BleState.TX.MINUS_16 -> -16 + Ble.BleState.TX.MINUS_12 -> -12 + Ble.BleState.TX.MINUS_8 -> -8 + Ble.BleState.TX.MINUS_4 -> -4 + Ble.BleState.TX.ZERO -> 0 + Ble.BleState.TX.PLUS_3 -> 3 + Ble.BleState.TX.PLUS_4 -> 4 + } + } + +val FftFrequency.sendData: Byte + get() { + return when(this){ + FftFrequency.OFF -> 0 + FftFrequency.F_1 -> 1 + FftFrequency.F_10 -> 2 + FftFrequency.F_25 -> 3 + FftFrequency.F_50 -> 4 + FftFrequency.F_100 -> 5 + FftFrequency.F_200 -> 6 + FftFrequency.F_400 -> 7 + FftFrequency.F_1620 -> 8 + FftFrequency.F_1344 -> 9 + } + } + +val FftAxis.sendData: Byte + get() { + return when(this){ + FftAxis.AUTO -> 0 + FftAxis.X -> 1 + FftAxis.Y -> 2 + FftAxis.Z -> 3 + } + } + +val FftViewMode.sendData: Byte + get() { + return when(this){ + FftViewMode.SPECTRE -> 0 + FftViewMode.X -> 1 + FftViewMode.Y -> 2 + FftViewMode.Z -> 3 + } + } + +fun AccelViewMode.Companion.fromByte(byte: Byte): AccelViewMode? { + return AccelViewMode.values().associateBy { it.sendData }[byte] +} + +val AccelViewMode.sendData: Byte + get() { + return when(this){ + AccelViewMode.ACCELERATION -> 0 + AccelViewMode.PEAK_ACCELERATION -> 1 + AccelViewMode.RMS -> 2 + AccelViewMode.VIBRATION -> 3 + AccelViewMode.ANGLE -> 4 + AccelViewMode.ROTATIONS -> 5 + } + } + +fun AccelScale.Companion.fromByte(byte: Byte): AccelScale? { + return AccelScale.values().associateBy { it.sendData }[byte] +} + +val AccelScale.sendData: Byte + get() { + return when(this){ + AccelScale.S_2 -> 0 + AccelScale.S_4 -> 1 + AccelScale.S_8 -> 2 + AccelScale.S_16 -> 3 + } + } \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/data/extensions/BleScanResultExtensions.kt b/app/src/main/java/llc/arma/ble/data/extensions/BleScanResultExtensions.kt new file mode 100644 index 0000000..5d7bdf3 --- /dev/null +++ b/app/src/main/java/llc/arma/ble/data/extensions/BleScanResultExtensions.kt @@ -0,0 +1,40 @@ +package llc.arma.ble.data.extensions + +import llc.arma.ble.domain.model.BleInfo +import no.nordicsemi.android.kotlin.ble.core.scanner.BleScanResult + +val BleScanResult.timerEnabled: Boolean + get() { + return data?.scanRecord?.manufacturerSpecificData?.get(89)?.getByte(2) == 1.toByte() + } + +val BleScanResult.info: BleInfo + get() { + this.device.name + return BleInfo( + name = this.device.name ?: "", + serial = device.address, + batteryLevel = batteryLevel ?: 0, + rssi = data?.rssi, + type = type, + scanTime = (data?.timestampNanos ?: 0) / 1_000_000, + tx = data?.txPower ?: 0, + recordEnabled = timerEnabled + ) + } + +val BleScanResult.batteryLevel: Int? + get() { + return data?.scanRecord?.manufacturerSpecificData?.get(89)?.getByte(1) + ?.toUByte()?.toInt() + } + +val BleScanResult.type: BleInfo.Type + get() { + return when(data?.scanRecord?.manufacturerSpecificData?.get(89)?.getByte(0)?.toUByte()?.toInt()){ + 1 -> BleInfo.Type.BEACON + 2 -> BleInfo.Type.THERMOMETER + else -> BleInfo.Type.ACCELEROMETER + } + } + diff --git a/app/src/main/java/llc/arma/ble/data/extensions/ByteArrayExtensions.kt b/app/src/main/java/llc/arma/ble/data/extensions/ByteArrayExtensions.kt new file mode 100644 index 0000000..a5269f4 --- /dev/null +++ b/app/src/main/java/llc/arma/ble/data/extensions/ByteArrayExtensions.kt @@ -0,0 +1,35 @@ +package llc.arma.ble.data.extensions + +import java.nio.ByteBuffer +import java.nio.ByteOrder + +fun ByteArray.get2byteShortAt(): Int { + val shorts = ShortArray(1) + ByteBuffer.wrap(this).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer()[shorts] + return shorts[0].toInt() +} + +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) + +fun ByteArray.get2byteUIntAt(idx: Int) = + ((this[idx + 1].toUInt() and 0xFFu) shl 8) or + (this[idx].toUInt() and 0xFFu) + +@OptIn(ExperimentalUnsignedTypes::class) +fun UByteArray.toTemperature(): Float { + + val uShort = (this[0] + this[1] * 256u).toUShort() + + val result = if (uShort > Short.MAX_VALUE.toUShort()) { + ((uShort.inv() + 1u).toFloat().unaryMinus()) / 100f + } else { + uShort.toFloat() / 100f + } + + return result + +} diff --git a/app/src/main/java/llc/arma/ble/domain/model/Ble.kt b/app/src/main/java/llc/arma/ble/domain/model/Ble.kt index 4c82b99..abd3912 100644 --- a/app/src/main/java/llc/arma/ble/domain/model/Ble.kt +++ b/app/src/main/java/llc/arma/ble/domain/model/Ble.kt @@ -13,21 +13,21 @@ sealed class Ble( val accelerometerState: AccelerometerState ): Ble(info) { - sealed class History { + sealed class HistorySettings { data class Enabled( val scale: AccelScale, val mode: AccelViewMode, val detailed: Boolean - ) : History() + ) : HistorySettings() - object Disabled : History() + object Disabled : HistorySettings() } data class WriteRequest( val tx: BleState.TX?, - val saveHistory: History?, + val saveHistorySettings: HistorySettings?, val historyInterval: Long? ) @@ -38,7 +38,12 @@ sealed class Ble( val value: Float ) : HistoryPoint() - class Accelerate ( + class Rotation ( + val date: Long, + val value: Long + ) : HistoryPoint() + + class Acceleration ( val date: Long, val x: Float, val y: Float, @@ -54,13 +59,39 @@ sealed class Ble( } - class MeasurePoint ( + sealed class RealtimePoint { + + data class Rotation( + val angle: Float, + val tmp: Float, + val turnovers: Int, + ) : RealtimePoint() + + data class Common( + val x: Float, + val y: Float, + val z: Float, + ) : RealtimePoint() + + data class Angle( + val x: Float, + val y: Float, + val z: Float, + ) : RealtimePoint() + + data class Vibration( + val value: Float + ) : RealtimePoint() + + } + + class SpectrePoint ( val frequency: Long, val value: Float ) data class AccelerometerState( - val saveHistory: History, + val saveHistorySettings: HistorySettings, val historyInterval: Long ) @@ -83,7 +114,7 @@ sealed class Ble( val thermometerState: ThermometerState ) : Ble(info) { - class MeasurePoint( + class HistoryPoint( val date: Long, val value: Float ) @@ -107,7 +138,9 @@ sealed class Ble( ){ enum class TX { - MINUS_40, MINUS_20, MINUS_16, MINUS_12, MINUS_8, MINUS_4, ZERO, PLUS_3, PLUS_4 + MINUS_40, MINUS_20, MINUS_16, MINUS_12, MINUS_8, MINUS_4, ZERO, PLUS_3, PLUS_4; + + companion object } } diff --git a/app/src/main/java/llc/arma/ble/domain/repository/BleRepository.kt b/app/src/main/java/llc/arma/ble/domain/repository/BleRepository.kt index cdac918..9c3c3dd 100644 --- a/app/src/main/java/llc/arma/ble/domain/repository/BleRepository.kt +++ b/app/src/main/java/llc/arma/ble/domain/repository/BleRepository.kt @@ -9,20 +9,17 @@ import llc.arma.ble.domain.model.BleInfo import llc.arma.ble.domain.model.ConnectedBleInfo import llc.arma.ble.domain.usecase.AccelScale import llc.arma.ble.domain.usecase.AccelViewMode -import llc.arma.ble.domain.usecase.MeasureData import llc.arma.ble.domain.usecase.FftAxis import llc.arma.ble.domain.usecase.FftFrequency import llc.arma.ble.domain.usecase.FftViewMode interface BleRepository { - fun getConnectedBle(): List - - fun getBleAroundFlow(): Flow, BleException>> + fun getBleAroundFlow(): Result>, BleException> suspend fun getBleBySerial(serial: String) : Result, BleException> - suspend fun getTemperatureHistoryBySerial(serial: String): Flow>, BleException>> + suspend fun getTemperatureHistoryBySerial(serial: String): Flow>, BleException>> suspend fun writeBle(serial: String, request: Ble.Thermometer.WriteRequest): Result @@ -39,7 +36,7 @@ interface BleRepository { fftAxis: FftAxis, fftMode: FftViewMode, frequency: FftFrequency - ): Flow> + ): Flow> suspend fun getAccelerometerSpectreBySerial( serial: String, @@ -48,7 +45,7 @@ interface BleRepository { fftAxis: FftAxis, fftMode: FftViewMode, frequency: FftFrequency - ): Flow>, BleException>> + ): Flow>, BleException>> suspend fun getAccelerometerHistoryBySerial(serial: String): Flow>, BleException>> diff --git a/app/src/main/java/llc/arma/ble/domain/repository/XlsxRepository.kt b/app/src/main/java/llc/arma/ble/domain/repository/XlsxRepository.kt index e839aab..5a148d7 100644 --- a/app/src/main/java/llc/arma/ble/domain/repository/XlsxRepository.kt +++ b/app/src/main/java/llc/arma/ble/domain/repository/XlsxRepository.kt @@ -1,7 +1,6 @@ package llc.arma.ble.domain.repository import llc.arma.ble.domain.model.Ble -import llc.arma.ble.domain.usecase.MeasureData import java.io.File interface XlsxRepository { diff --git a/app/src/main/java/llc/arma/ble/domain/usecase/GetAccelerometerMeasureBySerialFlow.kt b/app/src/main/java/llc/arma/ble/domain/usecase/GetAccelerometerMeasureBySerialFlow.kt index c80f6c4..4352389 100644 --- a/app/src/main/java/llc/arma/ble/domain/usecase/GetAccelerometerMeasureBySerialFlow.kt +++ b/app/src/main/java/llc/arma/ble/domain/usecase/GetAccelerometerMeasureBySerialFlow.kt @@ -3,6 +3,7 @@ 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.model.Ble import llc.arma.ble.domain.repository.BleRepository import javax.inject.Inject @@ -17,7 +18,7 @@ class GetAccelerometerMeasureBySerialFlow @Inject constructor( fftAxis: FftAxis, fftMode: FftViewMode, frequency: FftFrequency - ): Flow> { + ): Flow> { return bleRepository.getAccelerometerMeasureBySerialFlow(serial, accelScale, accelMode, fftAxis, fftMode, frequency) @@ -26,38 +27,15 @@ class GetAccelerometerMeasureBySerialFlow @Inject constructor( } enum class AccelViewMode { - ACCELERATION, PEAK_ACCELERATION, RMS, VIBRATION, ANGLE, ROTATIONS + ACCELERATION, PEAK_ACCELERATION, RMS, VIBRATION, ANGLE, ROTATIONS; + + companion object } enum class AccelScale(val k: Int) { - S_2(2_000), S_4(4_000), S_8(8_000), S_16(16_000) -} -sealed class MeasureData { + S_2(2_000), S_4(4_000), S_8(8_000), S_16(16_000); - data class Angle( - val xAngle: Float, - val yAngle: Float, - val zAngle: Float, - val xAccelerate: Float, - val yAccelerate: Float, - val zAccelerate: Float, - ) : MeasureData() + companion object - data class Accelerate( - val x: Float, - val y: Float, - val z: Float, - ) : MeasureData() - - data class Vibration( - val value: Float - ) : MeasureData() - -} - -data class Accelerate( - val x: Float, - val y: Float, - val z: Float, -) \ No newline at end of file +} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/domain/usecase/GetAccelerometerSpectreBySerial.kt b/app/src/main/java/llc/arma/ble/domain/usecase/GetAccelerometerSpectreBySerial.kt index 5190ba2..d025cdc 100644 --- a/app/src/main/java/llc/arma/ble/domain/usecase/GetAccelerometerSpectreBySerial.kt +++ b/app/src/main/java/llc/arma/ble/domain/usecase/GetAccelerometerSpectreBySerial.kt @@ -19,7 +19,7 @@ class GetAccelerometerSpectreBySerial @Inject constructor( fftAxis: FftAxis, fftMode: FftViewMode, frequency: FftFrequency - ): Flow>, BleException>> { + ): Flow>, BleException>> { return bleRepository.getAccelerometerSpectreBySerial(serial, accelScale, accelMode, fftAxis, fftMode, frequency) @@ -28,13 +28,19 @@ class GetAccelerometerSpectreBySerial @Inject constructor( } enum class FftFrequency { - OFF, F_1, F_10, F_25, F_50, F_100, F_200, F_400, F_1620, F_1344 + OFF, F_1, F_10, F_25, F_50, F_100, F_200, F_400, F_1620, F_1344; + + companion object } enum class FftViewMode { - SPECTRE, X, Y, Z + SPECTRE, X, Y, Z; + + companion object } enum class FftAxis { - AUTO, X, Y, Z + AUTO, X, Y, Z; + + companion object } \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/domain/usecase/GetBleAroundFlow.kt b/app/src/main/java/llc/arma/ble/domain/usecase/GetBleAroundFlow.kt index a594369..60e9a32 100644 --- a/app/src/main/java/llc/arma/ble/domain/usecase/GetBleAroundFlow.kt +++ b/app/src/main/java/llc/arma/ble/domain/usecase/GetBleAroundFlow.kt @@ -12,6 +12,6 @@ class GetBleAroundFlow @Inject constructor( private val bleRepository: BleRepository ) { - operator fun invoke(): Flow, BleException>> = bleRepository.getBleAroundFlow() + operator fun invoke(): Result>, BleException> = bleRepository.getBleAroundFlow() } \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/domain/usecase/GetConnectedBleDevices.kt b/app/src/main/java/llc/arma/ble/domain/usecase/GetConnectedBleDevices.kt deleted file mode 100644 index d331a54..0000000 --- a/app/src/main/java/llc/arma/ble/domain/usecase/GetConnectedBleDevices.kt +++ /dev/null @@ -1,15 +0,0 @@ -package llc.arma.ble.domain.usecase - -import llc.arma.ble.domain.model.ConnectedBleInfo -import llc.arma.ble.domain.repository.BleRepository -import javax.inject.Inject - -class GetConnectedBleDevices @Inject constructor( - private val bleRepository: BleRepository -) { - - operator fun invoke(): List{ - return bleRepository.getConnectedBle() - } - -} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/domain/usecase/GetTemperatureHistoryBySerial.kt b/app/src/main/java/llc/arma/ble/domain/usecase/GetTemperatureHistoryBySerial.kt index 8979043..1230181 100644 --- a/app/src/main/java/llc/arma/ble/domain/usecase/GetTemperatureHistoryBySerial.kt +++ b/app/src/main/java/llc/arma/ble/domain/usecase/GetTemperatureHistoryBySerial.kt @@ -12,7 +12,7 @@ class GetTemperatureHistoryBySerial @Inject constructor( private val bleRepository: BleRepository ) { - suspend operator fun invoke(serial: String): Flow>, BleException>> { + suspend operator fun invoke(serial: String): Flow>, BleException>> { return bleRepository.getTemperatureHistoryBySerial(serial)