kotlin ble migration

This commit is contained in:
Vineyro 2024-06-14 17:01:41 +07:00
parent f65023b4c7
commit ab1046fd61
36 changed files with 859 additions and 1828 deletions

View File

@ -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"

View File

@ -7,8 +7,10 @@
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
<uses-permission android:maxSdkVersion="30" android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:maxSdkVersion="30" android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
@ -16,7 +18,9 @@
tools:targetApi="s" />
<uses-feature android:name="android.hardware.location.gps" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
<uses-feature android:name="android.hardware.bluetooth_le"
android:required="true"/>
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />

View File

@ -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
)
)

View File

@ -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,
)
)

View File

@ -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)
}

View File

@ -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)
}
}*/
}

View File

@ -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
)
)

View File

@ -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

View File

@ -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<Ble.Accelerometer.MeasurePoint>?,
val loadingHistoryState : ProgressState<List<Ble.Accelerometer.MeasurePoint>>
val previousHistory : List<Ble.Accelerometer.SpectrePoint>?,
val loadingHistoryState : ProgressState<List<Ble.Accelerometer.SpectrePoint>>
) : State()
object Exception : State()

View File

@ -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<MeasureData>
val measureHistory : List<Ble.Accelerometer.RealtimePoint>
) : State()
object Exception : State()

View File

@ -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 -> {}
}
}

View File

@ -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 сек."
)
}

View File

@ -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

View File

@ -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

View File

@ -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 сек."
)
}

View File

@ -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()
}

View File

@ -256,7 +256,7 @@ class TemperatureHistoryContract {
sealed class State : ViewState {
data class Display(
val loadingHistoryState : ProgressState<List<Ble.Thermometer.MeasurePoint>>
val loadingHistoryState : ProgressState<List<Ble.Thermometer.HistoryPoint>>
) : State()
object Exception : State()

File diff suppressed because it is too large Load Diff

View File

@ -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(

View File

@ -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<Result<ProgressState<List<Ble.Accelerometer.HistoryPoint>>, BleException>> {
return flow {
var lastMeasureSystemTime: Long? = null
var bleMeasureInterval: Long? = null
var bleRealTime: Long? = null
var bleLastMeasureTime: Long? = null
val resultTemperaturePackage: MutableList<Float> = mutableListOf()
val resultPackage: MutableList<Float> = mutableListOf()
val result = mutableListOf<List<UByte>>()
@ -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))
}

View File

@ -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<Result<Ble.Accelerometer.RealtimePoint, BleException>> {
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))
}
}
}

View File

