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" applicationId "llc.arma.ble"
minSdk 26 minSdk 26
targetSdk 34 targetSdk 34
versionCode 18 versionCode 22
versionName "1.2.18" versionName "1.3.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { vectorDrawables {
@ -86,8 +86,8 @@ dependencies {
kapt('com.google.dagger:hilt-android-compiler:2.45') kapt('com.google.dagger:hilt-android-compiler:2.45')
kapt("androidx.hilt:hilt-compiler:1.0.0") kapt("androidx.hilt:hilt-compiler:1.0.0")
implementation 'no.nordicsemi.android.kotlin.ble:scanner:1.0.14' implementation 'no.nordicsemi.android.kotlin.ble:scanner:1.0.19'
implementation 'no.nordicsemi.android.kotlin.ble:client:1.0.14' implementation 'no.nordicsemi.android.kotlin.ble:client:1.0.19'
implementation "com.google.accompanist:accompanist-permissions:0.26.3-beta" implementation "com.google.accompanist:accompanist-permissions:0.26.3-beta"

View File

@ -7,8 +7,10 @@
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" /> android:maxSdkVersion="30" />
<uses-permission android:maxSdkVersion="30" android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
<uses-permission android:maxSdkVersion="30" android:name="android.permission.ACCESS_COARSE_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_CONNECT"/>
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" <uses-permission android:name="android.permission.BLUETOOTH_SCAN"
@ -16,7 +18,9 @@
tools:targetApi="s" /> tools:targetApi="s" />
<uses-feature android:name="android.hardware.location.gps" /> <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_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <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) tx = txMapper.map(input.state.tx)
), ),
accelerometerState = BleView.Accelerometer.AccelerometerState( accelerometerState = BleView.Accelerometer.AccelerometerState(
saveHistory = input.accelerometerState.saveHistory, saveHistorySettings = input.accelerometerState.saveHistorySettings,
historyInterval = input.accelerometerState.historyInterval historyInterval = input.accelerometerState.historyInterval
) )
) )

View File

@ -39,7 +39,7 @@ class BleViewMapper @Inject constructor(
tx = txMapper.map(input.state.tx) tx = txMapper.map(input.state.tx)
), ),
accelerometerState = Ble.Accelerometer.AccelerometerState( accelerometerState = Ble.Accelerometer.AccelerometerState(
saveHistory = input.accelerometerState.saveHistory, saveHistorySettings = input.accelerometerState.saveHistory,
historyInterval = input.accelerometerState.historyInterval, historyInterval = input.accelerometerState.historyInterval,
) )
) )

View File

