Поправки по графикам
This commit is contained in:
parent
62205d27a0
commit
bd10b5b6b6
|
|
@ -1,4 +1,3 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ android {
|
|||
applicationId "llc.arma.ble"
|
||||
minSdk 24
|
||||
targetSdk 33
|
||||
versionCode 4
|
||||
versionName "1.2.1"
|
||||
versionCode 5
|
||||
versionName "1.2.2"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package llc.arma.ble.app.ui.screen.inspection.accelerometer
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.ModalBottomSheetValue
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
|
|
@ -32,6 +34,7 @@ enum class SheetPage {
|
|||
MEASURE, POWER, WRITE, HISTORY, ACCEL_MODE_EDIT, SPECTRE_MODE_EDIT, SPECTRE_EDIT, FREQUENCY_EDIT, AXIS_EDIT, FFT_MODE_EDIT
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun AccelerometerScreen(
|
||||
ble: Ble.Accelerometer,
|
||||
|
|
@ -42,6 +45,7 @@ fun AccelerometerScreen(
|
|||
|
||||
val bottomDialog = rememberBottomDialogState()
|
||||
|
||||
|
||||
LaunchedEffect(ble){
|
||||
viewModel.setEvent(AccelerometerContract.Event.OnBleChanged(ble))
|
||||
}
|
||||
|
|
@ -50,6 +54,15 @@ fun AccelerometerScreen(
|
|||
mutableStateOf<SheetPage?>(null)
|
||||
}
|
||||
|
||||
LaunchedEffect(
|
||||
key1 = bottomDialog.sheetState?.currentValue,
|
||||
block = {
|
||||
if(bottomDialog.sheetState?.currentValue == ModalBottomSheetValue.Hidden) {
|
||||
sheetPage = null
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
LaunchedEffect(sheetPage) {
|
||||
|
|
|
|||
|
|
@ -86,47 +86,6 @@ fun AccelSpectreEdit(
|
|||
modifier = Modifier
|
||||
) {
|
||||
|
||||
Box(
|
||||
modifier = Modifier.padding(
|
||||
vertical = 8.dp,
|
||||
horizontal = 8.dp
|
||||
)
|
||||
) {
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.clickable {
|
||||
onEvent(AccelerometerContract.Event.OnAccelViewModeEdit(next = AccelerometerContract.Event.OnAccelViewModeEdit.Next.SPECTRE))
|
||||
}
|
||||
.padding(8.dp)
|
||||
) {
|
||||
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = "Accel view mode"
|
||||
)
|
||||
Text(
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
text = accelMode.localized
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.KeyboardArrowDown,
|
||||
contentDescription = null
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier.padding(
|
||||
vertical = 8.dp,
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ import com.patrykandpatrick.vico.compose.chart.scroll.rememberChartScrollState
|
|||
import com.patrykandpatrick.vico.core.axis.AxisPosition
|
||||
import com.patrykandpatrick.vico.core.axis.formatter.AxisValueFormatter
|
||||
import com.patrykandpatrick.vico.core.entry.ChartEntry
|
||||
import com.patrykandpatrick.vico.core.entry.FloatEntry
|
||||
import com.patrykandpatrick.vico.core.extension.sumByFloat
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import llc.arma.ble.domain.common.ProgressState
|
||||
|
|
@ -82,23 +85,67 @@ fun AccelerometerHistory(
|
|||
|
||||
val title = when(state){
|
||||
is AccelerometerHistoryContract.State.Display -> {
|
||||
when (state.loadingHistoryState) {
|
||||
is ProgressState.Finished -> "${fftMode.localized} (${state.loadingHistoryState.data.size})"
|
||||
else -> fftMode.localized
|
||||
if (state.previousHistory !== null) {
|
||||
"${fftMode.localized} (${state.previousHistory.size})"
|
||||
}else {
|
||||
fftMode.localized
|
||||
}
|
||||
}
|
||||
AccelerometerHistoryContract.State.Exception -> fftMode.localized
|
||||
}
|
||||
|
||||
Text(
|
||||
Row(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
|
||||
Text(
|
||||
modifier = Modifier.padding(end = 16.dp),
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
|
||||
if(state is AccelerometerHistoryContract.State.Display) {
|
||||
|
||||
when (state.loadingHistoryState) {
|
||||
is ProgressState.Indeterminate -> {
|
||||
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(16.dp),
|
||||
strokeWidth = 2.dp,
|
||||
strokeCap = StrokeCap.Round,
|
||||
)
|
||||
|
||||
}
|
||||
is ProgressState.Progress -> {
|
||||
|
||||
val progressAnimDuration = 1500
|
||||
val progressAnimation by animateFloatAsState(
|
||||
targetValue = state.loadingHistoryState.value,
|
||||
animationSpec = tween(
|
||||
durationMillis = progressAnimDuration,
|
||||
easing = FastOutSlowInEasing
|
||||
)
|
||||
)
|
||||
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(16.dp),
|
||||
strokeWidth = 2.dp,
|
||||
strokeCap = StrokeCap.Round,
|
||||
progress = progressAnimation,
|
||||
)
|
||||
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
IconButton(
|
||||
onClick = {
|
||||
viewModel.setEvent(AccelerometerHistoryContract.Event.OnRefreshHistory(ble.serial, accelMode, fftAxis, fftMode, frequency, accelScale))
|
||||
viewModel.setEvent(AccelerometerHistoryContract.Event.OnStart(ble.serial, accelMode, fftAxis, fftMode, frequency, accelScale))
|
||||
},
|
||||
enabled = when(state){
|
||||
is AccelerometerHistoryContract.State.Display -> state.loadingHistoryState is ProgressState.Finished
|
||||
|
|
@ -127,7 +174,14 @@ fun AccelerometerHistory(
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
val axisValueFormatter = AxisValueFormatter<AxisPosition.Horizontal.Bottom> { value, chartValues ->
|
||||
(chartValues.chartEntryModel.entries.firstOrNull()
|
||||
?.getOrNull(value.toInt()) as? AccelerometerEntry)
|
||||
?.frequency?.let { String.format("%.1f", (it.toFloat() / 256f)) }
|
||||
.orEmpty()
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun Display(
|
||||
|
|
@ -139,100 +193,89 @@ fun Display(
|
|||
.fillMaxSize()
|
||||
) {
|
||||
|
||||
when (state.loadingHistoryState) {
|
||||
val data = if(state.loadingHistoryState is ProgressState.Finished){
|
||||
state.loadingHistoryState.data
|
||||
} else {
|
||||
state.previousHistory
|
||||
}
|
||||
|
||||
is ProgressState.Finished -> {
|
||||
val producer = remember {
|
||||
ChartEntryModelProducer(listOf<FloatEntry>())
|
||||
}
|
||||
|
||||
if(state.loadingHistoryState.data.isEmpty()){
|
||||
if(data != null){
|
||||
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
text = "Нет данных"
|
||||
)
|
||||
if(data.isEmpty()){
|
||||
|
||||
} else {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
text = "Нет данных"
|
||||
)
|
||||
|
||||
val producer = remember(state.loadingHistoryState.data) {
|
||||
state.loadingHistoryState.data.mapIndexed { index, measurePoint ->
|
||||
} else {
|
||||
|
||||
LaunchedEffect(data){
|
||||
producer.setEntries(
|
||||
data.mapIndexed { index, measurePoint ->
|
||||
AccelerometerEntry(measurePoint.frequency, index.toFloat(), measurePoint.value)
|
||||
}.let {
|
||||
ChartEntryModelProducer(it)
|
||||
}
|
||||
}
|
||||
|
||||
val axisValueFormatter =
|
||||
AxisValueFormatter<AxisPosition.Horizontal.Bottom> { value, chartValues ->
|
||||
(chartValues.chartEntryModel.entries.firstOrNull()
|
||||
?.getOrNull(value.toInt()) as? AccelerometerEntry)
|
||||
?.frequency?.let { String.format("%.1f", (it.toFloat() / 256f)) }
|
||||
.orEmpty()
|
||||
}
|
||||
|
||||
val lineChart = columnChart(
|
||||
spacing = 1.5.dp
|
||||
)
|
||||
|
||||
LaunchedEffect(state.loadingHistoryState.data){
|
||||
lineChart.bounds
|
||||
lineChart.setBounds(
|
||||
left = lineChart.bounds.left / 20,
|
||||
top = lineChart.bounds.top,
|
||||
right = lineChart.bounds.right / 20,
|
||||
bottom = lineChart.bounds.bottom,
|
||||
)
|
||||
}
|
||||
|
||||
val scrollState = rememberChartScrollState()
|
||||
|
||||
Chart(
|
||||
isZoomEnabled = true,
|
||||
chartScrollState = scrollState,
|
||||
chart = lineChart,
|
||||
chartModelProducer = producer,
|
||||
startAxis = startAxis(),
|
||||
bottomAxis = bottomAxis(
|
||||
tickLength = 0.dp,
|
||||
valueFormatter = axisValueFormatter,
|
||||
labelRotationDegrees = -90f,
|
||||
),
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
|
||||
|
||||
/*LaunchedEffect(scrollState.maxValue) {
|
||||
scrollState.scrollBy(scrollState.maxValue)
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
is ProgressState.Indeterminate -> {
|
||||
|
||||
CircularProgressIndicator(
|
||||
strokeCap = StrokeCap.Round,
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
val lineChart = columnChart(
|
||||
spacing = 1.5.dp
|
||||
)
|
||||
|
||||
}
|
||||
is ProgressState.Progress -> {
|
||||
val scrollState = rememberChartScrollState()
|
||||
|
||||
val progressAnimDuration = 1500
|
||||
val progressAnimation by animateFloatAsState(
|
||||
targetValue = state.loadingHistoryState.value,
|
||||
animationSpec = tween(
|
||||
durationMillis = progressAnimDuration,
|
||||
easing = FastOutSlowInEasing
|
||||
Chart(
|
||||
diffAnimationSpec = tween(0),
|
||||
isZoomEnabled = true,
|
||||
chartScrollState = scrollState,
|
||||
chart = lineChart,
|
||||
chartModelProducer = producer,
|
||||
startAxis = startAxis(),
|
||||
bottomAxis = bottomAxis(
|
||||
tickLength = 0.dp,
|
||||
valueFormatter = axisValueFormatter,
|
||||
labelRotationDegrees = -90f,
|
||||
),
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
when (state.loadingHistoryState) {
|
||||
is ProgressState.Indeterminate -> {
|
||||
|
||||
CircularProgressIndicator(
|
||||
strokeCap = StrokeCap.Round,
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
)
|
||||
|
||||
CircularProgressIndicator(
|
||||
strokeCap = StrokeCap.Round,
|
||||
progress = progressAnimation,
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
}
|
||||
is ProgressState.Progress -> {
|
||||
|
||||
val progressAnimDuration = 1500
|
||||
val progressAnimation by animateFloatAsState(
|
||||
targetValue = state.loadingHistoryState.value,
|
||||
animationSpec = tween(
|
||||
durationMillis = progressAnimDuration,
|
||||
easing = FastOutSlowInEasing
|
||||
)
|
||||
)
|
||||
|
||||
CircularProgressIndicator(
|
||||
strokeCap = StrokeCap.Round,
|
||||
progress = progressAnimation,
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -286,6 +329,7 @@ class AccelerometerHistoryContract {
|
|||
sealed class State : ViewState {
|
||||
|
||||
data class Display(
|
||||
val previousHistory : List<Ble.Accelerometer.MeasurePoint>?,
|
||||
val loadingHistoryState : ProgressState<List<Ble.Accelerometer.MeasurePoint>>
|
||||
) : State()
|
||||
|
||||
|
|
@ -306,10 +350,12 @@ class AccelerometerHistoryViewModel @Inject constructor(
|
|||
private val getAccelerometerSpectreBySerial: GetAccelerometerSpectreBySerial
|
||||
) : BaseViewModel<AccelerometerHistoryContract.State, AccelerometerHistoryContract.Event, AccelerometerHistoryContract.Effect>() {
|
||||
|
||||
private var job: Job? = null
|
||||
private var lastSerial: String? = null
|
||||
|
||||
override fun setInitialState() = AccelerometerHistoryContract.State.Display(
|
||||
ProgressState.Indeterminate
|
||||
loadingHistoryState = ProgressState.Indeterminate,
|
||||
previousHistory = null
|
||||
)
|
||||
|
||||
override fun handleEvents(event: AccelerometerHistoryContract.Event) {
|
||||
|
|
@ -323,44 +369,81 @@ class AccelerometerHistoryViewModel @Inject constructor(
|
|||
state: AccelerometerHistoryContract.State,
|
||||
event: AccelerometerHistoryContract.Event.OnStart
|
||||
) {
|
||||
|
||||
viewModelScope.launch {
|
||||
|
||||
if(state is AccelerometerHistoryContract.State.Display) {
|
||||
|
||||
//if(lastSerial != event.serial) {
|
||||
|
||||
lastSerial = event.serial
|
||||
lastSerial = event.serial
|
||||
|
||||
setState {
|
||||
AccelerometerHistoryContract.State.Display(ProgressState.Indeterminate)
|
||||
}
|
||||
setState {
|
||||
AccelerometerHistoryContract.State.Display(
|
||||
loadingHistoryState = ProgressState.Indeterminate,
|
||||
previousHistory = when(state.loadingHistoryState){
|
||||
is ProgressState.Finished -> state.loadingHistoryState.data
|
||||
is ProgressState.Indeterminate -> null
|
||||
is ProgressState.Progress -> null
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
getAccelerometerSpectreBySerial(
|
||||
serial = event.serial,
|
||||
accelMode = event.accelMode,
|
||||
fftAxis = event.fftAxis,
|
||||
fftMode = event.fftMode,
|
||||
frequency = event.frequency,
|
||||
accelScale = event.accelScale
|
||||
).onEach {
|
||||
it.fold(
|
||||
onSuccess = {
|
||||
setState {
|
||||
AccelerometerHistoryContract.State.Display(it)
|
||||
}
|
||||
},
|
||||
onFailure = {
|
||||
setState {
|
||||
AccelerometerHistoryContract.State.Exception
|
||||
}
|
||||
}
|
||||
)
|
||||
}.launchIn(this)
|
||||
|
||||
//}
|
||||
} else {
|
||||
setState {
|
||||
AccelerometerHistoryContract.State.Display(
|
||||
loadingHistoryState = ProgressState.Indeterminate,
|
||||
previousHistory = null
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
job?.cancel()
|
||||
job = getAccelerometerSpectreBySerial(
|
||||
serial = event.serial,
|
||||
accelMode = event.accelMode,
|
||||
fftAxis = event.fftAxis,
|
||||
fftMode = event.fftMode,
|
||||
frequency = event.frequency,
|
||||
accelScale = event.accelScale
|
||||
).onEach {
|
||||
|
||||
val currentState = viewState.value
|
||||
|
||||
Log.d("currentState", currentState.toString())
|
||||
|
||||
|
||||
if(currentState is AccelerometerHistoryContract.State.Display) {
|
||||
|
||||
it.fold(
|
||||
onSuccess = {
|
||||
setState {
|
||||
|
||||
AccelerometerHistoryContract.State.Display(
|
||||
loadingHistoryState = it,
|
||||
previousHistory = when (it) {
|
||||
is ProgressState.Finished -> {
|
||||
it.data
|
||||
}
|
||||
is ProgressState.Indeterminate -> currentState.previousHistory
|
||||
is ProgressState.Progress -> currentState.previousHistory
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
},
|
||||
onFailure = {
|
||||
setState {
|
||||
AccelerometerHistoryContract.State.Exception
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}.launchIn(this)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -369,7 +452,7 @@ class AccelerometerHistoryViewModel @Inject constructor(
|
|||
state: AccelerometerHistoryContract.State,
|
||||
event: AccelerometerHistoryContract.Event.OnRefreshHistory
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
/*viewModelScope.launch {
|
||||
|
||||
setState {
|
||||
AccelerometerHistoryContract.State.Display(ProgressState.Indeterminate)
|
||||
|
|
@ -397,7 +480,7 @@ class AccelerometerHistoryViewModel @Inject constructor(
|
|||
)
|
||||
}.launchIn(this)
|
||||
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -381,7 +381,7 @@ class AccelerometerMeasureViewModel @Inject constructor(
|
|||
z = ((9806.65f / (Short.MAX_VALUE / 2)) * it.z) * (accelScale.k / 2)
|
||||
)
|
||||
)
|
||||
}
|
||||
}.takeLast(10)
|
||||
AccelerometerMeasureContract.State.Display(dataList)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -579,63 +579,9 @@ class BleRepositoryImpl @Inject constructor(
|
|||
|
||||
if (checkPermission()) {
|
||||
|
||||
writeCharacteristic(
|
||||
device = it,
|
||||
serviceId = serviceUUID,
|
||||
characteristicId = accelerometerReadUUID,
|
||||
writeData = byteArrayOf(
|
||||
4,
|
||||
accelMode.sendData,
|
||||
accelScale.sendData,
|
||||
fftMode.sendData,
|
||||
fftAxis.sendData,
|
||||
frequency.sendData,
|
||||
1
|
||||
)
|
||||
).onFailure {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
send(Result.failure(BleException.PermissionDenied))
|
||||
}
|
||||
}
|
||||
|
||||
var result = false
|
||||
|
||||
while (result.not()){
|
||||
|
||||
writeCharacteristic(
|
||||
device = it,
|
||||
serviceId = serviceUUID,
|
||||
characteristicId = accelerometerReadUUID,
|
||||
writeData = byteArrayOf(4, 0)
|
||||
).onFailure {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
send(Result.failure(BleException.PermissionDenied))
|
||||
}
|
||||
}
|
||||
|
||||
readCharacteristic(
|
||||
device = it,
|
||||
serviceId = serviceUUID,
|
||||
characteristicId = accelerometerReadUUID
|
||||
).fold(
|
||||
onSuccess = { readData ->
|
||||
Log.d("read", readData.joinToString(", "))
|
||||
if(readData.isNotEmpty() && readData.first() == (0).toByte()){
|
||||
result = true
|
||||
} else {
|
||||
delay(200)
|
||||
}
|
||||
},
|
||||
onFailure = {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
send(Result.failure(BleException.PermissionDenied))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
gatt = it.connectGatt(app, false, ReadAccelerometerSpectreCallback(app) {
|
||||
gatt = it.connectGatt(app, false, ReadAccelerometerSpectreCallback(
|
||||
app, accelScale, accelMode, fftAxis, fftMode, frequency
|
||||
) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
send(it)
|
||||
}
|
||||
|
|
@ -919,8 +865,6 @@ class BleRepositoryImpl @Inject constructor(
|
|||
) {
|
||||
super.onServicesDiscovered(gatt, status)
|
||||
|
||||
Log.d("read", "onServicesDiscovered $status")
|
||||
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
|
||||
gatt.services?.firstOrNull { service ->
|
||||
|
|
@ -1068,8 +1012,6 @@ class BleRepositoryImpl @Inject constructor(
|
|||
) {
|
||||
super.onServicesDiscovered(gatt, status)
|
||||
|
||||
Log.d("write", "onServicesDiscovered $status")
|
||||
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
|
||||
gatt.services?.firstOrNull { service ->
|
||||
|
|
@ -1114,8 +1056,6 @@ class BleRepositoryImpl @Inject constructor(
|
|||
) {
|
||||
super.onCharacteristicWrite(gatt, characteristic, status)
|
||||
|
||||
Log.d("write", "onCharacteristicWrite $status")
|
||||
|
||||
if (checkPermission()) {
|
||||
|
||||
gatt.close()
|
||||
|
|
|
|||
|
|
@ -67,6 +67,8 @@ class ReadAccelerometerCallback(
|
|||
) {
|
||||
super.onServicesDiscovered(gatt, status)
|
||||
|
||||
Log.d("accel", "onServicesDiscovered")
|
||||
|
||||
if(status == BluetoothGatt.GATT_SUCCESS){
|
||||
|
||||
gatt.getService(serviceUUID)?.getCharacteristic(accelerometerReadUUID)?.let {
|
||||
|
|
@ -80,11 +82,9 @@ class ReadAccelerometerCallback(
|
|||
fftMode.sendData,
|
||||
fftAxis.sendData,
|
||||
frequency.sendData,
|
||||
2
|
||||
1
|
||||
)
|
||||
|
||||
Log.d("payload", payload.joinToString(" - "))
|
||||
|
||||
gatt.writeCharacteristic(it, payload)
|
||||
|
||||
} else {
|
||||
|
|
@ -107,14 +107,14 @@ class ReadAccelerometerCallback(
|
|||
) {
|
||||
super.onCharacteristicWrite(gatt, characteristic, status)
|
||||
|
||||
Log.d("accel", "request written")
|
||||
|
||||
if(status == BluetoothGatt.GATT_SUCCESS){
|
||||
|
||||
gatt.getService(serviceUUID)?.getCharacteristic(accelerometerReadUUID)?.let {
|
||||
|
||||
if (checkPermission()) {
|
||||
|
||||
Log.d("write", "enable notifications")
|
||||
|
||||
gatt.setCharacteristicNotification(it, true)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
|
|
@ -162,6 +162,8 @@ class ReadAccelerometerCallback(
|
|||
value: ByteArray,
|
||||
){
|
||||
|
||||
Log.d("accel", "notification")
|
||||
|
||||
val data = value.toList().chunked(2).map {
|
||||
it.toByteArray().get2byteShortAt(0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import android.app.Application
|
|||
import android.bluetooth.BluetoothGatt
|
||||
import android.bluetooth.BluetoothGattCallback
|
||||
import android.bluetooth.BluetoothGattCharacteristic
|
||||
import android.bluetooth.BluetoothGattDescriptor
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
|
|
@ -13,8 +14,14 @@ import llc.arma.ble.domain.Result
|
|||
import llc.arma.ble.domain.common.BleException
|
||||
import llc.arma.ble.domain.common.ProgressState
|
||||
import llc.arma.ble.domain.model.Ble
|
||||
import llc.arma.ble.domain.usecase.AccelScale
|
||||
import llc.arma.ble.domain.usecase.AccelViewMode
|
||||
import llc.arma.ble.domain.usecase.FftAxis
|
||||
import llc.arma.ble.domain.usecase.FftFrequency
|
||||
import llc.arma.ble.domain.usecase.FftViewMode
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder.LITTLE_ENDIAN
|
||||
import java.util.UUID
|
||||
|
||||
fun ByteArray.get2byteShortAt(idx: Int): Int {
|
||||
val shorts = ShortArray(1)
|
||||
|
|
@ -22,11 +29,21 @@ fun ByteArray.get2byteShortAt(idx: Int): Int {
|
|||
return shorts[0].toInt()//(this[0].toInt() + (this[1].toInt() shl 8)).toShort()
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
|
@ -50,13 +67,17 @@ class ReadAccelerometerSpectreCallback(
|
|||
) {
|
||||
super.onConnectionStateChange(gatt, status, newState)
|
||||
|
||||
Log.d("spectre", "onConnectionStateChange")
|
||||
|
||||
|
||||
|
||||
|
||||
if(status == BluetoothGatt.GATT_SUCCESS){
|
||||
|
||||
if(newState == BluetoothGatt.STATE_CONNECTED){
|
||||
|
||||
if (checkPermission()) {
|
||||
gatt.discoverServices()
|
||||
|
||||
} else {
|
||||
onResult(Result.failure(BleException.UnexpectedResponse))
|
||||
gatt.close()
|
||||
|
|
@ -77,38 +98,52 @@ class ReadAccelerometerSpectreCallback(
|
|||
status: Int
|
||||
) {
|
||||
super.onServicesDiscovered(gatt, status)
|
||||
Log.d("read", "discover ${status}")
|
||||
Log.d("spectre", "onServicesDiscovered")
|
||||
if(status == BluetoothGatt.GATT_SUCCESS){
|
||||
gatt.getService(serviceUUID)?.getCharacteristic(accelerometerHistoryReadUUID)?.let {
|
||||
|
||||
if (checkPermission()) {
|
||||
|
||||
Log.d("read", "discovered")
|
||||
|
||||
readProperty = Property.DATA_SIZE
|
||||
gatt.writeCharacteristic(it, byteArrayOf(2))
|
||||
|
||||
} else {
|
||||
|
||||
onResult(Result.failure(BleException.PermissionDenied))
|
||||
gatt.close()
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
onResult(Result.failure(BleException.UnexpectedResponse))
|
||||
enableNotifications(gatt)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//private var lastMeasureSystemTime: Long? = null
|
||||
private fun enableNotifications(
|
||||
gatt: BluetoothGatt
|
||||
){
|
||||
|
||||
gatt.getService(serviceUUID)?.getCharacteristic(accelerometerReadUUID)?.let {
|
||||
|
||||
if (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 bleRealTime: Long? = null
|
||||
private var frequencyInterval: Long? = null
|
||||
|
||||
private val resultAccelerometerPackage: MutableList<Float> = mutableListOf()
|
||||
|
|
@ -127,6 +162,42 @@ class ReadAccelerometerSpectreCallback(
|
|||
}
|
||||
}
|
||||
|
||||
@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) {
|
||||
Log.d("spectre", "changed")
|
||||
readProperty = Property.DATA_SIZE
|
||||
gatt.getService(serviceUUID).getCharacteristic(accelerometerHistoryReadUUID)?.let {
|
||||
gatt.writeCharacteristic(it, byteArrayOf(2))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCharacteristicRead(
|
||||
gatt: BluetoothGatt,
|
||||
characteristic: BluetoothGattCharacteristic,
|
||||
|
|
@ -144,6 +215,8 @@ class ReadAccelerometerSpectreCallback(
|
|||
status: Int
|
||||
){
|
||||
|
||||
Log.d("spectre", "onCharacteristicRead")
|
||||
|
||||
if(status == BluetoothGatt.GATT_SUCCESS){
|
||||
when(readProperty){
|
||||
Property.DATA_SIZE -> {
|
||||
|
|
@ -209,6 +282,7 @@ class ReadAccelerometerSpectreCallback(
|
|||
}
|
||||
|
||||
} else {
|
||||
|
||||
onResult(
|
||||
Result.success(
|
||||
ProgressState.Finished(
|
||||
|
|
@ -221,7 +295,9 @@ class ReadAccelerometerSpectreCallback(
|
|||
)
|
||||
)
|
||||
)
|
||||
gatt.close()
|
||||
|
||||
start(gatt)
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
@ -247,6 +323,7 @@ class ReadAccelerometerSpectreCallback(
|
|||
gatt.readCharacteristic(characteristic)
|
||||
|
||||
} else {
|
||||
|
||||
onResult(
|
||||
Result.success(
|
||||
ProgressState.Finished(
|
||||
|
|
@ -259,7 +336,9 @@ class ReadAccelerometerSpectreCallback(
|
|||
)
|
||||
)
|
||||
)
|
||||
gatt.close()
|
||||
|
||||
start(gatt)
|
||||
|
||||
}
|
||||
} else {
|
||||
onResult(Result.failure(BleException.UnexpectedResponse))
|
||||
|
|
@ -285,11 +364,73 @@ class ReadAccelerometerSpectreCallback(
|
|||
status: Int
|
||||
) {
|
||||
super.onCharacteristicWrite(gatt, characteristic, status)
|
||||
if(status == BluetoothGatt.GATT_SUCCESS){
|
||||
|
||||
Log.d("spectre", "request written $readProperty")
|
||||
|
||||
if(readProperty !== null) {
|
||||
|
||||
if (status == BluetoothGatt.GATT_SUCCESS) {
|
||||
|
||||
if (checkPermission()) {
|
||||
|
||||
gatt.readCharacteristic(characteristic)
|
||||
|
||||
} else {
|
||||
|
||||
onResult(Result.failure(BleException.PermissionDenied))
|
||||
gatt.close()
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
onResult(Result.failure(BleException.UnexpectedResponse))
|
||||
gatt.close()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
|
||||
super.onMtuChanged(gatt, mtu, status)
|
||||
Log.d("spectre", "mtu $mtu")
|
||||
}
|
||||
|
||||
override fun onDescriptorWrite(
|
||||
gatt: BluetoothGatt,
|
||||
descriptor: BluetoothGattDescriptor,
|
||||
status: Int
|
||||
) {
|
||||
|
||||
Log.d("spectre", "descriptor written")
|
||||
super.onDescriptorWrite(gatt, descriptor, status)
|
||||
start(gatt)
|
||||
|
||||
}
|
||||
|
||||
private fun start(
|
||||
gatt: BluetoothGatt,
|
||||
){
|
||||
|
||||
Log.d("spectre", "start")
|
||||
|
||||
gatt.getService(serviceUUID)?.getCharacteristic(accelerometerReadUUID)?.let {
|
||||
|
||||
if (checkPermission()) {
|
||||
|
||||
gatt.readCharacteristic(characteristic)
|
||||
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 {
|
||||
|
||||
|
|
@ -298,14 +439,13 @@ class ReadAccelerometerSpectreCallback(
|
|||
|
||||
}
|
||||
|
||||
} else {
|
||||
onResult(Result.failure(BleException.UnexpectedResponse))
|
||||
gatt.close()
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun checkPermission(): Boolean {
|
||||
private fun checkPermission(): Boolean {
|
||||
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_CONNECT) ==
|
||||
|
|
@ -320,7 +460,7 @@ class ReadAccelerometerSpectreCallback(
|
|||
}
|
||||
}
|
||||
|
||||
fun BluetoothGatt.writeCharacteristic(
|
||||
private fun BluetoothGatt.writeCharacteristic(
|
||||
characteristic: BluetoothGattCharacteristic,
|
||||
data: ByteArray
|
||||
): Result<Unit, BleException>{
|
||||
|
|
|
|||
|
|
@ -133,7 +133,6 @@ class ReadHistoryCallback(
|
|||
value: ByteArray,
|
||||
status: Int
|
||||
){
|
||||
Log.d("read", value[0].toString())
|
||||
|
||||
if(status == BluetoothGatt.GATT_SUCCESS){
|
||||
when(readProperty){
|
||||
|
|
|
|||
Loading…
Reference in New Issue