@ -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<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, BleException>) -> Unit
) : BluetoothGattCallback() {
enum class Property {
DATA_SIZE, PACKAGE
}
private fun ByteArray.get4byteUIntAt(idx: Int) =
((this[idx + 3].toUInt() and 0xFFu) shl 24) or
((this[idx + 2].toUInt() and 0xFFu) shl 16) or
((this[idx + 1].toUInt() and 0xFFu) shl 8) or
(this[idx].toUInt() and 0xFFu)
private fun ByteArray.get2byteUIntAt(idx: Int) =
((this[idx + 1].toUInt() and 0xFFu) shl 8) or
(this[idx].toUInt() and 0xFFu)
private var readProperty: Property? = null
init {
onResult(Result.success(ProgressState.Indeterminate))
}
override fun onConnectionStateChange(
gatt: BluetoothGatt,
status: Int,
newState: Int
) {
super.onConnectionStateChange(gatt, status, newState)
if(status == BluetoothGatt.GATT_SUCCESS){
if(newState == BluetoothGatt.STATE_CONNECTED){
if (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<Float> = 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<Unit, BleException>{
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<Result<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, BleException>> {
): Flow<Result<ProgressState<List<Ble.Accelerometer.SpectrePoint>>, BleException>> {
return flow {
var initialValue: Long? = null
var frequencyInterval: Long? = null
val resultAccelerometerPackage: MutableList<Float> = 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<Float> = 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 {

View File

@ -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<ProgressState<List<Ble.Thermometer.MeasurePoint>>, BleException>) -> Unit
) : BluetoothGattCallback() {
enum class Property {
DATA_SIZE, PACKAGE
}
private fun ByteArray.get4byteUIntAt(idx: Int) =
((this[idx + 3].toUInt() and 0xFFu) shl 24) or
((this[idx + 2].toUInt() and 0xFFu) shl 16) or
((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<Float> = mutableListOf()
var expectedDataSize: Int? = null
override fun onCharacteristicRead(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
status: Int
) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
super.onCharacteristicRead(gatt, characteristic, status)
onCommonCharacteristicRead(gatt, characteristic, characteristic.value, status)
}
}
override fun onCharacteristicRead(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
value: ByteArray,
status: Int
) {
super.onCharacteristicRead(gatt, characteristic, value, status)
onCommonCharacteristicRead(gatt, characteristic, value, status)
}
@OptIn(ExperimentalUnsignedTypes::class)
private fun onCommonCharacteristicRead(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
value: ByteArray,
status: Int
){
if(status == BluetoothGatt.GATT_SUCCESS){
when(readProperty){
Property.DATA_SIZE -> {
if(value.contentEquals(byteArrayOf(0, 0))) {
onResult(
Result.success(
ProgressState.Finished(
emptyList()
)
)
)
gatt.close()
} else {
val writeData = mutableListOf(
1.toByte(),
0.toByte(),
0.toByte()
).apply {
addAll(value.toList())
}.toByteArray()
readProperty = Property.PACKAGE
gatt.writeCharacteristic(characteristic, writeData)
}
}
Property.PACKAGE -> {
if(value[0] == 250.toByte()){
bleMeasureInterval = value.get4byteUIntAt(4).toLong()
bleLastMeasureTime = value.get4byteUIntAt(8).toLong()
bleRealTime = value.get4byteUIntAt(12).toLong()
lastMeasureSystemTime = System.currentTimeMillis() - ((bleRealTime!! - bleLastMeasureTime!!) * 1_000)
val temperatureDataArray = value.toUByteArray().asList().subList(16, value.size)
resultTemperaturePackage.addAll(
temperatureDataArray.chunked(2).map {
it.toUByteArray().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<Unit, BleException>{
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<Result<ProgressState<List<Ble.Thermometer.MeasurePoint>>, BleException>> {
): Flow<Result<ProgressState<List<Ble.Thermometer.HistoryPoint>>, 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
)

View File

@ -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<Ble.Accelerometer.HistoryPoint>
): 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())
}
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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<ConnectedBleInfo>
fun getBleAroundFlow(): Flow<Result<List<BleInfo>, BleException>>
fun getBleAroundFlow(): Result<Flow<List<BleInfo>>, BleException>
suspend fun getBleBySerial(serial: String) : Result<Flow<Ble>, BleException>
suspend fun getTemperatureHistoryBySerial(serial: String): Flow<Result<ProgressState<List<Ble.Thermometer.MeasurePoint>>, BleException>>
suspend fun getTemperatureHistoryBySerial(serial: String): Flow<Result<ProgressState<List<Ble.Thermometer.HistoryPoint>>, BleException>>
suspend fun writeBle(serial: String, request: Ble.Thermometer.WriteRequest): Result<Unit, BleException>
@ -39,7 +36,7 @@ interface BleRepository {
fftAxis: FftAxis,
fftMode: FftViewMode,
frequency: FftFrequency
): Flow<Result<MeasureData, BleException>>
): Flow<Result<Ble.Accelerometer.RealtimePoint, BleException>>
suspend fun getAccelerometerSpectreBySerial(
serial: String,
@ -48,7 +45,7 @@ interface BleRepository {
fftAxis: FftAxis,
fftMode: FftViewMode,
frequency: FftFrequency
): Flow<Result<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, BleException>>
): Flow<Result<ProgressState<List<Ble.Accelerometer.SpectrePoint>>, BleException>>
suspend fun getAccelerometerHistoryBySerial(serial: String): Flow<Result<ProgressState<List<Ble.Accelerometer.HistoryPoint>>, BleException>>

View File

@ -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 {

View File

@ -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<Result<MeasureData, BleException>> {
): Flow<Result<Ble.Accelerometer.RealtimePoint, BleException>> {
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()
data class Accelerate(
val x: Float,
val y: Float,
val z: Float,
) : MeasureData()
data class Vibration(
val value: Float
) : MeasureData()
companion object
}
data class Accelerate(
val x: Float,
val y: Float,
val z: Float,
)

View File

@ -19,7 +19,7 @@ class GetAccelerometerSpectreBySerial @Inject constructor(
fftAxis: FftAxis,
fftMode: FftViewMode,
frequency: FftFrequency
): Flow<Result<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, BleException>> {
): Flow<Result<ProgressState<List<Ble.Accelerometer.SpectrePoint>>, 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
}

View File

@ -12,6 +12,6 @@ class GetBleAroundFlow @Inject constructor(
private val bleRepository: BleRepository
) {
operator fun invoke(): Flow<Result<List<BleInfo>, BleException>> = bleRepository.getBleAroundFlow()
operator fun invoke(): Result<Flow<List<BleInfo>>, BleException> = bleRepository.getBleAroundFlow()
}

View File

@ -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<ConnectedBleInfo>{
return bleRepository.getConnectedBle()
}
}

View File

@ -12,7 +12,7 @@ class GetTemperatureHistoryBySerial @Inject constructor(
private val bleRepository: BleRepository
) {
suspend operator fun invoke(serial: String): Flow<Result<ProgressState<List<Ble.Thermometer.MeasurePoint>>, BleException>> {
suspend operator fun invoke(serial: String): Flow<Result<ProgressState<List<Ble.Thermometer.HistoryPoint>>, BleException>> {
return bleRepository.getTemperatureHistoryBySerial(serial)