@ -5,8 +5,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.model.BleInfo import llc.arma.ble.domain.model.BleInfo
import llc.arma.ble.domain.usecase.AccelScale
import llc.arma.ble.domain.usecase.AccelViewMode
sealed class BleView( sealed class BleView(
val info: BleInfo val info: BleInfo
@ -19,10 +17,10 @@ sealed class BleView(
) : BleView(info) { ) : BleView(info) {
class AccelerometerState( class AccelerometerState(
saveHistory: Ble.Accelerometer.History, saveHistorySettings: Ble.Accelerometer.HistorySettings,
historyInterval: Long, historyInterval: Long,
) { ) {
var saveHistory by mutableStateOf(saveHistory) var saveHistory by mutableStateOf(saveHistorySettings)
var historyInterval by mutableStateOf(historyInterval) var historyInterval by mutableStateOf(historyInterval)
} }

View File

@ -24,7 +24,23 @@ class BleListViewModel @Inject constructor(
var job: Job? = null 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?.cancel()
job = getBleAroundFlow().onEach { job = getBleAroundFlow().onEach {
@ -46,7 +62,7 @@ class BleListViewModel @Inject constructor(
delay(30_000) delay(30_000)
} }*/
} }

View File

@ -82,8 +82,8 @@ class AccelerometerViewModel @Inject constructor(
var saveHistory = state.accelerometer.accelerometerState.saveHistory var saveHistory = state.accelerometer.accelerometerState.saveHistory
if(saveHistory is Ble.Accelerometer.History.Enabled){ if(saveHistory is Ble.Accelerometer.HistorySettings.Enabled){
saveHistory = Ble.Accelerometer.History.Enabled( saveHistory = Ble.Accelerometer.HistorySettings.Enabled(
mode = event.mode, mode = event.mode,
scale = saveHistory.scale, scale = saveHistory.scale,
detailed = saveHistory.detailed detailed = saveHistory.detailed
@ -105,7 +105,7 @@ class AccelerometerViewModel @Inject constructor(
var saveHistory = state.accelerometer.accelerometerState.saveHistory 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) saveHistory = saveHistory.copy(scale = event.scale)
} }
@ -199,7 +199,7 @@ class AccelerometerViewModel @Inject constructor(
if(event.save){ if(event.save){
state.accelerometer.accelerometerState.saveHistory = Ble.Accelerometer.History.Enabled( state.accelerometer.accelerometerState.saveHistory = Ble.Accelerometer.HistorySettings.Enabled(
scale = AccelScale.S_2, scale = AccelScale.S_2,
mode = AccelViewMode.ACCELERATION, mode = AccelViewMode.ACCELERATION,
detailed = true detailed = true
@ -211,7 +211,7 @@ class AccelerometerViewModel @Inject constructor(
} else { } 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( val writeRequest = Ble.Accelerometer.WriteRequest(
tx = if(newBle.state.tx == state.origin.state.tx) null else newBle.state.tx, tx = if(newBle.state.tx == state.origin.state.tx) null else newBle.state.tx,
saveHistory = if(newBle.accelerometerState.saveHistory == state.origin.accelerometerState.saveHistory) null else newBle.accelerometerState.saveHistory, 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, 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 tx = request.writeRequest.tx ?: state.origin.state.tx
), ),
accelerometerState = currentState.origin.accelerometerState.copy( accelerometerState = currentState.origin.accelerometerState.copy(
saveHistory = request.writeRequest.saveHistory saveHistorySettings = request.writeRequest.saveHistorySettings
?: currentState.origin.accelerometerState.saveHistory ?: 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.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape 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.MaterialTheme
import androidx.compose.material3.RadioButton import androidx.compose.material3.RadioButton
import androidx.compose.material3.Surface 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.app.ui.screen.inspection.accelerometer.AccelerometerContract
import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.usecase.AccelScale 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 @Composable
fun AccelScaleEdit( fun AccelScaleEdit(
@ -39,7 +30,7 @@ fun AccelScaleEdit(
state.accelScale state.accelScale
AccelerometerContract.Event.Next.HISTORY -> { AccelerometerContract.Event.Next.HISTORY -> {
val history = state.accelerometer.accelerometerState.saveHistory val history = state.accelerometer.accelerometerState.saveHistory
if (history is Ble.Accelerometer.History.Enabled) if (history is Ble.Accelerometer.HistorySettings.Enabled)
history.scale history.scale
else { else {
state.accelScale state.accelScale

View File

@ -76,7 +76,6 @@ fun AccelerometerSpectre(
DisposableEffect(key1 = "ble", effect = { DisposableEffect(key1 = "ble", effect = {
onDispose { onDispose {
Log.d("history", "dispose")
viewModel.setEvent(AccelerometerSpectreContract.Event.StopMeasure) viewModel.setEvent(AccelerometerSpectreContract.Event.StopMeasure)
} }
@ -340,8 +339,8 @@ class AccelerometerSpectreContract {
sealed class State : ViewState { sealed class State : ViewState {
data class Display( data class Display(
val previousHistory : List<Ble.Accelerometer.MeasurePoint>?, val previousHistory : List<Ble.Accelerometer.SpectrePoint>?,
val loadingHistoryState : ProgressState<List<Ble.Accelerometer.MeasurePoint>> val loadingHistoryState : ProgressState<List<Ble.Accelerometer.SpectrePoint>>
) : State() ) : State()
object Exception : 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.material.icons.rounded.Refresh
import androidx.compose.ui.draw.rotate import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.StrokeCap 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 androidx.compose.ui.text.style.TextAlign
import com.patrykandpatrick.vico.compose.axis.horizontal.bottomAxis import com.patrykandpatrick.vico.compose.axis.horizontal.bottomAxis
import com.patrykandpatrick.vico.compose.chart.line.lineChart 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.Job
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach 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.AccelScale
import llc.arma.ble.domain.usecase.AccelViewMode import llc.arma.ble.domain.usecase.AccelViewMode
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.FftAxis
import llc.arma.ble.domain.usecase.FftFrequency import llc.arma.ble.domain.usecase.FftFrequency
import llc.arma.ble.domain.usecase.FftViewMode import llc.arma.ble.domain.usecase.FftViewMode
@ -151,37 +149,40 @@ fun Display(
xProducer.setEntries(state.measureHistory.mapIndexed { index, measurePoint -> xProducer.setEntries(state.measureHistory.mapIndexed { index, measurePoint ->
when(measurePoint){ when(measurePoint){
is MeasureData.Accelerate -> { is Ble.Accelerometer.RealtimePoint.Common ->
FloatEntry(index.toFloat(), measurePoint.x ) FloatEntry(index.toFloat(), measurePoint.x )
} is Ble.Accelerometer.RealtimePoint.Vibration ->
is MeasureData.Vibration -> FloatEntry(index.toFloat(), measurePoint.value) FloatEntry(index.toFloat(), measurePoint.value)
is MeasureData.Angle -> { is Ble.Accelerometer.RealtimePoint.Angle ->
FloatEntry(index.toFloat(), measurePoint.xAngle ) FloatEntry(index.toFloat(), measurePoint.x )
} is Ble.Accelerometer.RealtimePoint.Rotation ->
FloatEntry(index.toFloat(), measurePoint.angle )
} }
}) })
yProducer.setEntries(state.measureHistory.mapIndexed { index, measurePoint -> yProducer.setEntries(state.measureHistory.mapIndexed { index, measurePoint ->
when(measurePoint){ when(measurePoint){
is MeasureData.Accelerate -> { is Ble.Accelerometer.RealtimePoint.Common ->
FloatEntry(index.toFloat(), measurePoint.y ) FloatEntry(index.toFloat(), measurePoint.y )
} is Ble.Accelerometer.RealtimePoint.Vibration ->
is MeasureData.Vibration -> FloatEntry(index.toFloat(), measurePoint.value) FloatEntry(index.toFloat(), measurePoint.value)
is MeasureData.Angle -> { is Ble.Accelerometer.RealtimePoint.Angle ->
FloatEntry(index.toFloat(), measurePoint.yAngle ) FloatEntry(index.toFloat(), measurePoint.y)
} is Ble.Accelerometer.RealtimePoint.Rotation ->
FloatEntry(index.toFloat(), measurePoint.tmp)
} }
}) })
zProducer.setEntries(state.measureHistory.mapIndexed { index, measurePoint -> zProducer.setEntries(state.measureHistory.mapIndexed { index, measurePoint ->
when(measurePoint){ when(measurePoint){
is MeasureData.Accelerate -> { is Ble.Accelerometer.RealtimePoint.Common ->
FloatEntry(index.toFloat(), measurePoint.z ) FloatEntry(index.toFloat(), measurePoint.z)
} is Ble.Accelerometer.RealtimePoint.Vibration ->
is MeasureData.Vibration -> FloatEntry(index.toFloat(), measurePoint.value) FloatEntry(index.toFloat(), measurePoint.value)
is MeasureData.Angle -> { is Ble.Accelerometer.RealtimePoint.Angle ->
FloatEntry(index.toFloat(), measurePoint.zAngle ) 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() val lastMeasure = state.measureHistory.lastOrNull()
/*when(lastMeasure){ if(lastMeasure is Ble.Accelerometer.RealtimePoint.Common) {
is MeasureData.Accelerate -> TODO()
is MeasureData.Angle -> TODO()
is MeasureData.Vibration -> {
}
null -> {}
}*/
if(lastMeasure is MeasureData.Accelerate) {
when(state.mode){ when(state.mode){
ROTATIONS -> { ROTATIONS -> {
@ -524,7 +516,7 @@ class AccelerometerAccelContract {
data class Display( data class Display(
val mode: AccelViewMode, val mode: AccelViewMode,
val measureHistory : List<MeasureData> val measureHistory : List<Ble.Accelerometer.RealtimePoint>
) : State() ) : State()
object Exception : 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.FftViewMode
import llc.arma.ble.domain.usecase.GetAccelerometerHistoryBySerial import llc.arma.ble.domain.usecase.GetAccelerometerHistoryBySerial
import llc.arma.ble.domain.usecase.GetBleBySerial import llc.arma.ble.domain.usecase.GetBleBySerial
import llc.arma.ble.domain.usecase.MeasureData
import java.util.Date import java.util.Date
class AccelEntry( class AccelEntry(
@ -208,7 +207,7 @@ fun Display(
xProducer.setEntries(state.loadingHistoryState.data.mapIndexed { index, measurePoint -> xProducer.setEntries(state.loadingHistoryState.data.mapIndexed { index, measurePoint ->
when(measurePoint){ when(measurePoint){
is Ble.Accelerometer.HistoryPoint.Accelerate -> { is Ble.Accelerometer.HistoryPoint.Acceleration -> {
AccelEntry(measurePoint.date, index.toFloat(), measurePoint.x ) AccelEntry(measurePoint.date, index.toFloat(), measurePoint.x )
} }
is Ble.Accelerometer.HistoryPoint.Vibration -> { is Ble.Accelerometer.HistoryPoint.Vibration -> {
@ -217,12 +216,16 @@ fun Display(
is Ble.Accelerometer.HistoryPoint.Angle -> { is Ble.Accelerometer.HistoryPoint.Angle -> {
AccelEntry(measurePoint.date, index.toFloat(), measurePoint.x ) 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 -> yProducer.setEntries(state.loadingHistoryState.data.mapIndexed { index, measurePoint ->
when(measurePoint){ when(measurePoint){
is Ble.Accelerometer.HistoryPoint.Accelerate -> { is Ble.Accelerometer.HistoryPoint.Acceleration -> {
AccelEntry(measurePoint.date, index.toFloat(), measurePoint.y ) AccelEntry(measurePoint.date, index.toFloat(), measurePoint.y )
} }
is Ble.Accelerometer.HistoryPoint.Vibration -> { is Ble.Accelerometer.HistoryPoint.Vibration -> {
@ -231,12 +234,15 @@ fun Display(
is Ble.Accelerometer.HistoryPoint.Angle -> { is Ble.Accelerometer.HistoryPoint.Angle -> {
AccelEntry(measurePoint.date, index.toFloat(), measurePoint.y ) 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 -> zProducer.setEntries(state.loadingHistoryState.data.mapIndexed { index, measurePoint ->
when(measurePoint){ when(measurePoint){
is Ble.Accelerometer.HistoryPoint.Accelerate -> { is Ble.Accelerometer.HistoryPoint.Acceleration -> {
AccelEntry(measurePoint.date, index.toFloat(), measurePoint.z ) AccelEntry(measurePoint.date, index.toFloat(), measurePoint.z )
} }
is Ble.Accelerometer.HistoryPoint.Vibration -> { is Ble.Accelerometer.HistoryPoint.Vibration -> {
@ -245,6 +251,9 @@ fun Display(
is Ble.Accelerometer.HistoryPoint.Angle -> { is Ble.Accelerometer.HistoryPoint.Angle -> {
AccelEntry(measurePoint.date, index.toFloat(), measurePoint.z ) 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() val lastMeasure = state.loadingHistoryState.data.lastOrNull()
if((lastMeasure is Ble.Accelerometer.HistoryPoint.Vibration).not()) { when(lastMeasure){
is Ble.Accelerometer.HistoryPoint.Acceleration,
Column() { is Ble.Accelerometer.HistoryPoint.Angle -> {
Column {
Text(text = "Ось X:") 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.model.BleView
import llc.arma.ble.app.ui.screen.BleInfoView import llc.arma.ble.app.ui.screen.BleInfoView
import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract
import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract
import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.model.Ble
@Composable @Composable
@ -115,7 +114,7 @@ fun DisplayState(
val history = ble.accelerometerState.saveHistory val history = ble.accelerometerState.saveHistory
if(history is Ble.Accelerometer.History.Enabled){ if(history is Ble.Accelerometer.HistorySettings.Enabled){
Text( Text(
color = MaterialTheme.colorScheme.secondary, color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
@ -133,7 +132,7 @@ fun DisplayState(
} }
Switch( Switch(
checked = ble.accelerometerState.saveHistory is Ble.Accelerometer.History.Enabled, checked = ble.accelerometerState.saveHistory is Ble.Accelerometer.HistorySettings.Enabled,
onCheckedChange = { onCheckedChange = {
onEvent(AccelerometerContract.Event.OnSaveHistoryChanged(it)) onEvent(AccelerometerContract.Event.OnSaveHistoryChanged(it))
} }
@ -168,13 +167,14 @@ fun DisplayState(
text = "Интервал измерений" text = "Интервал измерений"
) )
val hours = ble.accelerometerState.historyInterval / 1000 / 60 / 60 val hours = ble.accelerometerState.historyInterval / millisInHour
val minutes = (ble.accelerometerState.historyInterval - ( hours * 1000 * 60 * 60 )) / 1000 / 60 val minutes = (ble.accelerometerState.historyInterval - (hours * millisInHour)) / millisInMinute
val seconds = (ble.accelerometerState.historyInterval - (hours * millisInHour) - (minutes * millisInMinute)) / millisInSecond
Text( Text(
color = MaterialTheme.colorScheme.secondary, color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.bodyMedium, 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.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.KeyboardArrowDown import androidx.compose.material.icons.rounded.KeyboardArrowDown
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@ -21,10 +17,6 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract
import llc.arma.ble.domain.model.Ble 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 @Composable
fun HistoryEdit( fun HistoryEdit(
@ -34,9 +26,9 @@ fun HistoryEdit(
val history = state.accelerometer.accelerometerState.saveHistory val history = state.accelerometer.accelerometerState.saveHistory
val detailed = if (history is Ble.Accelerometer.History.Enabled) history.detailed else false val detailed = if (history is Ble.Accelerometer.HistorySettings.Enabled) history.detailed else false
val accelMode = if (history is Ble.Accelerometer.History.Enabled) history.mode else state.accelViewMode val accelMode = if (history is Ble.Accelerometer.HistorySettings.Enabled) history.mode else state.accelViewMode
val accelScale = if (history is Ble.Accelerometer.History.Enabled) history.scale else state.accelScale val accelScale = if (history is Ble.Accelerometer.HistorySettings.Enabled) history.scale else state.accelScale
Column( Column(
modifier = Modifier modifier = Modifier

View File

@ -1,5 +1,6 @@
package llc.arma.ble.app.ui.screen.inspection.accelerometer.view package llc.arma.ble.app.ui.screen.inspection.accelerometer.view
import android.util.Log
import androidx.compose.animation.* import androidx.compose.animation.*
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
@ -26,15 +27,17 @@ fun IntervalEdit(
} }
val maxInterval = 10 * 24 * 60 * 60 * 1000 val maxInterval = 10 * 24 * 60 * 60 * 1000
val minInterval = 10_000
if(value > maxInterval){ if(value > maxInterval){
value = maxInterval value = maxInterval
} }
if(value < 1){ if(value < minInterval){
value = 1 * 60 * 1000 value = minInterval
} }
val maxSeconds = maxInterval / millisInSecond
val maxMinutes = maxInterval / millisInMinute val maxMinutes = maxInterval / millisInMinute
val maxHours = maxInterval / millisInHour val maxHours = maxInterval / millisInHour
val maxDays = maxInterval / millisInDay val maxDays = maxInterval / millisInDay
@ -42,6 +45,7 @@ fun IntervalEdit(
val dayValue = value / millisInDay val dayValue = value / millisInDay
val hourValue = (value - (dayValue * millisInDay)) / millisInHour val hourValue = (value - (dayValue * millisInDay)) / millisInHour
val minutesValue = (value - (dayValue * millisInDay) - (hourValue * millisInHour)) / millisInMinute val minutesValue = (value - (dayValue * millisInDay) - (hourValue * millisInHour)) / millisInMinute
val secondsValue = (value - (dayValue * millisInDay) - (hourValue * millisInHour) - (minutesValue * millisInMinute)) / millisInSecond
Column( Column(
modifier = Modifier modifier = Modifier
@ -65,13 +69,13 @@ fun IntervalEdit(
range = -1..maxDays, range = -1..maxDays,
value = dayValue, value = dayValue,
onValueChanged = { onValueChanged = {
value = (it * millisInDay) + (hourValue * millisInHour) + (minutesValue * millisInMinute) value = (it * millisInDay) + (hourValue * millisInHour) + (minutesValue * millisInMinute) + (secondsValue * millisInSecond)
} }
) )
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
Text(text = "Дни") Text(text = "Д.")
Spacer(modifier = Modifier.width(16.dp)) Spacer(modifier = Modifier.width(16.dp))
@ -79,13 +83,13 @@ fun IntervalEdit(
range = -1..maxHours, range = -1..maxHours,
value = hourValue, value = hourValue,
onValueChanged = { onValueChanged = {
value = (it * millisInHour) + (dayValue * millisInDay) + (minutesValue * millisInMinute) value = (it * millisInHour) + (dayValue * millisInDay) + (minutesValue * millisInMinute) + (secondsValue * millisInSecond)
} }
) )
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
Text(text = "Часы") Text(text = "Ч.")
Spacer(modifier = Modifier.width(16.dp)) Spacer(modifier = Modifier.width(16.dp))
@ -93,13 +97,27 @@ fun IntervalEdit(
range = -1..maxMinutes, range = -1..maxMinutes,
value = minutesValue, value = minutesValue,
onValueChanged = { onValueChanged = {
value = (it * millisInMinute) + (dayValue * millisInDay) + (hourValue * millisInHour) value = (secondsValue * millisInSecond) + (it * millisInMinute) + (dayValue * millisInDay) + (hourValue * millisInHour)
} }
) )
Spacer(modifier = Modifier.width(8.dp)) 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 millisInHour = millisInMinute * 60
const val millisInDay = millisInHour * 24 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.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
import llc.arma.ble.R import llc.arma.ble.R
import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract 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.app.ui.screen.inspection.thermometer.localizedName
import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.model.Ble
@ -45,7 +42,7 @@ fun Write(
when (state) { when (state) {
is AccelerometerContract.State.Display.WriteState.DisplayPreview -> { 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 { state.writeRequest.tx?.let {
Box( Box(
@ -82,7 +79,7 @@ fun Write(
} }
} }
state.writeRequest.saveHistory?.let { state.writeRequest.saveHistorySettings?.let {
Box( Box(
modifier = Modifier.padding( modifier = Modifier.padding(
@ -109,8 +106,8 @@ fun Write(
color = MaterialTheme.colorScheme.secondary, color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
text = when(it){ text = when(it){
Ble.Accelerometer.History.Disabled -> "Выключено" Ble.Accelerometer.HistorySettings.Disabled -> "Выключено"
is Ble.Accelerometer.History.Enabled -> "Включено" is Ble.Accelerometer.HistorySettings.Enabled -> "Включено"
} }
) )
@ -142,17 +139,18 @@ fun Write(
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)
) { ) {
val hours = it / 1000 / 60 / 60
val minutes = (it - ( hours * 1000 * 60 * 60 )) / 1000 / 60
Text( Text(
text = "Интервал измерений" text = "Интервал измерений"
) )
val hours = it / millisInHour
val minutes = (it - (hours * millisInHour)) / millisInMinute
val seconds = (it - (hours * millisInHour) - (minutes * millisInMinute)) / millisInSecond
Text( Text(
color = MaterialTheme.colorScheme.secondary, color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.bodyMedium, 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 val Ble.BleState.TX.localizedName: String
get() { get() {
return when(this){ return when(this){
Ble.BleState.TX.MINUS_40 -> -40 Ble.BleState.TX.MINUS_40 -> "-40"
Ble.BleState.TX.MINUS_20 -> -20 Ble.BleState.TX.MINUS_20 -> "-20"
Ble.BleState.TX.MINUS_16 -> -16 Ble.BleState.TX.MINUS_16 -> "-16"
Ble.BleState.TX.MINUS_12 -> -12 Ble.BleState.TX.MINUS_12 -> "-12"
Ble.BleState.TX.MINUS_8 -> -8 Ble.BleState.TX.MINUS_8 -> "-8"
Ble.BleState.TX.MINUS_4 -> -4 Ble.BleState.TX.MINUS_4 -> "-4"
Ble.BleState.TX.ZERO -> 0 Ble.BleState.TX.ZERO -> "0"
Ble.BleState.TX.PLUS_3 -> 3 Ble.BleState.TX.PLUS_3 -> "3"
Ble.BleState.TX.PLUS_4 -> 4 Ble.BleState.TX.PLUS_4 -> "4"
}.toString() }.toString()
} }

View File

@ -256,7 +256,7 @@ class TemperatureHistoryContract {
sealed class State : ViewState { sealed class State : ViewState {
data class Display( data class Display(
val loadingHistoryState : ProgressState<List<Ble.Thermometer.MeasurePoint>> val loadingHistoryState : ProgressState<List<Ble.Thermometer.HistoryPoint>>
) : State() ) : State()
object Exception : 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.app.Application
import android.content.Intent import android.content.Intent
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import llc.arma.ble.R
import llc.arma.ble.domain.repository.EmailRepository 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.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.util.UUID
import javax.inject.Inject import javax.inject.Inject
class EmailRepositoryImpl @Inject constructor( class EmailRepositoryImpl @Inject constructor(

View File

@ -10,56 +10,36 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
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.Result
import llc.arma.ble.domain.common.BleException import llc.arma.ble.domain.common.BleException
import llc.arma.ble.domain.common.ProgressState import llc.arma.ble.domain.common.ProgressState
import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.usecase.AccelScale import llc.arma.ble.domain.usecase.AccelScale
import llc.arma.ble.domain.usecase.AccelViewMode 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.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) @OptIn(ExperimentalUnsignedTypes::class)
fun readAccelerometerHistory( fun getAccelerometerHistory(
address: String, address: String,
mode: AccelViewMode,
scale: AccelScale,
app: Application, app: Application,
): Flow<Result<ProgressState<List<Ble.Accelerometer.HistoryPoint>>, BleException>> { ): Flow<Result<ProgressState<List<Ble.Accelerometer.HistoryPoint>>, BleException>> {
return flow { return flow {
var lastMeasureSystemTime: Long? = null var lastMeasureSystemTime: Long? = null
var bleMeasureInterval: Long? = null var bleMeasureInterval: Long? = null
var bleRealTime: Long? = null var bleRealTime: Long? = null
var bleLastMeasureTime: Long? = null var bleLastMeasureTime: Long? = null
val resultTemperaturePackage: MutableList<Float> = mutableListOf() val resultPackage: MutableList<Float> = mutableListOf()
val result = mutableListOf<List<UByte>>() val result = mutableListOf<List<UByte>>()
@ -67,10 +47,21 @@ fun readAccelerometerHistory(
if(app.checkPermission()) { if(app.checkPermission()) {
val connection =
ClientBleGatt.connect(app, address, CoroutineScope(Dispatchers.Default))
try { try {
val connection = val specData = connection.discoverServices()
ClientBleGatt.connect(app, address, CoroutineScope(Dispatchers.Default)) .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() val characteristic = connection.discoverServices()
.findService(serviceUUID) .findService(serviceUUID)
@ -78,7 +69,6 @@ fun readAccelerometerHistory(
if(characteristic != null) { if(characteristic != null) {
characteristic.write(DataByteArray.from(2)) characteristic.write(DataByteArray.from(2))
var value = characteristic.read().value var value = characteristic.read().value
@ -89,7 +79,7 @@ fun readAccelerometerHistory(
} else { } else {
Log.d("expected data size", value.get2byteUIntAt(0).toString()) var nextPackageDataCount = value.get2byteUIntAt(0)
val writeData = mutableListOf( val writeData = mutableListOf(
1.toByte(), 1.toByte(),
@ -101,7 +91,7 @@ fun readAccelerometerHistory(
characteristic.write(DataByteArray(writeData)) characteristic.write(DataByteArray(writeData))
value = characteristic.read().value value = characteristic.read().value
var nextPackageDataCount = value.get2byteUIntAt(2)
while (nextPackageDataCount.toInt() != 0) { while (nextPackageDataCount.toInt() != 0) {
@ -112,7 +102,7 @@ fun readAccelerometerHistory(
bleRealTime = value.get4byteUIntAt(12).toLong() bleRealTime = value.get4byteUIntAt(12).toLong()
lastMeasureSystemTime = lastMeasureSystemTime =
System.currentTimeMillis() - ((bleRealTime!! - bleLastMeasureTime!!) * 1_000) System.currentTimeMillis() - ((bleRealTime - bleLastMeasureTime) * 1_000)
value.toUByteArray().asList().subList(16, value.size) value.toUByteArray().asList().subList(16, value.size)
@ -125,23 +115,17 @@ fun readAccelerometerHistory(
result.add(value.toUByteArray().toList()) result.add(value.toUByteArray().toList())
nextPackageDataCount = value.get2byteUIntAt(2) nextPackageDataCount = value.get2byteUIntAt(2)
resultTemperaturePackage.addAll( resultPackage.addAll(
temperatureDataArray.chunked(2).map { temperatureDataArray.chunked(2).map {
it.toUByteArray().toByteArray().get2byteShortAt().toFloat() it.toUByteArray().toByteArray().get2byteShortAt().toFloat()
}.toMutableList() }.toMutableList()
) )
Log.d(
"received data size",
(temperatureDataArray.chunked(2).size).toString()
)
Log.d("next data size", nextPackageDataCount.toString())
expectedDataSize = expectedDataSize =
nextPackageDataCount.toInt() + resultTemperaturePackage.size nextPackageDataCount.toInt() + resultPackage.size
emit(Result.success(ProgressState.Progress(0f / expectedDataSize.toFloat()))) 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)) characteristic.write(DataByteArray.from(5))
value = characteristic.read().value value = characteristic.read().value
@ -152,13 +136,20 @@ fun readAccelerometerHistory(
Result.success( Result.success(
ProgressState.Finished( ProgressState.Finished(
when (mode) { 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.ACCELERATION,
AccelViewMode.PEAK_ACCELERATION, AccelViewMode.PEAK_ACCELERATION,
AccelViewMode.RMS -> { AccelViewMode.RMS -> {
resultTemperaturePackage.chunked(3).withIndex().map { resultPackage.chunked(3).withIndex().map {
Ble.Accelerometer.HistoryPoint.Angle( 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, x = (it.value[0] * scale.k) / Short.MAX_VALUE,
y = (it.value[1] * scale.k) / Short.MAX_VALUE, y = (it.value[1] * scale.k) / Short.MAX_VALUE,
z = (it.value[2] * scale.k) / Short.MAX_VALUE z = (it.value[2] * scale.k) / Short.MAX_VALUE
@ -167,29 +158,29 @@ fun readAccelerometerHistory(
} }
AccelViewMode.ANGLE -> { AccelViewMode.ANGLE -> {
resultTemperaturePackage.chunked(3).withIndex().map { resultPackage.chunked(3).withIndex().map {
Ble.Accelerometer.HistoryPoint.Angle( Ble.Accelerometer.HistoryPoint.Angle(
date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), date = lastMeasureSystemTime!! - (((resultPackage.size - 1) - it.index) * bleMeasureInterval!!),
x = calculateAngle( x = calculateAngle(
it.value[2], it.value[2],
it.value[1] it.value[1]
) * 180f / Math.PI.toFloat(), ),
y = calculateAngle( y = calculateAngle(
it.value[2], it.value[2],
it.value[0] it.value[0]
) * 180f / Math.PI.toFloat(), ),
z = calculateAngle( z = calculateAngle(
it.value[0], it.value[0],
it.value[1] it.value[1]
) * 180f / Math.PI.toFloat() )
) )
} }
} }
AccelViewMode.VIBRATION -> { AccelViewMode.VIBRATION -> {
resultTemperaturePackage.withIndex().map { resultPackage.withIndex().map {
Ble.Accelerometer.HistoryPoint.Vibration( 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 value = (it.value * scale.k) / Short.MAX_VALUE
) )
} }
@ -210,13 +201,16 @@ fun readAccelerometerHistory(
} }
} catch (err: Throwable) { } catch (err: Throwable) {
err.printStackTrace()
emit(Result.failure(BleException.UnexpectedResponse)) emit(Result.failure(BleException.UnexpectedResponse))
} finally {
connection.close()
} }
} else { } else {
emit(Result.failure(BleException.PermissionDenied)) 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 package llc.arma.ble.data
import android.Manifest
import android.app.Application import android.app.Application
import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCallback import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor import android.bluetooth.BluetoothGattDescriptor
import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.util.Log import android.util.Log
import androidx.core.app.ActivityCompat
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
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.Result
import llc.arma.ble.domain.common.BleException import llc.arma.ble.domain.common.BleException
import llc.arma.ble.domain.common.ProgressState 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.FftAxis
import llc.arma.ble.domain.usecase.FftFrequency import llc.arma.ble.domain.usecase.FftFrequency
import llc.arma.ble.domain.usecase.FftViewMode 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.client.main.callback.ClientBleGatt
import no.nordicsemi.android.kotlin.ble.core.data.util.DataByteArray
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.nio.ByteOrder.LITTLE_ENDIAN import java.nio.ByteOrder.LITTLE_ENDIAN
import java.util.UUID 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( fun readAccelerometerSpectre(
address: String, address: String,
app: Application, app: Application,
@ -466,72 +41,65 @@ fun readAccelerometerSpectre(
fftAxis: FftAxis, fftAxis: FftAxis,
fftMode: FftViewMode, fftMode: FftViewMode,
frequency: FftFrequency, frequency: FftFrequency,
): Flow<Result<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, BleException>> { ): Flow<Result<ProgressState<List<Ble.Accelerometer.SpectrePoint>>, BleException>> {
return flow { return flow {
var initialValue: Long? = null
var frequencyInterval: Long? = null
val resultAccelerometerPackage: MutableList<Float> = mutableListOf()
var expectedDataSize: Int? = null
if(app.checkPermission()) { if(app.checkPermission()) {
val connection =
ClientBleGatt.connect(app, address, CoroutineScope(Dispatchers.Default))
try { try {
val connection = val service = connection.discoverServices()
ClientBleGatt.connect(app, address, CoroutineScope(Dispatchers.Default)) .findService(serviceUUID) ?: throw IllegalStateException()
val characteristic = connection.discoverServices() val characteristic = service.findCharacteristic(accelerometerReadUUID)
.findService(serviceUUID)
?.findCharacteristic(accelerometerReadUUID)
?: throw IllegalStateException() ?: throw IllegalStateException()
val historyCharacteristic = connection.discoverServices() val historyCharacteristic = service.findCharacteristic(accelerometerHistoryReadUUID)
.findService(serviceUUID)
?.findCharacteristic(accelerometerHistoryReadUUID)
?: throw IllegalStateException() ?: 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(
characteristic.write(DataByteArray(byteArrayOf( 4,
4, accelMode.sendData,
accelMode.sendData, accelScale.sendData,
accelScale.sendData, fftMode.sendData,
fftMode.sendData, fftAxis.sendData,
fftAxis.sendData, frequency.sendData,
frequency.sendData, 2
2 ))
))) )
Log.d("notification", "1")
val notifications = characteristic.getNotifications() val notifications = characteristic.getNotifications()
notifications.collect { 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)) historyCharacteristic.write(DataByteArray.from(2))
var value = historyCharacteristic.read().value var value = historyCharacteristic.read().value
Log.d("value", value.joinToString(separator = ""))
Log.d("value", value.get2byteUIntAt(0).toString())
if (value.contentEquals(byteArrayOf(0, 0))) { if (value.contentEquals(byteArrayOf(0, 0))) {
emit(Result.success(ProgressState.Finished(emptyList()))) emit(Result.success(ProgressState.Finished(emptyList())))
} else { } else {
Log.d("expected data size", value.get2byteUIntAt(0).toString()) var nextPackageDataCount = value.get2byteUIntAt(0)
val writeData = mutableListOf( val writeData = mutableListOf(
1.toByte(), 1.toByte(),
@ -543,7 +111,7 @@ fun readAccelerometerSpectre(
historyCharacteristic.write(DataByteArray(writeData)) historyCharacteristic.write(DataByteArray(writeData))
value = historyCharacteristic.read().value value = historyCharacteristic.read().value
var nextPackageDataCount = value.get2byteUIntAt(2)
while (nextPackageDataCount.toInt() != 0) { while (nextPackageDataCount.toInt() != 0) {
@ -569,8 +137,8 @@ fun readAccelerometerSpectre(
expectedDataSize = nextPackageDataCount.toInt() + resultAccelerometerPackage.size expectedDataSize = nextPackageDataCount.toInt() + resultAccelerometerPackage.size
emit(Result.success(ProgressState.Progress(0f / expectedDataSize!!.toFloat()))) emit(Result.success(ProgressState.Progress(0f / expectedDataSize.toFloat())))
emit(Result.success(ProgressState.Progress(resultAccelerometerPackage.size.toFloat() / expectedDataSize!!.toFloat()))) emit(Result.success(ProgressState.Progress(resultAccelerometerPackage.size.toFloat() / expectedDataSize.toFloat())))
historyCharacteristic.write(DataByteArray.from(5)) historyCharacteristic.write(DataByteArray.from(5))
value = historyCharacteristic.read().value value = historyCharacteristic.read().value
@ -581,7 +149,7 @@ fun readAccelerometerSpectre(
Result.success( Result.success(
ProgressState.Finished( ProgressState.Finished(
resultAccelerometerPackage.withIndex().map { resultAccelerometerPackage.withIndex().map {
Ble.Accelerometer.MeasurePoint( Ble.Accelerometer.SpectrePoint(
frequency = frequencyInterval!! * it.index + initialValue!!, frequency = frequencyInterval!! * it.index + initialValue!!,
value = (it.value * accelScale.k) / Short.MAX_VALUE value = (it.value * accelScale.k) / Short.MAX_VALUE
) )
@ -611,6 +179,10 @@ fun readAccelerometerSpectre(
err.printStackTrace() err.printStackTrace()
emit(Result.failure(BleException.UnexpectedResponse)) emit(Result.failure(BleException.UnexpectedResponse))
} finally {
connection.close()
} }
} else { } else {

View File

@ -1,339 +1,31 @@
package llc.arma.ble.data package llc.arma.ble.data
import android.Manifest
import android.app.Application import android.app.Application
import android.bluetooth.BluetoothGatt import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCallback import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothGattCharacteristic import android.bluetooth.BluetoothGattCharacteristic
import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.util.Log import android.util.Log
import androidx.core.app.ActivityCompat
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
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.Result
import llc.arma.ble.domain.common.BleException import llc.arma.ble.domain.common.BleException
import llc.arma.ble.domain.common.ProgressState import llc.arma.ble.domain.common.ProgressState
import llc.arma.ble.domain.model.Ble 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.client.main.callback.ClientBleGatt
import no.nordicsemi.android.kotlin.ble.core.data.util.DataByteArray
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)
}
}
}
@OptIn(ExperimentalUnsignedTypes::class) @OptIn(ExperimentalUnsignedTypes::class)
fun readThermometerHistory( fun readThermometerHistory(
address: String, address: String,
app: Application, app: Application,
): Flow<Result<ProgressState<List<Ble.Thermometer.MeasurePoint>>, BleException>> { ): Flow<Result<ProgressState<List<Ble.Thermometer.HistoryPoint>>, BleException>> {
return flow { return flow {
@ -369,8 +61,6 @@ fun readThermometerHistory(
} else { } else {
Log.d("expected data size", value.get2byteUIntAt(0).toString())
val writeData = mutableListOf( val writeData = mutableListOf(
1.toByte(), 1.toByte(),
0.toByte(), 0.toByte(),
@ -410,12 +100,6 @@ fun readThermometerHistory(
}.toMutableList() }.toMutableList()
) )
Log.d(
"received data size",
(temperatureDataArray.chunked(2).size).toString()
)
Log.d("next data size", nextPackageDataCount.toString())
expectedDataSize = expectedDataSize =
nextPackageDataCount.toInt() + resultTemperaturePackage.size nextPackageDataCount.toInt() + resultTemperaturePackage.size
@ -431,7 +115,7 @@ fun readThermometerHistory(
Result.success( Result.success(
ProgressState.Finished( ProgressState.Finished(
resultTemperaturePackage.withIndex().map { resultTemperaturePackage.withIndex().map {
Ble.Thermometer.MeasurePoint( Ble.Thermometer.HistoryPoint(
date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!),
value = it.value value = it.value
) )

View File

@ -1,10 +1,9 @@
package llc.arma.ble.data package llc.arma.ble.data
import android.R.attr.src
import android.app.Application import android.app.Application
import android.icu.text.SimpleDateFormat import android.icu.text.SimpleDateFormat
import android.os.Environment import android.os.Environment
import android.os.FileUtils import android.util.Log
import llc.arma.ble.R import llc.arma.ble.R
import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.repository.XlsxRepository import llc.arma.ble.domain.repository.XlsxRepository
@ -15,7 +14,6 @@ import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
import java.io.FileOutputStream import java.io.FileOutputStream
import java.util.Date import java.util.Date
import java.util.UUID
import javax.inject.Inject import javax.inject.Inject
@ -28,12 +26,14 @@ class XlsxRepositoryImpl @Inject constructor(
data: List<Ble.Accelerometer.HistoryPoint> data: List<Ble.Accelerometer.HistoryPoint>
): File { ): File {
val fileNameDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS") val fileNameDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm")
val fileName = "$bleName ${fileNameDateFormat.format(Date(System.currentTimeMillis()))}.xlsx" val fileName = "${fileNameDateFormat.format(Date(System.currentTimeMillis()))}.xlsx".replace(' ', '_')
val formatter = SimpleDateFormat("dd.MM.yyyy HH:mm") val formatter = SimpleDateFormat("dd.MM.yyyy HH:mm")
File(application.filesDir.absolutePath).mkdirs()
val mailFile = File(application.filesDir, fileName) val mailFile = File(application.filesDir, fileName)
mailFile.createNewFile() mailFile.createNewFile()
when(data.firstOrNull()){ when(data.firstOrNull()){
@ -73,7 +73,7 @@ class XlsxRepositoryImpl @Inject constructor(
x.setCellValue(value.value.toDouble()) x.setCellValue(value.value.toDouble())
} }
is Ble.Accelerometer.HistoryPoint.Accelerate -> { is Ble.Accelerometer.HistoryPoint.Acceleration -> {
dateX.setCellValue(formatter.format(Date(value.date))) dateX.setCellValue(formatter.format(Date(value.date)))
dateY.setCellValue(formatter.format(Date(value.date))) dateY.setCellValue(formatter.format(Date(value.date)))
dateZ.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()) 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 val accelerometerState: AccelerometerState
): Ble(info) { ): Ble(info) {
sealed class History { sealed class HistorySettings {
data class Enabled( data class Enabled(
val scale: AccelScale, val scale: AccelScale,
val mode: AccelViewMode, val mode: AccelViewMode,
val detailed: Boolean val detailed: Boolean
) : History() ) : HistorySettings()
object Disabled : History() object Disabled : HistorySettings()
} }
data class WriteRequest( data class WriteRequest(
val tx: BleState.TX?, val tx: BleState.TX?,
val saveHistory: History?, val saveHistorySettings: HistorySettings?,
val historyInterval: Long? val historyInterval: Long?
) )
@ -38,7 +38,12 @@ sealed class Ble(
val value: Float val value: Float
) : HistoryPoint() ) : HistoryPoint()
class Accelerate ( class Rotation (
val date: Long,
val value: Long
) : HistoryPoint()
class Acceleration (
val date: Long, val date: Long,
val x: Float, val x: Float,
val y: 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 frequency: Long,
val value: Float val value: Float
) )
data class AccelerometerState( data class AccelerometerState(
val saveHistory: History, val saveHistorySettings: HistorySettings,
val historyInterval: Long val historyInterval: Long
) )
@ -83,7 +114,7 @@ sealed class Ble(
val thermometerState: ThermometerState val thermometerState: ThermometerState
) : Ble(info) { ) : Ble(info) {
class MeasurePoint( class HistoryPoint(
val date: Long, val date: Long,
val value: Float val value: Float
) )
@ -107,7 +138,9 @@ sealed class Ble(
){ ){
enum class TX { 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.model.ConnectedBleInfo
import llc.arma.ble.domain.usecase.AccelScale 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.FftAxis
import llc.arma.ble.domain.usecase.FftFrequency import llc.arma.ble.domain.usecase.FftFrequency
import llc.arma.ble.domain.usecase.FftViewMode import llc.arma.ble.domain.usecase.FftViewMode
interface BleRepository { interface BleRepository {
fun getConnectedBle(): List<ConnectedBleInfo> fun getBleAroundFlow(): Result<Flow<List<BleInfo>>, BleException>
fun getBleAroundFlow(): Flow<Result<List<BleInfo>, BleException>>
suspend fun getBleBySerial(serial: String) : Result<Flow<Ble>, 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> suspend fun writeBle(serial: String, request: Ble.Thermometer.WriteRequest): Result<Unit, BleException>
@ -39,7 +36,7 @@ interface BleRepository {
fftAxis: FftAxis, fftAxis: FftAxis,
fftMode: FftViewMode, fftMode: FftViewMode,
frequency: FftFrequency frequency: FftFrequency
): Flow<Result<MeasureData, BleException>> ): Flow<Result<Ble.Accelerometer.RealtimePoint, BleException>>
suspend fun getAccelerometerSpectreBySerial( suspend fun getAccelerometerSpectreBySerial(
serial: String, serial: String,
@ -48,7 +45,7 @@ interface BleRepository {
fftAxis: FftAxis, fftAxis: FftAxis,
fftMode: FftViewMode, fftMode: FftViewMode,
frequency: FftFrequency 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>> 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 package llc.arma.ble.domain.repository
import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.usecase.MeasureData
import java.io.File import java.io.File
interface XlsxRepository { interface XlsxRepository {

View File

@ -3,6 +3,7 @@ package llc.arma.ble.domain.usecase
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import llc.arma.ble.domain.Result import llc.arma.ble.domain.Result
import llc.arma.ble.domain.common.BleException import llc.arma.ble.domain.common.BleException
import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.repository.BleRepository import llc.arma.ble.domain.repository.BleRepository
import javax.inject.Inject import javax.inject.Inject
@ -17,7 +18,7 @@ class GetAccelerometerMeasureBySerialFlow @Inject constructor(
fftAxis: FftAxis, fftAxis: FftAxis,
fftMode: FftViewMode, fftMode: FftViewMode,
frequency: FftFrequency frequency: FftFrequency
): Flow<Result<MeasureData, BleException>> { ): Flow<Result<Ble.Accelerometer.RealtimePoint, BleException>> {
return bleRepository.getAccelerometerMeasureBySerialFlow(serial, accelScale, accelMode, fftAxis, fftMode, frequency) return bleRepository.getAccelerometerMeasureBySerialFlow(serial, accelScale, accelMode, fftAxis, fftMode, frequency)
@ -26,38 +27,15 @@ class GetAccelerometerMeasureBySerialFlow @Inject constructor(
} }
enum class AccelViewMode { 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) { 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( companion object
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()
} }
data class Accelerate(
val x: Float,
val y: Float,
val z: Float,
)

View File

@ -19,7 +19,7 @@ class GetAccelerometerSpectreBySerial @Inject constructor(
fftAxis: FftAxis, fftAxis: FftAxis,
fftMode: FftViewMode, fftMode: FftViewMode,
frequency: FftFrequency 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) return bleRepository.getAccelerometerSpectreBySerial(serial, accelScale, accelMode, fftAxis, fftMode, frequency)
@ -28,13 +28,19 @@ class GetAccelerometerSpectreBySerial @Inject constructor(
} }
enum class FftFrequency { 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 { enum class FftViewMode {
SPECTRE, X, Y, Z SPECTRE, X, Y, Z;
companion object
} }
enum class FftAxis { 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 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 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) return bleRepository.getTemperatureHistoryBySerial(serial)