From a059cacda9d5f0e94a131c1498587d24475c2ba7 Mon Sep 17 00:00:00 2001 From: Vineyro Date: Tue, 11 Jun 2024 17:00:17 +0700 Subject: [PATCH] add rotations --- app/build.gradle | 7 +- .../accelerometer/view/AccelFrequencyEdit.kt | 10 - .../accelerometer/view/AccelViewEdit.kt | 1 + .../accelerometer/view/AcceleromterAccel.kt | 262 ++-- .../java/llc/arma/ble/app/ui/theme/Theme.kt | 13 - .../llc/arma/ble/data/BleRepositoryImpl.kt | 1253 ++++++++--------- .../ble/data/ReadAccelerometerCallback.kt | 297 ---- .../arma/ble/data/ReadAccelerometerHistory.kt | 228 +++ .../data/ReadAccelerometerHistoryCallback.kt | 410 ------ .../data/ReadAccelerometerSpectreCallback.kt | 219 ++- .../data/ReadTemperatureHistoryCallback.kt | 25 +- .../ble/data/WriteAccelerometerCallback.kt | 245 ---- .../llc/arma/ble/data/WriteBeaconCallback.kt | 209 --- .../arma/ble/data/WriteThermometerCallback.kt | 19 +- .../GetAccelerometerMeasureBySerialFlow.kt | 2 +- 15 files changed, 1209 insertions(+), 1991 deletions(-) delete mode 100644 app/src/main/java/llc/arma/ble/data/ReadAccelerometerCallback.kt create mode 100644 app/src/main/java/llc/arma/ble/data/ReadAccelerometerHistory.kt delete mode 100644 app/src/main/java/llc/arma/ble/data/ReadAccelerometerHistoryCallback.kt delete mode 100644 app/src/main/java/llc/arma/ble/data/WriteAccelerometerCallback.kt delete mode 100644 app/src/main/java/llc/arma/ble/data/WriteBeaconCallback.kt diff --git a/app/build.gradle b/app/build.gradle index 81f1633..959d359 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId "llc.arma.ble" minSdk 26 targetSdk 34 - versionCode 17 - versionName "1.2.17" + versionCode 18 + versionName "1.2.18" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { @@ -86,6 +86,9 @@ dependencies { kapt('com.google.dagger:hilt-android-compiler:2.45') kapt("androidx.hilt:hilt-compiler:1.0.0") + implementation 'no.nordicsemi.android.kotlin.ble:scanner:1.0.14' + implementation 'no.nordicsemi.android.kotlin.ble:client:1.0.14' + implementation "com.google.accompanist:accompanist-permissions:0.26.3-beta" implementation "com.patrykandpatrick.vico:core:1.7.1" diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelFrequencyEdit.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelFrequencyEdit.kt index 73cc0af..7102b2e 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelFrequencyEdit.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelFrequencyEdit.kt @@ -24,16 +24,6 @@ import llc.arma.ble.domain.usecase.FftAxis import llc.arma.ble.domain.usecase.FftFrequency import llc.arma.ble.domain.usecase.FftViewMode -/*val AccelViewMode.localized: String - get() { - return when(this){ - ACCELERATION -> "Ускорение" - PEAK_ACCELERATION -> "Пиковое ускорение" - RMS -> "Среднеквадратичное ускорение" - ANGLE -> "Угол" - } - }*/ - @Composable fun AccelFrequencyEdit( state: AccelerometerContract.State.Display, diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelViewEdit.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelViewEdit.kt index 0cf428f..e763384 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelViewEdit.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelViewEdit.kt @@ -25,6 +25,7 @@ val AccelViewMode.localized: String RMS -> "Среднеквадратичное ускорение" VIBRATION -> "Вибрация" ANGLE -> "Угол" + ROTATIONS -> "Обороты" } } diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterAccel.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterAccel.kt index e05195c..f8400c2 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterAccel.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterAccel.kt @@ -41,6 +41,7 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import llc.arma.ble.domain.usecase.AccelScale import llc.arma.ble.domain.usecase.AccelViewMode +import llc.arma.ble.domain.usecase.AccelViewMode.* import llc.arma.ble.domain.usecase.MeasureData import llc.arma.ble.domain.usecase.FftAxis import llc.arma.ble.domain.usecase.FftFrequency @@ -194,126 +195,181 @@ fun Display( val lastMeasure = state.measureHistory.lastOrNull() + /*when(lastMeasure){ + is MeasureData.Accelerate -> TODO() + is MeasureData.Angle -> TODO() + is MeasureData.Vibration -> { + + } + null -> {} + }*/ + if(lastMeasure is MeasureData.Accelerate) { - if(state.mode == AccelViewMode.ANGLE){ - - Column( - verticalArrangement = Arrangement.spacedBy(16.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - + when(state.mode){ + ROTATIONS -> { Column( - modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(16.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - Text(text = "Ось X: ${lastMeasure.x}") - - Spacer(modifier = Modifier.height(8.dp)) - - Angle( - angle = lastMeasure.x, - modifier = Modifier.weight(1f), - ) - - } - - Column( - modifier = Modifier.weight(1f), - horizontalAlignment = Alignment.CenterHorizontally - ) { Text(text = "Ось Y: ${lastMeasure.y}") - Spacer(modifier = Modifier.height(8.dp)) - - Angle( + Column( modifier = Modifier.weight(1f), - angle = lastMeasure.y + horizontalAlignment = Alignment.CenterHorizontally + ) { + + Text(text = "Ось X: ${lastMeasure.x}") + + Spacer(modifier = Modifier.height(8.dp)) + + Angle( + angle = lastMeasure.x, + modifier = Modifier.weight(1f), + ) + + } + + Text(text = "Ось Z:") + Chart( + chart = lineChart, + chartModelProducer = zProducer, + startAxis = startAxis(), + bottomAxis = bottomAxis(), + modifier = Modifier + .fillMaxWidth() + .weight(1f), + autoScaleUp = AutoScaleUp.None, + diffAnimationSpec = tween(0), + chartScrollSpec = rememberChartScrollSpec( + initialScroll = InitialScroll.End, + autoScrollCondition = AutoScrollCondition.OnModelSizeIncreased, + autoScrollAnimationSpec = tween(0) + ) ) } - + } + ANGLE -> { Column( - modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(16.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - Text(text = "Ось Z: ${lastMeasure.z}") - - Spacer(modifier = Modifier.height(8.dp)) - - Angle( + Column( modifier = Modifier.weight(1f), - angle = lastMeasure.z + horizontalAlignment = Alignment.CenterHorizontally + ) { + + Text(text = "Ось X: ${lastMeasure.x}") + + Spacer(modifier = Modifier.height(8.dp)) + + Angle( + angle = lastMeasure.x, + modifier = Modifier.weight(1f), + ) + + } + + Column( + modifier = Modifier.weight(1f), + horizontalAlignment = Alignment.CenterHorizontally + ) { + + Text(text = "Ось Y: ${lastMeasure.y}") + + Spacer(modifier = Modifier.height(8.dp)) + + Angle( + modifier = Modifier.weight(1f), + angle = lastMeasure.y + ) + + } + + Column( + modifier = Modifier.weight(1f), + horizontalAlignment = Alignment.CenterHorizontally + ) { + + Text(text = "Ось Z: ${lastMeasure.z}") + + Spacer(modifier = Modifier.height(8.dp)) + + Angle( + modifier = Modifier.weight(1f), + angle = lastMeasure.z + ) + + } + + } + } + else -> { + + Column() { + + Text(text = "Ось X:") + + Chart( + chart = lineChart, + chartModelProducer = xProducer, + startAxis = startAxis(), + bottomAxis = bottomAxis(), + modifier = Modifier + .fillMaxWidth() + .weight(1f), + autoScaleUp = AutoScaleUp.None, + diffAnimationSpec = tween(0), + chartScrollSpec = rememberChartScrollSpec( + initialScroll = InitialScroll.End, + autoScrollCondition = AutoScrollCondition.OnModelSizeIncreased, + autoScrollAnimationSpec = tween(0) + ) + ) + + Text(text = "Ось Y:") + Chart( + chart = lineChart, + chartModelProducer = yProducer, + startAxis = startAxis(), + bottomAxis = bottomAxis(), + modifier = Modifier + .fillMaxWidth() + .weight(1f), + autoScaleUp = AutoScaleUp.None, + diffAnimationSpec = tween(0), + chartScrollSpec = rememberChartScrollSpec( + initialScroll = InitialScroll.End, + autoScrollCondition = AutoScrollCondition.OnModelSizeIncreased, + autoScrollAnimationSpec = tween(0) + ) + ) + + Text(text = "Ось Z:") + Chart( + chart = lineChart, + chartModelProducer = zProducer, + startAxis = startAxis(), + bottomAxis = bottomAxis(), + modifier = Modifier + .fillMaxWidth() + .weight(1f), + autoScaleUp = AutoScaleUp.None, + diffAnimationSpec = tween(0), + chartScrollSpec = rememberChartScrollSpec( + initialScroll = InitialScroll.End, + autoScrollCondition = AutoScrollCondition.OnModelSizeIncreased, + autoScrollAnimationSpec = tween(0) + ) ) } } - - } else { - - Column() { - - Text(text = "Ось X:") - - Chart( - chart = lineChart, - chartModelProducer = xProducer, - startAxis = startAxis(), - bottomAxis = bottomAxis(), - modifier = Modifier - .fillMaxWidth() - .weight(1f), - autoScaleUp = AutoScaleUp.None, - diffAnimationSpec = tween(0), - chartScrollSpec = rememberChartScrollSpec( - initialScroll = InitialScroll.End, - autoScrollCondition = AutoScrollCondition.OnModelSizeIncreased, - autoScrollAnimationSpec = tween(0) - ) - ) - - Text(text = "Ось Y:") - Chart( - chart = lineChart, - chartModelProducer = yProducer, - startAxis = startAxis(), - bottomAxis = bottomAxis(), - modifier = Modifier - .fillMaxWidth() - .weight(1f), - autoScaleUp = AutoScaleUp.None, - diffAnimationSpec = tween(0), - chartScrollSpec = rememberChartScrollSpec( - initialScroll = InitialScroll.End, - autoScrollCondition = AutoScrollCondition.OnModelSizeIncreased, - autoScrollAnimationSpec = tween(0) - ) - ) - - Text(text = "Ось Z:") - Chart( - chart = lineChart, - chartModelProducer = zProducer, - startAxis = startAxis(), - bottomAxis = bottomAxis(), - modifier = Modifier - .fillMaxWidth() - .weight(1f), - autoScaleUp = AutoScaleUp.None, - diffAnimationSpec = tween(0), - chartScrollSpec = rememberChartScrollSpec( - initialScroll = InitialScroll.End, - autoScrollCondition = AutoScrollCondition.OnModelSizeIncreased, - autoScrollAnimationSpec = tween(0) - ) - ) - - } - } } else { @@ -349,7 +405,7 @@ fun Display( } @Composable -public fun Angle( +fun Angle( modifier: Modifier = Modifier, angle: Float ) { @@ -493,7 +549,7 @@ class AccelerometerAccelViewModel @Inject constructor( private var lastSerial: String? = null override fun setInitialState() = AccelerometerAccelContract.State.Display( - mode = AccelViewMode.ACCELERATION, + mode = ACCELERATION, measureHistory = emptyList() ) @@ -515,7 +571,7 @@ class AccelerometerAccelViewModel @Inject constructor( setState { AccelerometerAccelContract.State.Display( - mode = AccelViewMode.ACCELERATION, + mode = ACCELERATION, measureHistory = emptyList() ) } @@ -555,7 +611,7 @@ class AccelerometerAccelViewModel @Inject constructor( setState { AccelerometerAccelContract.State.Display( - mode = AccelViewMode.ACCELERATION, + mode = ACCELERATION, measureHistory = emptyList() ) } @@ -569,8 +625,8 @@ class AccelerometerAccelViewModel @Inject constructor( var dataList = this.measureHistory.toMutableList().apply { add(it) } - if(accelMode != AccelViewMode.ANGLE) { - dataList = dataList.takeLast(10).toMutableList() + if(accelMode != ANGLE) { + dataList = dataList.takeLast(100).toMutableList() } AccelerometerAccelContract.State.Display(accelMode, dataList) } diff --git a/app/src/main/java/llc/arma/ble/app/ui/theme/Theme.kt b/app/src/main/java/llc/arma/ble/app/ui/theme/Theme.kt index 73dd177..ec9b545 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/theme/Theme.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/theme/Theme.kt @@ -44,19 +44,6 @@ val LightColorScheme = lightColorScheme( surfaceTint = Color(0xFF755B00), ) -@Composable -fun BleTheme( - colorScheme: ColorScheme, - content: @Composable () -> Unit -){ - MaterialTheme( - colorScheme = colorScheme, - typography = Typography, - content = content, - shapes = shapes - ) -} - @Composable fun BleTheme( diff --git a/app/src/main/java/llc/arma/ble/data/BleRepositoryImpl.kt b/app/src/main/java/llc/arma/ble/data/BleRepositoryImpl.kt index b729b08..e2f4fe8 100644 --- a/app/src/main/java/llc/arma/ble/data/BleRepositoryImpl.kt +++ b/app/src/main/java/llc/arma/ble/data/BleRepositoryImpl.kt @@ -15,7 +15,12 @@ import kotlinx.coroutines.* import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import llc.arma.ble.domain.Result import llc.arma.ble.domain.common.BleException import llc.arma.ble.domain.common.ProgressState @@ -25,14 +30,29 @@ import llc.arma.ble.domain.model.ConnectedBleInfo import llc.arma.ble.domain.repository.BleRepository import llc.arma.ble.domain.usecase.AccelScale import llc.arma.ble.domain.usecase.AccelViewMode +import llc.arma.ble.domain.usecase.AccelViewMode.* import llc.arma.ble.domain.usecase.MeasureData import llc.arma.ble.domain.usecase.FftAxis import llc.arma.ble.domain.usecase.FftFrequency import llc.arma.ble.domain.usecase.FftViewMode +import no.nordicsemi.android.common.core.DataByteArray +import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt +import no.nordicsemi.android.kotlin.ble.core.scanner.BleNumOfMatches +import no.nordicsemi.android.kotlin.ble.core.scanner.BleScanMode +import no.nordicsemi.android.kotlin.ble.core.scanner.BleScanResult +import no.nordicsemi.android.kotlin.ble.core.scanner.BleScannerCallbackType +import no.nordicsemi.android.kotlin.ble.core.scanner.BleScannerMatchMode +import no.nordicsemi.android.kotlin.ble.core.scanner.BleScannerSettings +import no.nordicsemi.android.kotlin.ble.scanner.BleScanner +import no.nordicsemi.android.kotlin.ble.scanner.aggregator.BleScanResultAggregator import java.util.* import javax.inject.Inject import javax.inject.Singleton import kotlin.coroutines.resume +import kotlin.math.PI +import kotlin.math.atan +import kotlin.math.pow +import kotlin.math.sqrt val FftFrequency.sendData: Byte @@ -74,11 +94,12 @@ val FftViewMode.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 -> 0 + ACCELERATION -> 0 + PEAK_ACCELERATION -> 1 + RMS -> 2 + VIBRATION -> 3 + ANGLE -> 0 + ROTATIONS -> 4 } } @@ -132,6 +153,26 @@ class BleRepositoryImpl @Inject constructor( return scanRecord?.manufacturerSpecificData?.get(89)?.get(2) == 1.toByte() } + private val BleScanResult.timerEnabled: Boolean + get() { + return data?.scanRecord?.manufacturerSpecificData?.get(89)?.getByte(2) == 1.toByte() + } + + private val BleScanResult.info: BleInfo + get() { + this.device.name + return BleInfo( + name = this.device.name ?: "", + serial = device.address, + batteryLevel = batteryLevel ?: 0, + rssi = data?.rssi, + type = type, + scanTime = (data?.timestampNanos ?: 0) / 1_000_000, + tx = data?.txPower ?: 0, + recordEnabled = timerEnabled + ) + } + private val ScanResult.info: BleInfo get() { return BleInfo( @@ -146,12 +187,27 @@ class BleRepositoryImpl @Inject constructor( ) } + private val BleScanResult.batteryLevel: Int? + get() { + return data?.scanRecord?.manufacturerSpecificData?.get(89)?.getByte(1) + ?.toUByte()?.toInt() + } + private val ScanResult.batteryLevel: Int? get() { return scanRecord?.manufacturerSpecificData?.get(89)?.get(1) ?.toUByte()?.toInt() } + private val BleScanResult.type: BleInfo.Type + get() { + return when(data?.scanRecord?.manufacturerSpecificData?.get(89)?.getByte(0)?.toUByte()?.toInt()){ + 1 -> BleInfo.Type.BEACON + 2 -> BleInfo.Type.THERMOMETER + else -> BleInfo.Type.ACCELEROMETER + } + } + private val ScanResult.type: BleInfo.Type get() { return when(scanRecord?.manufacturerSpecificData?.get(89)?.get(0)?.toUByte()?.toInt()){ @@ -172,7 +228,7 @@ class BleRepositoryImpl @Inject constructor( override fun getConnectedBle(): List { - if(checkPermission()) { + if(app.checkPermission()) { return app.getSystemService(BluetoothManager::class.java) .getConnectedDevices(BluetoothProfile.GATT) @@ -186,15 +242,60 @@ class BleRepositoryImpl @Inject constructor( ) } - }else{ + } else { return emptyList() } } + private val aggregator = BleScanResultAggregator() + override fun getBleAroundFlow(): Flow, BleException>> { - return if(checkPermission()){ + /*return if(checkPermission()){ + + callbackFlow { + + BleScanner(app).scan( + settings = BleScannerSettings( + includeStoredBondedDevices = false, + scanMode = BleScanMode.SCAN_MODE_LOW_LATENCY, + callbackType = BleScannerCallbackType.CALLBACK_TYPE_ALL_MATCHES, + matchMode = BleScannerMatchMode.MATCH_MODE_AGGRESSIVE, + numOfMatches = BleNumOfMatches.MATCH_NUM_ONE_ADVERTISEMENT, + reportDelay = 0L + ), + + ).filter { it.device.name?.contains("ArmA") == true } + .onEach { aggregator.aggregateDevices(it) } + .onEach { + resultList[it.device.address] = it.info + //deviceCache[it.device.address] = it + }.launchIn(CoroutineScope(Dispatchers.IO)) + + val timer = Timer().apply { + schedule(object : TimerTask() { + override fun run() { + CoroutineScope(Dispatchers.IO).launch { + send(Result.success(resultList.values.toList())) + } + } + }, 500, 500) + } + + awaitClose { + timer.cancel() + } + + } + + } else { + + flow { emit(Result.failure(BleException.PermissionDenied)) } + + }*/ + + return if(app.checkPermission()){ callbackFlow { @@ -207,7 +308,7 @@ class BleRepositoryImpl @Inject constructor( super.onScanResult(callbackType, result) - if (checkPermission()) { + if (app.checkPermission()) { if (result.scanRecord?.deviceName?.contains("ArmA") == true) { @@ -230,7 +331,7 @@ class BleRepositoryImpl @Inject constructor( override fun onBatchScanResults(results: MutableList) { super.onBatchScanResults(results) - if (checkPermission()) { + if (app.checkPermission()) { results.forEach { result -> @@ -303,15 +404,7 @@ class BleRepositoryImpl @Inject constructor( return when(result.info.type) { BleInfo.Type.ACCELEROMETER -> { - val tState = suspendCancellableCoroutine { - - CoroutineScope(Dispatchers.IO).launch { - - it.resume(readAccelState(result)) - - } - - }.fold( + val tState = readAccelState(result).fold( onFailure = { return Result.failure(it) }, @@ -413,15 +506,7 @@ class BleRepositoryImpl @Inject constructor( } BleInfo.Type.THERMOMETER -> { - val tState = suspendCancellableCoroutine { - - CoroutineScope(Dispatchers.IO).launch { - - it.resume(readThermometerState(result)) - - } - - }.fold( + val tState = readThermometerState(result).fold( onFailure = { return Result.failure(it) }, @@ -486,31 +571,61 @@ class BleRepositoryImpl @Inject constructor( } + @OptIn(ExperimentalUnsignedTypes::class) private suspend fun readThermometerState( record: ScanResult ): Result { - val temperature = readTemperature(record).fold( - onFailure = { - return Result.failure(it) - }, - onSuccess = { return@fold it } - ) + return if(app.checkPermission()) { - val history = readHistoryInterval(record).fold( - onFailure = { - return Result.failure(it) - }, - onSuccess = { return@fold it } - ) + try { - return Result.success( - Ble.Thermometer.ThermometerState( - temperature = temperature, - saveHistory = record.timerEnabled, - historyInterval = history - ) - ) + val connection = + ClientBleGatt.connect(app, record.device.address, CoroutineScope(Dispatchers.IO)) + + val service = connection.discoverServices() + .findService(serviceUUID) ?: return Result.failure(BleException.UnexpectedResponse) + + var characteristic = service.findCharacteristic(temperatureReadUUID) + ?: return Result.failure(BleException.UnexpectedResponse) + + characteristic.write(DataByteArray.from(1, 1)) + delay(2_000) + val temperature = characteristic.read().value.toUByteArray().toTemperature() + + characteristic = service.findCharacteristic(intervalReadUUID) + ?: return Result.failure(BleException.UnexpectedResponse) + + characteristic.write(DataByteArray.from(3, 0, 0, 0, )) + + val interval = characteristic.read().value.let { + if(it.size == 4){ + it.getUIntAt(0).toLong() + }else{ + 0 + } + } + + connection.close() + + return Result.success( + Ble.Thermometer.ThermometerState( + temperature = temperature, + saveHistory = record.timerEnabled, + historyInterval = interval + ) + ) + + } catch (err: Throwable){ + err.printStackTrace() + return Result.failure(BleException.UnexpectedResponse) + } + + } else { + + Result.failure(BleException.PermissionDenied) + + } } @@ -518,137 +633,90 @@ class BleRepositoryImpl @Inject constructor( record: ScanResult ): Result { - val history = readHistoryInterval(record).fold( - onFailure = { - return Result.failure(it) - }, - onSuccess = { return@fold it } - ) + return if(app.checkPermission()) { - val historyParams = when(record.timerEnabled){ - true -> { + try { - writeCharacteristic( - device = record.device, - serviceId = serviceUUID, - characteristicId = accelerometerReadUUID, - writeData = byteArrayOf(4) - ).onFailure { - return Result.failure(BleException.UnexpectedResponse) + val connection = + ClientBleGatt.connect(app, record.device.address, CoroutineScope(Dispatchers.IO)) + + val service = connection.discoverServices() + .findService(serviceUUID) ?: return Result.failure(BleException.UnexpectedResponse) + + var characteristic = service.findCharacteristic(intervalReadUUID) + ?: return Result.failure(BleException.UnexpectedResponse) + + characteristic.write(DataByteArray.from(3, 0, 0, 0, )) + + val interval = characteristic.read().value.let { + if(it.size == 4){ + it.getUIntAt(0).toLong() + }else{ + 0 + } } - readCharacteristic( - device = record.device, - serviceId = serviceUUID, - characteristicId = accelerometerReadUUID, - ).fold( - onSuccess = { - Log.d("history", it.joinToString { it.toString() }) - val scale = when(it[1].toInt()){ - 0 -> AccelScale.S_2 - 1 -> AccelScale.S_4 - 2 -> AccelScale.S_8 - 3 -> AccelScale.S_16 - else -> { - return Result.failure(BleException.UnexpectedResponse) + val historyParams = when(record.timerEnabled){ + true -> { + + characteristic = service.findCharacteristic(accelerometerReadUUID) + ?: return Result.failure(BleException.UnexpectedResponse) + + characteristic.write(DataByteArray.from(4)) + characteristic.read().let { + + val data = it.value + + val scale = when(data[1].toInt()){ + 0 -> AccelScale.S_2 + 1 -> AccelScale.S_4 + 2 -> AccelScale.S_8 + 3 -> AccelScale.S_16 + else -> { + return Result.failure(BleException.UnexpectedResponse) + } } - } - val mode = when(it[0].toInt()){ - 0 -> AccelViewMode.ACCELERATION - 1 -> AccelViewMode.PEAK_ACCELERATION - 2 -> AccelViewMode.RMS - 3 -> AccelViewMode.VIBRATION - 4 -> AccelViewMode.ANGLE - else -> { - return Result.failure(BleException.UnexpectedResponse) + val mode = when(data[0].toInt()){ + 0 -> ACCELERATION + 1 -> PEAK_ACCELERATION + 2 -> RMS + 3 -> VIBRATION + 4 -> ANGLE + else -> { + return Result.failure(BleException.UnexpectedResponse) + } } + Ble.Accelerometer.History.Enabled( + scale = scale, + mode = mode, + detailed = false //TODO + ) } - Ble.Accelerometer.History.Enabled( - scale = scale, - mode = mode, - detailed = false //TODO - ) - }, - onFailure = { - return Result.failure(BleException.UnexpectedResponse) } + false -> Ble.Accelerometer.History.Disabled + } + + connection.close() + + return Result.success( + Ble.Accelerometer.AccelerometerState( + saveHistory = historyParams, + historyInterval = interval + ) ) + + } catch (err: Throwable){ + + return Result.failure(BleException.UnexpectedResponse) + } - false -> Ble.Accelerometer.History.Disabled + + } else { + + Result.failure(BleException.PermissionDenied) + } - return Result.success( - Ble.Accelerometer.AccelerometerState( - saveHistory = historyParams, - historyInterval = history - ) - ) - - } - - @OptIn(ExperimentalUnsignedTypes::class) - private suspend fun readTemperature( - record: ScanResult - ): Result { - - writeCharacteristic( - device = record.device, - serviceId = serviceUUID, - characteristicId = temperatureReadUUID, - writeData = byteArrayOf(1, 1) - ).onFailure { - return Result.failure(it) - } - - delay(2_000) - - val dataResult = readCharacteristic( - device = record.device, - serviceId = serviceUUID, - characteristicId = temperatureReadUUID - ).fold( - onFailure = { - return Result.failure(it) - }, - onSuccess = { return@fold it } - ) - - return Result.success(dataResult.toUByteArray().toTemperature()) - - } - - private suspend fun readHistoryInterval( - record: ScanResult - ): Result { - - writeCharacteristic( - device = record.device, - serviceId = serviceUUID, - characteristicId = intervalReadUUID, - writeData = byteArrayOf(3, 0, 0, 0, 0) - ).onFailure { - return Result.failure(it) - } - - val dataResult = readCharacteristic( - device = record.device, - serviceId = serviceUUID, - characteristicId = intervalReadUUID - ).fold( - onFailure = { - return Result.failure(it) - }, - onSuccess = { return@fold it } - ) - - return Result.success( - if(dataResult.size == 4){ - dataResult.getUIntAt(0).toLong() - }else{ - 0 - } - ) - } override suspend fun getAccelerometerSpectreBySerial( @@ -666,7 +734,7 @@ class BleRepositoryImpl @Inject constructor( deviceCache[serial]?.device?.let { - if (checkPermission()) { + if (app.checkPermission()) { gatt = it.connectGatt(app, false, ReadAccelerometerSpectreCallback( app, accelScale, accelMode, fftAxis, fftMode, frequency @@ -707,7 +775,7 @@ class BleRepositoryImpl @Inject constructor( deviceCache[serial]?.device?.let { - if (checkPermission()) { + if (app.checkPermission()) { gatt = it.connectGatt(app, false, ReadTemperatureHistoryCallback(app) { CoroutineScope(Dispatchers.IO).launch { @@ -739,94 +807,70 @@ class BleRepositoryImpl @Inject constructor( serial: String ): Flow>, BleException>> { - var gatt: BluetoothGatt? = null + return flow { - return callbackFlow { + if (app.checkPermission()) { - deviceCache[serial]?.let { result -> - - val device = result.device - - if (checkPermission()) { - - val state = readAccelState(result).fold( - onSuccess = { - - }, - onFailure = { - null - } - ) + try { var scale: AccelScale? = null var mode: AccelViewMode? = null - writeCharacteristic( - device = device, - serviceId = serviceUUID, - characteristicId = accelerometerReadUUID, - writeData = byteArrayOf(4) - ).fold( - onSuccess = { - readCharacteristic( - device = device, - serviceId = serviceUUID, - characteristicId = accelerometerReadUUID, - ).fold( - onSuccess = { - scale = when(it[1].toInt()){ - 0 -> AccelScale.S_2 - 1 -> AccelScale.S_4 - 2 -> AccelScale.S_8 - 3 -> AccelScale.S_16 - else -> null - } - mode = when(it[0].toInt()){ - 0 -> AccelViewMode.ACCELERATION - 1 -> AccelViewMode.PEAK_ACCELERATION - 2 -> AccelViewMode.RMS - 3 -> AccelViewMode.VIBRATION - 4 -> AccelViewMode.ANGLE - else -> null - } - }, - onFailure = { null } - ) - }, - onFailure = { - null - } + val connection = ClientBleGatt.connect( + app, + serial, + CoroutineScope(Dispatchers.Default) ) - if(scale != null && mode != null) { + val characteristic = connection.discoverServices() + .findService(serviceUUID) + ?.findCharacteristic(accelerometerReadUUID) - gatt = device.connectGatt( - app, - false, - ReadAccelerometerHistoryCallback(mode!!, scale!!, app) { - CoroutineScope(Dispatchers.IO).launch { - send(it) - } - }) - - } else { - CoroutineScope(Dispatchers.IO).launch { - send(Result.failure(BleException.UnexpectedResponse)) + characteristic?.write(DataByteArray.from(4)) + characteristic?.read()?.let { + val data = it.value + scale = when (data[1].toInt()) { + 0 -> AccelScale.S_2 + 1 -> AccelScale.S_4 + 2 -> AccelScale.S_8 + 3 -> AccelScale.S_16 + else -> null + } + mode = when (data[0].toInt()) { + 0 -> ACCELERATION + 1 -> PEAK_ACCELERATION + 2 -> RMS + 3 -> VIBRATION + 4 -> ANGLE + else -> null } } - } else { + if (scale != null && mode != null) { + + readAccelerometerHistory(serial, mode!!, scale!!, app).collect { + + emit(it) + + } + + } else { + + emit(Result.failure(BleException.UnexpectedResponse)) - CoroutineScope(Dispatchers.IO).launch { - send(Result.failure(BleException.PermissionDenied)) } + } catch (err: Throwable) { + err.printStackTrace() + + emit(Result.failure(BleException.UnexpectedResponse)) + } - } + } else { + + emit(Result.failure(BleException.PermissionDenied)) - awaitClose { - gatt?.close() } } @@ -836,35 +880,79 @@ class BleRepositoryImpl @Inject constructor( override suspend fun writeBle( serial: String, request: Ble.Thermometer.WriteRequest - ): Result = suspendCancellableCoroutine { + ): Result { - deviceCache[serial]?.let { scanResult -> + fun UInt.to4ByteArrayInLittleEndian(): ByteArray = + (3 downTo 0).map { + (this shr (it * Byte.SIZE_BITS)).toByte() + }.toByteArray() - if(checkPermission()) { + return if(app.checkPermission()) { - var gatt: BluetoothGatt? = null + try { - val callback = WriteThermometerCallback(app, request) { result -> + val connection = + ClientBleGatt.connect(app, serial, CoroutineScope(Dispatchers.Default)) + val services = connection.discoverServices() + val service = services.findService(serviceUUID) ?: return Result.failure(BleException.UnexpectedResponse) - gatt?.close() + request.tx?.let { + service.findCharacteristic(txWriteUUID)!!.write( + DataByteArray.from( + when (it) { + Ble.BleState.TX.MINUS_40 -> -40 + Ble.BleState.TX.MINUS_20 -> -20 + Ble.BleState.TX.MINUS_16 -> -16 + Ble.BleState.TX.MINUS_12 -> -12 + Ble.BleState.TX.MINUS_8 -> -8 + Ble.BleState.TX.MINUS_4 -> -4 + Ble.BleState.TX.ZERO -> 0 + Ble.BleState.TX.PLUS_3 -> 3 + Ble.BleState.TX.PLUS_4 -> 4 + } + ) + ) + } - result.onSuccess { - deviceCache.remove(serial) - resultList.remove(serial) - } + request.saveHistory?.let { - it.resume(result) + service.findCharacteristic(saveEnabledWriteUUID)!!.write( + DataByteArray.from( + *mutableListOf(4).apply { + add(if (it) 1 else 0) + }.toByteArray() + ) + ) } - gatt = scanResult.device.connectGatt(app, false, callback) + request.historyInterval?.let { - } else { + service.findCharacteristic(intervalWriteUUID)!!.write( + DataByteArray.from( + *mutableListOf(3).apply { + addAll((it).toUInt().to4ByteArrayInLittleEndian().reversed().toList()) + }.toByteArray() + ) + ) - it.resume(Result.failure(BleException.PermissionDenied)) + } + + connection.close() + + Result.success(Unit) + + } catch (err: Throwable){ + + err.printStackTrace() + Result.failure(BleException.UnexpectedResponse) } + } else { + + Result.failure(BleException.PermissionDenied) + } } @@ -872,35 +960,49 @@ class BleRepositoryImpl @Inject constructor( override suspend fun writeBle( serial: String, request: Ble.Beacon.WriteRequest - ): Result = suspendCancellableCoroutine { + ): Result { - deviceCache[serial]?.let { scanResult -> + return if(app.checkPermission()) { - if(checkPermission()) { + try { - var gatt: BluetoothGatt? = null + val connect = ClientBleGatt.connect(app, serial, scope = CoroutineScope(Dispatchers.Default)) - val callback = WriteBeaconCallback(app, request) { result -> + request.tx?.let { - gatt?.close() - - result.onSuccess { - deviceCache.remove(serial) - resultList.remove(serial) - } - - it.resume(result) + connect.discoverServices().findService(serviceUUID)?.findCharacteristic(txWriteUUID)?.write( + DataByteArray.from( + when (it) { + Ble.BleState.TX.MINUS_40 -> -40 + Ble.BleState.TX.MINUS_20 -> -20 + Ble.BleState.TX.MINUS_16 -> -16 + Ble.BleState.TX.MINUS_12 -> -12 + Ble.BleState.TX.MINUS_8 -> -8 + Ble.BleState.TX.MINUS_4 -> -4 + Ble.BleState.TX.ZERO -> 0 + Ble.BleState.TX.PLUS_3 -> 3 + Ble.BleState.TX.PLUS_4 -> 4 + } + ) + ) ?: return Result.failure(BleException.UnexpectedResponse) } - gatt = scanResult.device.connectGatt(app, false, callback) + connect.close() - } else { + Result.success(Unit) - it.resume(Result.failure(BleException.PermissionDenied)) + } catch (err: Throwable){ + + err.printStackTrace() + Result.failure(BleException.UnexpectedResponse) } + } else { + + Result.failure(BleException.PermissionDenied) + } } @@ -908,35 +1010,87 @@ class BleRepositoryImpl @Inject constructor( override suspend fun writeBle( serial: String, request: Ble.Accelerometer.WriteRequest - ): Result = suspendCancellableCoroutine { + ): Result { - deviceCache[serial]?.let { scanResult -> + fun UInt.to4ByteArrayInLittleEndian(): ByteArray = + (3 downTo 0).map { + (this shr (it * Byte.SIZE_BITS)).toByte() + }.toByteArray() - if(checkPermission()) { + return if(app.checkPermission()) { - var gatt: BluetoothGatt? = null + try { - val callback = WriteAccelerometerCallback(app, request) { result -> + val connection = + ClientBleGatt.connect(app, serial, CoroutineScope(Dispatchers.Default)) + val services = connection.discoverServices() + val service = services.findService(serviceUUID) ?: return Result.failure( + BleException.UnexpectedResponse + ) - gatt?.close() + request.tx?.let { + service.findCharacteristic(txWriteUUID)!!.write( + DataByteArray.from( + when (it) { + Ble.BleState.TX.MINUS_40 -> -40 + Ble.BleState.TX.MINUS_20 -> -20 + Ble.BleState.TX.MINUS_16 -> -16 + Ble.BleState.TX.MINUS_12 -> -12 + Ble.BleState.TX.MINUS_8 -> -8 + Ble.BleState.TX.MINUS_4 -> -4 + Ble.BleState.TX.ZERO -> 0 + Ble.BleState.TX.PLUS_3 -> 3 + Ble.BleState.TX.PLUS_4 -> 4 + } + ) + ) + } - result.onSuccess { - deviceCache.remove(serial) - resultList.remove(serial) - } + request.saveHistory?.let { - it.resume(result) + service.findCharacteristic(saveEnabledWriteUUID)!!.write( + DataByteArray.from( + *mutableListOf(4).apply { + add(if (it is Ble.Accelerometer.History.Enabled) 1 else 0) + if (it is Ble.Accelerometer.History.Enabled) { + add(it.mode.sendData) + add(it.scale.sendData) + } + }.toByteArray() + ) + ) } - gatt = scanResult.device.connectGatt(app, false, callback) + request.historyInterval?.let { - } else { + service.findCharacteristic(intervalWriteUUID)!!.write( + DataByteArray.from( + *mutableListOf(3).apply { + addAll( + (it).toUInt().to4ByteArrayInLittleEndian().reversed().toList() + ) + }.toByteArray() + ) + ) - it.resume(Result.failure(BleException.PermissionDenied)) + } + + connection.close() + + Result.success(Unit) + + } catch (err: Throwable){ + + err.printStackTrace() + Result.failure(BleException.UnexpectedResponse) } + } else { + + Result.failure(BleException.PermissionDenied) + } } @@ -945,24 +1099,40 @@ class BleRepositoryImpl @Inject constructor( password: String, serial: String ): Result { - deviceCache[serial]?.device?.let { - return writeCharacteristic( - device = it, - serviceId = serviceUUID, - characteristicId = passwordWriteUUID, - writeData = mutableListOf(8.toByte()).apply { - addAll(password.toByteArray(Charsets.US_ASCII).toList()) - }.toByteArray() - ).fold( - onFailure = { - Result.failure(it) - }, - onSuccess = { - Result.success(Unit) - } - ) + + return if(app.checkPermission()) { + + try { + + val connection = + ClientBleGatt.connect(app, serial, CoroutineScope(Dispatchers.Default)) + val services = connection.discoverServices() + val service = services.findService(serviceUUID) + ?: return Result.failure(BleException.UnexpectedResponse) + + service.findCharacteristic(passwordWriteUUID)?.write( + DataByteArray( + mutableListOf(8.toByte()).apply { + addAll(password.toByteArray(Charsets.US_ASCII).toList()) + }.toByteArray() + ) + ) ?: return Result.failure(BleException.UnexpectedResponse) + + connection.close() + + Result.success(Unit) + + } catch (err: Throwable) { + + err.printStackTrace() + Result.failure(BleException.UnexpectedResponse) + + } + + } else { + Result.failure(BleException.PermissionDenied) } - return Result.success(Unit) + } override fun getAccelerometerMeasureBySerialFlow( @@ -974,392 +1144,189 @@ class BleRepositoryImpl @Inject constructor( frequency: FftFrequency, ): Flow> { - return callbackFlow { + return if (app.checkPermission()) { - var gatt: BluetoothGatt? = null + flow { - if(checkPermission()) { + try { - deviceCache[serial]?.let { + val connection = + ClientBleGatt.connect(app, serial, CoroutineScope(Dispatchers.Default)) - gatt = it.device.connectGatt( - app, - false, - ReadAccelerometerCallback( - app = app, - accelScale = accelScale, - accelMode = accelMode, - fftAxis = fftAxis, - fftMode = fftMode, - frequency = frequency - ) { result -> - CoroutineScope(Dispatchers.IO).launch { - send(result) - } - } + val services = connection.discoverServices() + + val characteristic = + services.findService(serviceUUID) + ?.findCharacteristic(accelerometerReadUUID) + + val payload = DataByteArray.from( + 4, + accelMode.sendData, + accelScale.sendData, + fftMode.sendData, + fftAxis.sendData, + frequency.sendData, + 1 ) - } + characteristic?.write(payload) - awaitClose { - gatt?.close() - } + characteristic?.getNotifications()?.collect { - } else { - CoroutineScope(Dispatchers.IO).launch { + val value = it.value - send(Result.failure(BleException.PermissionDenied)) - } + val result = if (accelMode == VIBRATION) { - } - - } - - - } - - private suspend fun readCharacteristic( - device: BluetoothDevice, - serviceId: UUID, - characteristicId: UUID - ): Result { - - var result: Result = Result.failure(BleException.UnexpectedResponse) - - repeat(5){ - result = suspendCancellableCoroutine { - - var result: ByteArray? - - val callback = object : BluetoothGattCallback() { - - override fun onConnectionStateChange( - gatt: BluetoothGatt, - status: Int, - newState: Int - ) { - - if(status == BluetoothGatt.GATT_SUCCESS) { - - if (newState == BluetoothProfile.STATE_CONNECTED) { - - if (checkPermission()) { - - gatt.discoverServices() - - } else { - it.resume(Result.failure(BleException.PermissionDenied)) - gatt.disconnect() - } - - } else { - gatt.close() - } + MeasureData.Vibration( + (value.get2byteShortAt() + .toFloat() * accelScale.k) / Short.MAX_VALUE + ) } else { - it.resume(Result.failure(BleException.PermissionDenied)) - gatt.disconnect() - - } - - } - - override fun onServicesDiscovered( - gatt: BluetoothGatt, - status: Int - ) { - super.onServicesDiscovered(gatt, status) - - if (status == BluetoothGatt.GATT_SUCCESS) { - - gatt.services?.firstOrNull { service -> - service.uuid == serviceId - }?.characteristics?.firstOrNull { characteristic -> - characteristic.uuid == characteristicId - }?.let { char -> - - if (checkPermission()) { - - gatt.readCharacteristic(char) - - } else { - - gatt.disconnect() - it.resume(Result.failure(BleException.PermissionDenied)) - - } - - return - + val data = value.toList().chunked(2).map { + it.toByteArray().get2byteShortAt() } - gatt.disconnect() - it.resume(Result.failure(BleException.UnexpectedResponse)) + Log.d( + "accel", + "x: ${data[0]} y: ${data[1]} z: ${data[2]} bytes: ${value.toUByteArray().joinToString { it.toString(16).padStart(2, '0') }}" + ) - } else { - - gatt.disconnect() - it.resume(Result.failure(BleException.UnexpectedResponse)) - - } - - } - - 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) - } - - fun onCommonCharacteristicRead( - gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - value: ByteArray, - status: Int - ) { - - Log.d("read", "onCharacteristicRead $status") - - if(status == BluetoothGatt.GATT_SUCCESS) { - - if (checkPermission()) { - result = value - it.resume(Result.success(result!!)) - - } else { - it.resume(Result.failure(BleException.PermissionDenied)) - - } - gatt.disconnect() - } else { - - it.resume(Result.failure(BleException.UnexpectedResponse)) - gatt.disconnect() - } - - } - - } - - if (checkPermission()) { - device.connectGatt(app, false, callback) - } else { - it.resume(Result.failure(BleException.PermissionDenied)) - } - - } - - if(result.isSuccess || (result.getErrorOrNull() is BleException.UnexpectedResponse).not()){ - return result - } - - } - - return result - - } - - private suspend fun writeCharacteristic( - device: BluetoothDevice, - serviceId: UUID, - characteristicId: UUID, - writeData: ByteArray - ): Result { - - var result: Result = Result.failure(BleException.UnexpectedResponse) - - repeat(5){ - result = suspendCancellableCoroutine { - - var bleGatt: BluetoothGatt? = null - - val callback = object : BluetoothGattCallback() { - - override fun onConnectionStateChange( - gatt: BluetoothGatt, - status: Int, - newState: Int - ) { - - if (status == BluetoothGatt.GATT_SUCCESS) { - - if (newState == BluetoothProfile.STATE_CONNECTED) { - - if (checkPermission()) { - - gatt.discoverServices() - - } else { - - bleGatt?.disconnect() - it.resume(Result.failure(BleException.PermissionDenied)) + val x: Float + val y: Float + val z: Float + when(accelMode){ + ANGLE -> { + x = calculateZAngle( + data[2].toFloat(), + data[1].toFloat() + ) * 180f / Math.PI.toFloat() + y = calculateZAngle( + data[2].toFloat(), + data[0].toFloat() + ) * 180f / Math.PI.toFloat() + z = calculateZAngle( + data[0].toFloat(), + data[1].toFloat() + ) * 180f / Math.PI.toFloat() } + ROTATIONS -> { + x = ((360f / 8f) * ((data[0] / 100f) + 1f)) - 45f + y = data[1].toFloat() + z = data[2].toFloat() + } + else -> { + x = (data[0].toFloat() * accelScale.k) / Short.MAX_VALUE + y = (data[1].toFloat() * accelScale.k) / Short.MAX_VALUE + z = (data[2].toFloat() * accelScale.k) / Short.MAX_VALUE + } + } + + /*if (accelMode == ANGLE) { + + x = calculateZAngle( + data[2].toFloat(), + data[1].toFloat() + ) * 180f / Math.PI.toFloat() + y = calculateZAngle( + data[2].toFloat(), + data[0].toFloat() + ) * 180f / Math.PI.toFloat() + z = calculateZAngle( + data[0].toFloat(), + data[1].toFloat() + ) * 180f / Math.PI.toFloat() } else { - bleGatt?.close() + 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 - } + }*/ - } else { + val state = MeasureData.Accelerate( + x = x, + y = y, + z = z + ) + + state - bleGatt?.disconnect() - it.resume(Result.failure(BleException.UnexpectedResponse)) } - } - - override fun onServicesDiscovered( - gatt: BluetoothGatt, - status: Int - ) { - super.onServicesDiscovered(gatt, status) - - if (status == BluetoothGatt.GATT_SUCCESS) { - - gatt.services?.firstOrNull { service -> - service.uuid == serviceId - }?.characteristics?.firstOrNull { characteristic -> - characteristic.uuid == characteristicId - }?.let { char -> - - if (checkPermission()) { - - gatt.writeCharacteristic(char, writeData) - - } else { - - bleGatt?.disconnect() - it.resume(Result.failure(BleException.PermissionDenied)) - - } - - return - - } - - Log.d("write", "service not found") - - bleGatt?.disconnect() - it.resume(Result.failure(BleException.UnexpectedResponse)) - - } else { - - bleGatt?.disconnect() - it.resume(Result.failure(BleException.UnexpectedResponse)) - - } + emit(Result.success(result)) } - override fun onCharacteristicWrite( - gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - status: Int - ) { - super.onCharacteristicWrite(gatt, characteristic, status) + connection.close() - if (checkPermission()) { + } catch (err: Exception) { - if(status == BluetoothGatt.GATT_SUCCESS) { + err.printStackTrace() - it.resume(Result.success(Unit)) - - } else { - - it.resume(Result.failure(BleException.UnexpectedResponse)) - - } - - } else { - - it.resume(Result.failure(BleException.PermissionDenied)) - - } - - gatt.disconnect() - - } - - } - - if (checkPermission()) { - - bleGatt = device.connectGatt(app, false, callback) - - } else { - - it.resume(Result.failure(BleException.PermissionDenied)) + emit(Result.failure(BleException.UnexpectedResponse)) } } - if(result.isSuccess || (result.getErrorOrNull() is BleException.UnexpectedResponse).not()){ - return result - } - - } - - return result - - } - - fun checkPermission(): Boolean { - - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_CONNECT) == - PackageManager.PERMISSION_GRANTED && - ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_SCAN) == - PackageManager.PERMISSION_GRANTED - } else { - return ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_FINE_LOCATION) == - PackageManager.PERMISSION_GRANTED && - ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_COARSE_LOCATION) == - PackageManager.PERMISSION_GRANTED - } - } - - fun BluetoothGatt.writeCharacteristic( - characteristic: BluetoothGattCharacteristic, - data: ByteArray - ): Result{ - - return if(checkPermission()){ - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - writeCharacteristic(characteristic, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT) - }else{ - characteristic.value = data - writeCharacteristic(characteristic) - } - - Result.success(Unit) } else { - Result.failure(BleException.PermissionDenied) + + flow { } + } + } +} + +fun calculateAngle( + targetAxis: Float, + firstAxis: Float, + secondAxis: Float +): Float { + + return atan(targetAxis.div(sqrt(firstAxis.pow(2) + secondAxis.pow(2)))) + +} + +public fun calculateZAngle( + x: Float, + y: Float +): Float { + + var x = x + + if(x == 0f && y == 0f){ + x = 0.0001f + } + + if(x > 0){ + return atan(y/x) + } + + if(x < 0 && y >= 0){ + return atan(y/x) + PI.toFloat() + } + + if(x < 0 && y < 0){ + return atan(y/x) - PI.toFloat() + } + + if(x == 0f && y > 0){ + return PI.toFloat() / 2f + } + + if(x == 0f && y < 0){ + return -PI.toFloat() / 2f + } + + return 0f + } \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/data/ReadAccelerometerCallback.kt b/app/src/main/java/llc/arma/ble/data/ReadAccelerometerCallback.kt deleted file mode 100644 index 6b2420b..0000000 --- a/app/src/main/java/llc/arma/ble/data/ReadAccelerometerCallback.kt +++ /dev/null @@ -1,297 +0,0 @@ -package llc.arma.ble.data - -import android.Manifest -import android.app.Application -import android.bluetooth.BluetoothGatt -import android.bluetooth.BluetoothGattCallback -import android.bluetooth.BluetoothGattCharacteristic -import android.bluetooth.BluetoothGattDescriptor -import android.content.pm.PackageManager -import android.os.Build -import android.util.Log -import androidx.core.app.ActivityCompat -import llc.arma.ble.domain.Result -import llc.arma.ble.domain.common.BleException -import llc.arma.ble.domain.usecase.AccelScale -import llc.arma.ble.domain.usecase.AccelViewMode -import llc.arma.ble.domain.usecase.MeasureData -import llc.arma.ble.domain.usecase.FftAxis -import llc.arma.ble.domain.usecase.FftFrequency -import llc.arma.ble.domain.usecase.FftViewMode -import java.util.UUID -import kotlin.math.PI -import kotlin.math.atan -import kotlin.math.pow -import kotlin.math.sqrt - - -class ReadAccelerometerCallback( - 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) -> Unit -) : BluetoothGattCallback() { - - override fun onConnectionStateChange( - gatt: BluetoothGatt, - status: Int, - newState: Int - ) { - super.onConnectionStateChange(gatt, status, newState) - - if(status == BluetoothGatt.GATT_SUCCESS){ - - if(newState == BluetoothGatt.STATE_CONNECTED){ - - if (checkPermission()) { - gatt.discoverServices() - - } else { - onResult(Result.failure(BleException.UnexpectedResponse)) - gatt.close() - } - - } - - } else { - - onResult(Result.failure(BleException.UnexpectedResponse)) - gatt.close() - - } - - } - - override fun onServicesDiscovered( - gatt: BluetoothGatt, - status: Int - ) { - super.onServicesDiscovered(gatt, status) - - Log.d("accel", "onServicesDiscovered") - - if(status == BluetoothGatt.GATT_SUCCESS){ - - gatt.getService(serviceUUID)?.getCharacteristic(accelerometerReadUUID)?.let { - - if (checkPermission()) { - - val payload = byteArrayOf( - 4, - accelMode.sendData, - accelScale.sendData, - fftMode.sendData, - fftAxis.sendData, - frequency.sendData, - 1 - ) - - gatt.writeCharacteristic(it, payload) - - } else { - - onResult(Result.failure(BleException.PermissionDenied)) - gatt.close() - - } - - } - - } - - } - - override fun onCharacteristicWrite( - gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - status: Int - ) { - super.onCharacteristicWrite(gatt, characteristic, status) - - Log.d("accel", "request written") - - if(status == BluetoothGatt.GATT_SUCCESS){ - - 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) - } - - } else { - - onResult(Result.failure(BleException.PermissionDenied)) - gatt.close() - - } - - } - - } - - } - - @Deprecated("Deprecated in Java") - override fun onCharacteristicChanged( - gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic - ) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { - super.onCharacteristicChanged(gatt, characteristic) - onCommonCharacteristicRead(characteristic.value) - } - } - - override fun onCharacteristicChanged( - gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - value: ByteArray - ) { - super.onCharacteristicChanged(gatt, characteristic, value) - onCommonCharacteristicRead(value) - } - - private fun onCommonCharacteristicRead( - value: ByteArray, - ){ - - - - val result = if(accelMode == AccelViewMode.VIBRATION){ - - MeasureData.Vibration((value.get2byteShortAt().toFloat() * accelScale.k) / Short.MAX_VALUE) - - } else { - - val data = value.toList().chunked(2).map { - it.toByteArray().get2byteShortAt() - } - - Log.d("accel", "x: ${data[0]} y: ${data[1]} z: ${data[2]} bytes: ${value.joinToString { it.toString() }}") - - val x: Float - val y: Float - val z: Float - - if (accelMode == AccelViewMode.ANGLE) { - - x = calculateZAngle(data[2].toFloat(), data[1].toFloat()) * 180f / Math.PI.toFloat() - y = calculateZAngle(data[2].toFloat(), data[0].toFloat()) * 180f / Math.PI.toFloat() - z = calculateZAngle(data[0].toFloat(), data[1].toFloat()) * 180f / Math.PI.toFloat() - - } else { - - x = (data[0].toFloat() * accelScale.k) / Short.MAX_VALUE - y = (data[1].toFloat() * accelScale.k) / Short.MAX_VALUE - z = (data[2].toFloat() * accelScale.k) / Short.MAX_VALUE - - } - - val state = MeasureData.Accelerate( - x = x, - y = y, - z = z - ) - - state - - } - - onResult(Result.success(result)) - - } - - private fun checkPermission(): Boolean { - - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_CONNECT) == - PackageManager.PERMISSION_GRANTED && - ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_SCAN) == - PackageManager.PERMISSION_GRANTED - } else { - return ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_FINE_LOCATION) == - PackageManager.PERMISSION_GRANTED && - ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_COARSE_LOCATION) == - PackageManager.PERMISSION_GRANTED - } - } - - private fun BluetoothGatt.writeCharacteristic( - characteristic: BluetoothGattCharacteristic, - data: ByteArray - ): Result{ - - return if(checkPermission()){ - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - writeCharacteristic(characteristic, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT) - }else{ - characteristic.value = data - writeCharacteristic(characteristic) - } - - Result.success(Unit) - - } else { - Result.failure(BleException.PermissionDenied) - } - - } - -} - -fun calculateAngle( - targetAxis: Float, - firstAxis: Float, - secondAxis: Float -): Float { - - return atan(targetAxis.div(sqrt(firstAxis.pow(2) + secondAxis.pow(2)))) - -} - -public fun calculateZAngle( - x: Float, - y: Float -): Float { - - var x = x - - if(x == 0f && y == 0f){ - x = 0.0001f - } - - if(x > 0){ - return atan(y/x) - } - - if(x < 0 && y >= 0){ - return atan(y/x) + PI.toFloat() - } - - if(x < 0 && y < 0){ - return atan(y/x) - PI.toFloat() - } - - if(x == 0f && y > 0){ - return PI.toFloat() / 2f - } - - if(x == 0f && y < 0){ - return -PI.toFloat() / 2f - } - - return 0f - -} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/data/ReadAccelerometerHistory.kt b/app/src/main/java/llc/arma/ble/data/ReadAccelerometerHistory.kt new file mode 100644 index 0000000..969a7fa --- /dev/null +++ b/app/src/main/java/llc/arma/ble/data/ReadAccelerometerHistory.kt @@ -0,0 +1,228 @@ +package llc.arma.ble.data + +import android.Manifest +import android.app.Application +import android.content.pm.PackageManager +import android.os.Build +import android.util.Log +import androidx.core.app.ActivityCompat +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import llc.arma.ble.domain.Result +import llc.arma.ble.domain.common.BleException +import llc.arma.ble.domain.common.ProgressState +import llc.arma.ble.domain.model.Ble +import llc.arma.ble.domain.usecase.AccelScale +import llc.arma.ble.domain.usecase.AccelViewMode +import no.nordicsemi.android.common.core.DataByteArray +import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt + +fun Application.checkPermission(): Boolean { + + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) == + PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) == + PackageManager.PERMISSION_GRANTED + } else { + return ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == + PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == + PackageManager.PERMISSION_GRANTED + } +} + +fun ByteArray.get4byteUIntAt(idx: Int) = + ((this[idx + 3].toUInt() and 0xFFu) shl 24) or + ((this[idx + 2].toUInt() and 0xFFu) shl 16) or + ((this[idx + 1].toUInt() and 0xFFu) shl 8) or + (this[idx].toUInt() and 0xFFu) + +/*fun ByteArray.get2byteUIntAt(idx: Int) = + ((this[idx + 1].toUInt() and 0xFFu) shl 8) or + (this[idx].toUInt() and 0xFFu)*/ + +@OptIn(ExperimentalUnsignedTypes::class) +fun readAccelerometerHistory( + address: String, + mode: AccelViewMode, + scale: AccelScale, + app: Application, +): Flow>, BleException>> { + + return flow { + var lastMeasureSystemTime: Long? = null + + var bleMeasureInterval: Long? = null + var bleRealTime: Long? = null + var bleLastMeasureTime: Long? = null + + val resultTemperaturePackage: MutableList = mutableListOf() + + val result = mutableListOf>() + + var expectedDataSize: Int? = null + + if(app.checkPermission()) { + + try { + + val connection = + ClientBleGatt.connect(app, address, CoroutineScope(Dispatchers.Default)) + + val characteristic = connection.discoverServices() + .findService(serviceUUID) + ?.findCharacteristic(accelerometerHistoryReadUUID) + + if(characteristic != null) { + + + characteristic.write(DataByteArray.from(2)) + + var value = characteristic.read().value + + if (value.contentEquals(byteArrayOf(0, 0))) { + + emit(Result.success(ProgressState.Finished(emptyList()))) + + } else { + + Log.d("expected data size", value.get2byteUIntAt(0).toString()) + + val writeData = mutableListOf( + 1.toByte(), + 0.toByte(), + 0.toByte() + ).apply { + addAll(value.toList()) + }.toByteArray() + + characteristic.write(DataByteArray(writeData)) + value = characteristic.read().value + var nextPackageDataCount = value.get2byteUIntAt(2) + + while (nextPackageDataCount.toInt() != 0) { + + val temperatureDataArray = 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) + + value.toUByteArray().asList().subList(16, value.size) + + } else { + + value.toUByteArray().asList().subList(4, value.size) + + } + + result.add(value.toUByteArray().toList()) + nextPackageDataCount = value.get2byteUIntAt(2) + + resultTemperaturePackage.addAll( + temperatureDataArray.chunked(2).map { + it.toUByteArray().toByteArray().get2byteShortAt().toFloat() + }.toMutableList() + ) + + Log.d( + "received data size", + (temperatureDataArray.chunked(2).size).toString() + ) + Log.d("next data size", nextPackageDataCount.toString()) + + expectedDataSize = + nextPackageDataCount.toInt() + resultTemperaturePackage.size + + emit(Result.success(ProgressState.Progress(0f / expectedDataSize.toFloat()))) + emit(Result.success(ProgressState.Progress(resultTemperaturePackage.size.toFloat() / expectedDataSize.toFloat()))) + + characteristic.write(DataByteArray.from(5)) + value = characteristic.read().value + + } + + emit( + Result.success( + ProgressState.Finished( + when (mode) { + AccelViewMode.ROTATIONS, + AccelViewMode.ACCELERATION, + AccelViewMode.PEAK_ACCELERATION, + AccelViewMode.RMS -> { + resultTemperaturePackage.chunked(3).withIndex().map { + Ble.Accelerometer.HistoryPoint.Angle( + date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), + x = (it.value[0] * scale.k) / Short.MAX_VALUE, + y = (it.value[1] * scale.k) / Short.MAX_VALUE, + z = (it.value[2] * scale.k) / Short.MAX_VALUE + ) + } + } + + AccelViewMode.ANGLE -> { + resultTemperaturePackage.chunked(3).withIndex().map { + Ble.Accelerometer.HistoryPoint.Angle( + date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), + x = calculateZAngle( + it.value[2], + it.value[1] + ) * 180f / Math.PI.toFloat(), + y = calculateZAngle( + it.value[2], + it.value[0] + ) * 180f / Math.PI.toFloat(), + z = calculateZAngle( + it.value[0], + it.value[1] + ) * 180f / Math.PI.toFloat() + ) + } + } + + AccelViewMode.VIBRATION -> { + resultTemperaturePackage.withIndex().map { + Ble.Accelerometer.HistoryPoint.Vibration( + date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), + value = (it.value * scale.k) / Short.MAX_VALUE + ) + } + } + } + ) + ) + + ) + + } + + + } else { + + emit(Result.failure(BleException.UnexpectedResponse)) + + } + + } catch (err: Throwable) { + + emit(Result.failure(BleException.UnexpectedResponse)) + + } + + } else { + + emit(Result.failure(BleException.PermissionDenied)) + + } + + } + + + +} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/data/ReadAccelerometerHistoryCallback.kt b/app/src/main/java/llc/arma/ble/data/ReadAccelerometerHistoryCallback.kt deleted file mode 100644 index dca14e3..0000000 --- a/app/src/main/java/llc/arma/ble/data/ReadAccelerometerHistoryCallback.kt +++ /dev/null @@ -1,410 +0,0 @@ -package llc.arma.ble.data - -import android.Manifest -import android.app.Application -import android.bluetooth.BluetoothGatt -import android.bluetooth.BluetoothGattCallback -import android.bluetooth.BluetoothGattCharacteristic -import android.content.pm.PackageManager -import android.os.Build -import android.util.Log -import androidx.core.app.ActivityCompat -import llc.arma.ble.domain.Result -import llc.arma.ble.domain.common.BleException -import llc.arma.ble.domain.common.ProgressState -import llc.arma.ble.domain.model.Ble -import llc.arma.ble.domain.usecase.AccelScale -import llc.arma.ble.domain.usecase.AccelViewMode - -class ReadAccelerometerHistoryCallback( - private val mode: AccelViewMode, - private val scale: AccelScale, - private val app: Application, - private val onResult: (Result>, BleException>) -> Unit -) : BluetoothGattCallback() { - - enum class Property { - DATA_SIZE, PACKAGE - } - - private fun ByteArray.get4byteUIntAt(idx: Int) = - ((this[idx + 3].toUInt() and 0xFFu) shl 24) or - ((this[idx + 2].toUInt() and 0xFFu) shl 16) or - ((this[idx + 1].toUInt() and 0xFFu) shl 8) or - (this[idx].toUInt() and 0xFFu) - - private fun ByteArray.get2byteUIntAt(idx: Int) = - ((this[idx + 1].toUInt() and 0xFFu) shl 8) or - (this[idx].toUInt() and 0xFFu) - - private var readProperty: Property? = null - - init { - Log.d("history", scale.name) - onResult(Result.success(ProgressState.Indeterminate)) - } - - override fun onConnectionStateChange( - gatt: BluetoothGatt, - status: Int, - newState: Int - ) { - super.onConnectionStateChange(gatt, status, newState) - - if(status == BluetoothGatt.GATT_SUCCESS){ - - if(newState == BluetoothGatt.STATE_CONNECTED){ - - if (checkPermission()) { - gatt.discoverServices() - - } else { - onResult(Result.failure(BleException.UnexpectedResponse)) - gatt.close() - } - } - - } else { - - onResult(Result.failure(BleException.UnexpectedResponse)) - gatt.close() - - } - - } - - override fun onServicesDiscovered( - gatt: BluetoothGatt, - status: Int - ) { - super.onServicesDiscovered(gatt, status) - if(status == BluetoothGatt.GATT_SUCCESS){ - gatt.getService(serviceUUID)?.getCharacteristic(accelerometerHistoryReadUUID)?.let { - - if (checkPermission()) { - - readProperty = Property.DATA_SIZE - gatt.writeCharacteristic(it, byteArrayOf(2)) - - } else { - - onResult(Result.failure(BleException.PermissionDenied)) - gatt.close() - - } - - } - - } - - } - - private var lastMeasureSystemTime: Long? = null - - private var bleMeasureInterval: Long? = null - private var bleRealTime: Long? = null - private var bleLastMeasureTime: Long? = null - - private val resultTemperaturePackage: MutableList = mutableListOf() - - private val result = 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 { - - Log.d("expected data size", value.get2byteUIntAt(0).toString()) - - 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()){ - - result.add(value.toUByteArray().toList()) - - bleMeasureInterval = value.get4byteUIntAt(4).toLong() - bleLastMeasureTime = value.get4byteUIntAt(8).toLong() - bleRealTime = value.get4byteUIntAt(12).toLong() - - lastMeasureSystemTime = System.currentTimeMillis() - ((bleRealTime!! - bleLastMeasureTime!!) * 1_000) - - val nextPackageDataCount = value.get2byteUIntAt(2) - val temperatureDataArray = value.toUByteArray().asList().subList(16, value.size) - - - - resultTemperaturePackage.addAll( - temperatureDataArray.chunked(2).map { - it.toUByteArray().toByteArray().get2byteShortAt().toFloat() - }.toMutableList() - ) - - Log.d("received data size", (temperatureDataArray.chunked(2).size).toString()) - Log.d("next data size", nextPackageDataCount.toString()) - - expectedDataSize = nextPackageDataCount.toInt() + resultTemperaturePackage.size - - onResult(Result.success(ProgressState.Progress(0f / expectedDataSize!!.toFloat()))) - onResult(Result.success(ProgressState.Progress(resultTemperaturePackage.size.toFloat() / expectedDataSize!!.toFloat()))) - - if(nextPackageDataCount != 0.toUInt()){ - - if (checkPermission()) { - - gatt.writeCharacteristic(characteristic, byteArrayOf(5)) - gatt.readCharacteristic(characteristic) - - } else { - - onResult(Result.failure(BleException.PermissionDenied)) - gatt.close() - - } - - } else { - onResult( - Result.success( - ProgressState.Finished( - when(mode){ - AccelViewMode.ACCELERATION, - AccelViewMode.PEAK_ACCELERATION, - AccelViewMode.RMS -> { - resultTemperaturePackage.chunked(3).withIndex().map { - Ble.Accelerometer.HistoryPoint.Angle( - date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), - x = (it.value[0] * scale.k) / Short.MAX_VALUE, - y = (it.value[1] * scale.k) / Short.MAX_VALUE, - z = (it.value[2] * scale.k) / Short.MAX_VALUE - ) - } - } - AccelViewMode.ANGLE -> { - resultTemperaturePackage.chunked(3).withIndex().map { - Ble.Accelerometer.HistoryPoint.Angle( - date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), - x = calculateZAngle(it.value[2], it.value[1]) * 180f / Math.PI.toFloat(), - y = calculateZAngle(it.value[2], it.value[0]) * 180f / Math.PI.toFloat(), - z = calculateZAngle(it.value[0], it.value[1]) * 180f / Math.PI.toFloat() - ) - } - } - AccelViewMode.VIBRATION -> { - resultTemperaturePackage.withIndex().map { - Ble.Accelerometer.HistoryPoint.Vibration( - date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), - value = (it.value * scale.k) / Short.MAX_VALUE - ) - } - } - } - ) - ) - ) - gatt.close() - } - - } else { - - if (value[0] == 251.toByte()) { - - result.add(value.toUByteArray().toList()) - - val nextPackageDataCount = value.get2byteUIntAt(2) - val temperatureDataArray = value.toUByteArray().toList().subList(4, value.size) - - resultTemperaturePackage.addAll( - temperatureDataArray.chunked(2).map { - it.toUByteArray().toByteArray().get2byteShortAt().toFloat() - } - ) - - Log.d("received data size", (temperatureDataArray.chunked(2).size).toString()) - Log.d("next data size", nextPackageDataCount.toString()) - - 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( - when(mode){ - AccelViewMode.ACCELERATION, - AccelViewMode.PEAK_ACCELERATION, - AccelViewMode.RMS -> { - resultTemperaturePackage.chunked(3).withIndex().map { - Ble.Accelerometer.HistoryPoint.Angle( - date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), - x = (it.value[0] * scale.k) / Short.MAX_VALUE, - y = (it.value[1] * scale.k) / Short.MAX_VALUE, - z = (it.value[2] * scale.k) / Short.MAX_VALUE - ) - } - } - AccelViewMode.ANGLE -> { - resultTemperaturePackage.chunked(3).withIndex().map { - Ble.Accelerometer.HistoryPoint.Angle( - date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), - x = calculateZAngle(it.value[2], it.value[1]) * 180f / Math.PI.toFloat(), - y = calculateZAngle(it.value[2], it.value[0]) * 180f / Math.PI.toFloat(), - z = calculateZAngle(it.value[0], it.value[1]) * 180f / Math.PI.toFloat() - ) - } - } - AccelViewMode.VIBRATION -> { - resultTemperaturePackage.withIndex().map { - Ble.Accelerometer.HistoryPoint.Vibration( - date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), - value = (it.value * scale.k) / Short.MAX_VALUE - ) - } - } - } - ) - ) - ) - gatt.close() - } - } else { - onResult(Result.failure(BleException.UnexpectedResponse)) - gatt.close() - } - - } - } - else -> { - onResult(Result.failure(BleException.UnexpectedResponse)) - gatt.close() - - } - - } - - } - } - - override fun onCharacteristicWrite( - gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - status: Int - ) { - super.onCharacteristicWrite(gatt, characteristic, status) - if(status == BluetoothGatt.GATT_SUCCESS){ - - if (checkPermission()) { - - gatt.readCharacteristic(characteristic) - - } else { - - onResult(Result.failure(BleException.PermissionDenied)) - gatt.close() - - } - - } else { - onResult(Result.failure(BleException.UnexpectedResponse)) - gatt.close() - } - - } - - fun checkPermission(): Boolean { - - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_CONNECT) == - PackageManager.PERMISSION_GRANTED && - ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_SCAN) == - PackageManager.PERMISSION_GRANTED - } else { - return ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_FINE_LOCATION) == - PackageManager.PERMISSION_GRANTED && - ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_COARSE_LOCATION) == - PackageManager.PERMISSION_GRANTED - } - } - - fun BluetoothGatt.writeCharacteristic( - characteristic: BluetoothGattCharacteristic, - data: ByteArray - ): Result{ - - return if(checkPermission()){ - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - writeCharacteristic(characteristic, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT) - }else{ - characteristic.value = data - writeCharacteristic(characteristic) - } - - Result.success(Unit) - - } else { - Result.failure(BleException.PermissionDenied) - } - - } - -} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/data/ReadAccelerometerSpectreCallback.kt b/app/src/main/java/llc/arma/ble/data/ReadAccelerometerSpectreCallback.kt index 573ec2a..2a4ee2c 100644 --- a/app/src/main/java/llc/arma/ble/data/ReadAccelerometerSpectreCallback.kt +++ b/app/src/main/java/llc/arma/ble/data/ReadAccelerometerSpectreCallback.kt @@ -10,6 +10,10 @@ import android.content.pm.PackageManager import android.os.Build import android.util.Log import androidx.core.app.ActivityCompat +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow import llc.arma.ble.domain.Result import llc.arma.ble.domain.common.BleException import llc.arma.ble.domain.common.ProgressState @@ -19,6 +23,8 @@ import llc.arma.ble.domain.usecase.AccelViewMode import llc.arma.ble.domain.usecase.FftAxis import llc.arma.ble.domain.usecase.FftFrequency import llc.arma.ble.domain.usecase.FftViewMode +import no.nordicsemi.android.common.core.DataByteArray +import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt import java.nio.ByteBuffer import java.nio.ByteOrder.LITTLE_ENDIAN import java.util.UUID @@ -70,7 +76,7 @@ class ReadAccelerometerSpectreCallback( if(newState == BluetoothGatt.STATE_CONNECTED){ - if (checkPermission()) { + if (app.checkPermission()) { gatt.discoverServices() } else { onResult(Result.failure(BleException.UnexpectedResponse)) @@ -105,7 +111,7 @@ class ReadAccelerometerSpectreCallback( gatt.getService(serviceUUID)?.getCharacteristic(accelerometerReadUUID)?.let { - if (checkPermission()) { + if (app.checkPermission()) { gatt.setCharacteristicNotification(it, true) @@ -260,7 +266,7 @@ class ReadAccelerometerSpectreCallback( if(nextPackageDataCount != 0.toUInt()){ - if (checkPermission()) { + if (app.checkPermission()) { gatt.writeCharacteristic(characteristic, byteArrayOf(5)) gatt.readCharacteristic(characteristic) @@ -360,7 +366,7 @@ class ReadAccelerometerSpectreCallback( if (status == BluetoothGatt.GATT_SUCCESS) { - if (checkPermission()) { + if (app.checkPermission()) { gatt.readCharacteristic(characteristic) @@ -394,7 +400,7 @@ class ReadAccelerometerSpectreCallback( gatt.getService(serviceUUID)?.getCharacteristic(accelerometerReadUUID)?.let { - if (checkPermission()) { + if (app.checkPermission()) { val payload = byteArrayOf( 4, @@ -424,27 +430,12 @@ class ReadAccelerometerSpectreCallback( } - private fun checkPermission(): Boolean { - - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_CONNECT) == - PackageManager.PERMISSION_GRANTED && - ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_SCAN) == - PackageManager.PERMISSION_GRANTED - } else { - return ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_FINE_LOCATION) == - PackageManager.PERMISSION_GRANTED && - ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_COARSE_LOCATION) == - PackageManager.PERMISSION_GRANTED - } - } - private fun BluetoothGatt.writeCharacteristic( characteristic: BluetoothGattCharacteristic, data: ByteArray ): Result{ - return if(checkPermission()){ + return if(app.checkPermission()){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { writeCharacteristic(characteristic, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT) @@ -461,4 +452,188 @@ class ReadAccelerometerSpectreCallback( } -} \ No newline at end of file +} + +/* +@OptIn(ExperimentalUnsignedTypes::class) +fun readAccelerometerSpectre( + address: String, + mode: AccelViewMode, + scale: AccelScale, + app: Application, +): Flow>, BleException>> { + + return flow { + var lastMeasureSystemTime: Long? = null + + var bleMeasureInterval: Long? = null + var bleRealTime: Long? = null + var bleLastMeasureTime: Long? = null + + val resultTemperaturePackage: MutableList = mutableListOf() + + val result = mutableListOf>() + + var expectedDataSize: Int? = null + + if(app.checkPermission()) { + + try { + + val connection = + ClientBleGatt.connect(app, address, CoroutineScope(Dispatchers.Default)) + + val characteristic = connection.discoverServices() + .findService(serviceUUID) + ?.findCharacteristic(accelerometerHistoryReadUUID) + + if(characteristic != null) { + + + characteristic.write(DataByteArray.from(2)) + + var value = characteristic.read().value + + if (value.contentEquals(byteArrayOf(0, 0))) { + + emit(Result.success(ProgressState.Finished(emptyList()))) + + } else { + + Log.d("expected data size", value.get2byteUIntAt(0).toString()) + + val writeData = mutableListOf( + 1.toByte(), + 0.toByte(), + 0.toByte() + ).apply { + addAll(value.toList()) + }.toByteArray() + + characteristic.write(DataByteArray(writeData)) + value = characteristic.read().value + var nextPackageDataCount = value.get2byteUIntAt(2) + + while (nextPackageDataCount.toInt() != 0) { + + val temperatureDataArray = 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) + + value.toUByteArray().asList().subList(16, value.size) + + } else { + + value.toUByteArray().asList().subList(4, value.size) + + } + + result.add(value.toUByteArray().toList()) + nextPackageDataCount = value.get2byteUIntAt(2) + + resultTemperaturePackage.addAll( + temperatureDataArray.chunked(2).map { + it.toUByteArray().toByteArray().get2byteShortAt().toFloat() + }.toMutableList() + ) + + Log.d( + "received data size", + (temperatureDataArray.chunked(2).size).toString() + ) + Log.d("next data size", nextPackageDataCount.toString()) + + expectedDataSize = + nextPackageDataCount.toInt() + resultTemperaturePackage.size + + emit(Result.success(ProgressState.Progress(0f / expectedDataSize.toFloat()))) + emit(Result.success(ProgressState.Progress(resultTemperaturePackage.size.toFloat() / expectedDataSize.toFloat()))) + + characteristic.write(DataByteArray.from(5)) + value = characteristic.read().value + + } + + emit( + Result.success( + ProgressState.Finished( + when (mode) { + AccelViewMode.ROTATIONS, + AccelViewMode.ACCELERATION, + AccelViewMode.PEAK_ACCELERATION, + AccelViewMode.RMS -> { + resultTemperaturePackage.chunked(3).withIndex().map { + Ble.Accelerometer.HistoryPoint.Angle( + date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), + x = (it.value[0] * scale.k) / Short.MAX_VALUE, + y = (it.value[1] * scale.k) / Short.MAX_VALUE, + z = (it.value[2] * scale.k) / Short.MAX_VALUE + ) + } + } + + AccelViewMode.ANGLE -> { + resultTemperaturePackage.chunked(3).withIndex().map { + Ble.Accelerometer.HistoryPoint.Angle( + date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), + x = calculateZAngle( + it.value[2], + it.value[1] + ) * 180f / Math.PI.toFloat(), + y = calculateZAngle( + it.value[2], + it.value[0] + ) * 180f / Math.PI.toFloat(), + z = calculateZAngle( + it.value[0], + it.value[1] + ) * 180f / Math.PI.toFloat() + ) + } + } + + AccelViewMode.VIBRATION -> { + resultTemperaturePackage.withIndex().map { + Ble.Accelerometer.HistoryPoint.Vibration( + date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), + value = (it.value * scale.k) / Short.MAX_VALUE + ) + } + } + } + ) + ) + + ) + + } + + + } else { + + emit(Result.failure(BleException.UnexpectedResponse)) + + } + + } catch (err: Throwable) { + + emit(Result.failure(BleException.UnexpectedResponse)) + + } + + } else { + + emit(Result.failure(BleException.PermissionDenied)) + + } + + } + + + +}*/ \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/data/ReadTemperatureHistoryCallback.kt b/app/src/main/java/llc/arma/ble/data/ReadTemperatureHistoryCallback.kt index 8c7ef9d..70fe95c 100644 --- a/app/src/main/java/llc/arma/ble/data/ReadTemperatureHistoryCallback.kt +++ b/app/src/main/java/llc/arma/ble/data/ReadTemperatureHistoryCallback.kt @@ -50,7 +50,7 @@ class ReadTemperatureHistoryCallback( if(newState == BluetoothGatt.STATE_CONNECTED){ - if (checkPermission()) { + if (app.checkPermission()) { gatt.discoverServices() } else { @@ -76,7 +76,7 @@ class ReadTemperatureHistoryCallback( if(status == BluetoothGatt.GATT_SUCCESS){ gatt.getService(serviceUUID)?.getCharacteristic(temperatureHistoryReadUUID)?.let { - if (checkPermission()) { + if (app.checkPermission()) { readProperty = Property.DATA_SIZE gatt.writeCharacteristic(it, byteArrayOf(2)) @@ -188,7 +188,7 @@ class ReadTemperatureHistoryCallback( if(nextPackageDataCount != 0.toUInt()){ - if (checkPermission()) { + if (app.checkPermission()) { gatt.writeCharacteristic(characteristic, byteArrayOf(5)) gatt.readCharacteristic(characteristic) @@ -279,7 +279,7 @@ class ReadTemperatureHistoryCallback( super.onCharacteristicWrite(gatt, characteristic, status) if(status == BluetoothGatt.GATT_SUCCESS){ - if (checkPermission()) { + if (app.checkPermission()) { gatt.readCharacteristic(characteristic) @@ -297,27 +297,12 @@ class ReadTemperatureHistoryCallback( } - fun checkPermission(): Boolean { - - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_CONNECT) == - PackageManager.PERMISSION_GRANTED && - ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_SCAN) == - PackageManager.PERMISSION_GRANTED - } else { - return ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_FINE_LOCATION) == - PackageManager.PERMISSION_GRANTED && - ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_COARSE_LOCATION) == - PackageManager.PERMISSION_GRANTED - } - } - fun BluetoothGatt.writeCharacteristic( characteristic: BluetoothGattCharacteristic, data: ByteArray ): Result{ - return if(checkPermission()){ + return if(app.checkPermission()){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { writeCharacteristic(characteristic, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT) diff --git a/app/src/main/java/llc/arma/ble/data/WriteAccelerometerCallback.kt b/app/src/main/java/llc/arma/ble/data/WriteAccelerometerCallback.kt deleted file mode 100644 index 83253e0..0000000 --- a/app/src/main/java/llc/arma/ble/data/WriteAccelerometerCallback.kt +++ /dev/null @@ -1,245 +0,0 @@ -package llc.arma.ble.data - -import android.Manifest -import android.app.Application -import android.bluetooth.BluetoothGatt -import android.bluetooth.BluetoothGattCallback -import android.bluetooth.BluetoothGattCharacteristic -import android.bluetooth.BluetoothProfile -import android.content.pm.PackageManager -import android.os.Build -import android.util.Log -import androidx.core.app.ActivityCompat -import llc.arma.ble.domain.Result -import llc.arma.ble.domain.common.BleException -import llc.arma.ble.domain.model.Ble -import java.util.UUID - -class WriteAccelerometerCallback( - private val app: Application, - private var request: Ble.Accelerometer.WriteRequest, - private val onResult: (Result) -> Unit -) : BluetoothGattCallback() { - - private var flashed = false - - override fun onConnectionStateChange( - gatt: BluetoothGatt, - status: Int, - newState: Int - ) { - super.onConnectionStateChange(gatt, status, newState) - - if(checkPermission()) { - - if(status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) { - - gatt.discoverServices() - - } else { - - onResult(Result.failure(BleException.UnexpectedResponse)) - - } - - } else { - - onResult(Result.failure(BleException.PermissionDenied)) - - } - - } - - override fun onServicesDiscovered( - gatt: BluetoothGatt, - status: Int - ) { - super.onServicesDiscovered(gatt, status) - onCycle(gatt, status) - - } - - private fun onCycle( - gatt: BluetoothGatt, - status: Int - ){ - - Log.d("write", "${request.tx != null} ${request.saveHistory != null} ${request.historyInterval != null}") - - if(request.tx != null || request.saveHistory != null || request.historyInterval != null) { - - fun UInt.to4ByteArrayInLittleEndian(): ByteArray = - (3 downTo 0).map { - (this shr (it * Byte.SIZE_BITS)).toByte() - }.toByteArray() - - var uuid: Triple? = null - - uuid = request.historyInterval?.let { - - Triple( - intervalWriteUUID, - mutableListOf(3).apply { - addAll((it).toUInt().to4ByteArrayInLittleEndian().reversed().toList()) - }.toByteArray(), - request.copy( - historyInterval = null - ) - ) - } - - uuid = request.saveHistory?.let { - - Triple( - saveEnabledWriteUUID, - mutableListOf(4).apply { - add(if (it is Ble.Accelerometer.History.Enabled) 1 else 0) - if(it is Ble.Accelerometer.History.Enabled) { - add(it.mode.sendData) - add(it.scale.sendData) - } - }.toByteArray(), - request.copy( - saveHistory = null - ) - ) - } ?: uuid - - uuid = request.tx?.let { - - Triple( - txWriteUUID, - byteArrayOf( - when (it) { - Ble.BleState.TX.MINUS_40 -> -40 - Ble.BleState.TX.MINUS_20 -> -20 - Ble.BleState.TX.MINUS_16 -> -16 - Ble.BleState.TX.MINUS_12 -> -12 - Ble.BleState.TX.MINUS_8 -> -8 - Ble.BleState.TX.MINUS_4 -> -4 - Ble.BleState.TX.ZERO -> 0 - Ble.BleState.TX.PLUS_3 -> 3 - Ble.BleState.TX.PLUS_4 -> 4 - } - ), - request.copy( - tx = null - ) - ) - - } ?: uuid - - uuid?.let { uuid -> - - gatt.services.firstOrNull { it.uuid == serviceUUID }?.characteristics?.firstOrNull { - it.uuid == uuid.first - }?.let { - - gatt.writeCharacteristic(it, uuid.second) - - request = uuid.third - - return - - } - - } - - onResult(Result.failure(BleException.UnexpectedResponse)) - - } else { - - if(flashed.not()){ - - flashed = true - - gatt.services.firstOrNull { it.uuid == serviceUUID }?.characteristics?.firstOrNull { - it.uuid == flashWriteUUID - }?.let { - - gatt.writeCharacteristic(it, byteArrayOf(9)) - - return - - } - - onResult(Result.failure(BleException.UnexpectedResponse)) - - } else { - - onResult(Result.success(Unit)) - - } - - } - - } - - override fun onCharacteristicWrite( - gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - status: Int - ) { - - super.onCharacteristicWrite(gatt, characteristic, status) - - if(checkPermission()) { - - if(status == BluetoothGatt.GATT_SUCCESS || flashed) { - - onCycle(gatt, status) - - } else { - - onResult(Result.failure(BleException.UnexpectedResponse)) - - } - - } else { - - onResult(Result.failure(BleException.PermissionDenied)) - - } - - } - - fun BluetoothGatt.writeCharacteristic( - characteristic: BluetoothGattCharacteristic, - data: ByteArray - ): Result { - - return if(checkPermission()){ - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - writeCharacteristic(characteristic, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT) - }else{ - - characteristic.writeType - characteristic.value = data - writeCharacteristic(characteristic) - } - - Result.success(Unit) - - } else { - Result.failure(BleException.PermissionDenied) - } - - } - - fun checkPermission(): Boolean { - - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_CONNECT) == - PackageManager.PERMISSION_GRANTED && - ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_SCAN) == - PackageManager.PERMISSION_GRANTED - } else { - return ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_FINE_LOCATION) == - PackageManager.PERMISSION_GRANTED && - ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_COARSE_LOCATION) == - PackageManager.PERMISSION_GRANTED - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/data/WriteBeaconCallback.kt b/app/src/main/java/llc/arma/ble/data/WriteBeaconCallback.kt deleted file mode 100644 index cae6bcd..0000000 --- a/app/src/main/java/llc/arma/ble/data/WriteBeaconCallback.kt +++ /dev/null @@ -1,209 +0,0 @@ -package llc.arma.ble.data - -import android.Manifest -import android.app.Application -import android.bluetooth.BluetoothGatt -import android.bluetooth.BluetoothGattCallback -import android.bluetooth.BluetoothGattCharacteristic -import android.bluetooth.BluetoothProfile -import android.content.pm.PackageManager -import android.os.Build -import android.util.Log -import androidx.core.app.ActivityCompat -import llc.arma.ble.domain.Result -import llc.arma.ble.domain.common.BleException -import llc.arma.ble.domain.model.Ble -import java.util.UUID - -class WriteBeaconCallback( - private val app: Application, - private var request: Ble.Beacon.WriteRequest, - private val onResult: (Result) -> Unit -) : BluetoothGattCallback() { - - private var flashed = false - - override fun onConnectionStateChange( - gatt: BluetoothGatt, - status: Int, - newState: Int - ) { - super.onConnectionStateChange(gatt, status, newState) - - if(checkPermission()) { - - if(status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) { - - gatt.discoverServices() - - } else { - - onResult(Result.failure(BleException.UnexpectedResponse)) - - } - - } else { - - onResult(Result.failure(BleException.PermissionDenied)) - - } - - } - - override fun onServicesDiscovered( - gatt: BluetoothGatt, - status: Int - ) { - super.onServicesDiscovered(gatt, status) - onCycle(gatt, status) - - } - - private fun onCycle( - gatt: BluetoothGatt, - status: Int - ){ - - if(request.tx != null) { - - var uuid: Pair? = null - - uuid = request.tx?.let { - - this.request = request.copy( - tx = null - ) - - Pair( - txWriteUUID, - byteArrayOf( - when (it) { - Ble.BleState.TX.MINUS_40 -> -40 - Ble.BleState.TX.MINUS_20 -> -20 - Ble.BleState.TX.MINUS_16 -> -16 - Ble.BleState.TX.MINUS_12 -> -12 - Ble.BleState.TX.MINUS_8 -> -8 - Ble.BleState.TX.MINUS_4 -> -4 - Ble.BleState.TX.ZERO -> 0 - Ble.BleState.TX.PLUS_3 -> 3 - Ble.BleState.TX.PLUS_4 -> 4 - } - ) - ) - - } ?: uuid - - uuid?.let { uuid -> - - gatt.services.firstOrNull { it.uuid == serviceUUID }?.characteristics?.firstOrNull { - it.uuid == uuid.first - }?.let { - - gatt.writeCharacteristic(it, uuid.second) - - return - - } - - } - - onResult(Result.failure(BleException.UnexpectedResponse)) - - } else { - - if(flashed.not()){ - - flashed = true - - gatt.services.firstOrNull { it.uuid == serviceUUID }?.characteristics?.firstOrNull { - it.uuid == flashWriteUUID - }?.let { - - gatt.writeCharacteristic(it, byteArrayOf(9)) - - return - - } - - onResult(Result.failure(BleException.UnexpectedResponse)) - - } else { - - onResult(Result.success(Unit)) - - } - - } - - } - - override fun onCharacteristicWrite( - gatt: BluetoothGatt, - characteristic: BluetoothGattCharacteristic, - status: Int - ) { - - Log.d("beacon", "onCharacteristicWrite $status") - - super.onCharacteristicWrite(gatt, characteristic, status) - - if(checkPermission()) { - - if(status == BluetoothGatt.GATT_SUCCESS || flashed) { - - onCycle(gatt, status) - - } else { - - onResult(Result.failure(BleException.UnexpectedResponse)) - - } - - } else { - - onResult(Result.failure(BleException.PermissionDenied)) - - } - - } - - fun BluetoothGatt.writeCharacteristic( - characteristic: BluetoothGattCharacteristic, - data: ByteArray - ): Result { - - return if(checkPermission()){ - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - writeCharacteristic(characteristic, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT) - }else{ - - characteristic.writeType - characteristic.value = data - writeCharacteristic(characteristic) - } - - Result.success(Unit) - - } else { - Result.failure(BleException.PermissionDenied) - } - - } - - fun checkPermission(): Boolean { - - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_CONNECT) == - PackageManager.PERMISSION_GRANTED && - ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_SCAN) == - PackageManager.PERMISSION_GRANTED - } else { - return ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_FINE_LOCATION) == - PackageManager.PERMISSION_GRANTED && - ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_COARSE_LOCATION) == - PackageManager.PERMISSION_GRANTED - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/data/WriteThermometerCallback.kt b/app/src/main/java/llc/arma/ble/data/WriteThermometerCallback.kt index a9fe747..c8b1a6c 100644 --- a/app/src/main/java/llc/arma/ble/data/WriteThermometerCallback.kt +++ b/app/src/main/java/llc/arma/ble/data/WriteThermometerCallback.kt @@ -34,7 +34,7 @@ class WriteThermometerCallback( ) { super.onConnectionStateChange(gatt, status, newState) - if(checkPermission()) { + if(app.checkPermission()) { if(status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) { @@ -179,7 +179,7 @@ class WriteThermometerCallback( super.onCharacteristicWrite(gatt, characteristic, status) - if(checkPermission()) { + if(app.checkPermission()) { if(status == BluetoothGatt.GATT_SUCCESS || flashed) { @@ -204,7 +204,7 @@ class WriteThermometerCallback( data: ByteArray ): Result { - return if(checkPermission()){ + return if(app.checkPermission()){ Log.d("write", data.asUByteArray().joinToString(" ") { it.toString(16).padStart(2, '0') }) @@ -225,19 +225,6 @@ class WriteThermometerCallback( } - private fun checkPermission(): Boolean { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_CONNECT) == - PackageManager.PERMISSION_GRANTED && - ActivityCompat.checkSelfPermission(app, Manifest.permission.BLUETOOTH_SCAN) == - PackageManager.PERMISSION_GRANTED - } else { - return ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_FINE_LOCATION) == - PackageManager.PERMISSION_GRANTED && - ActivityCompat.checkSelfPermission(app, Manifest.permission.ACCESS_COARSE_LOCATION) == - PackageManager.PERMISSION_GRANTED - } - } } \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/domain/usecase/GetAccelerometerMeasureBySerialFlow.kt b/app/src/main/java/llc/arma/ble/domain/usecase/GetAccelerometerMeasureBySerialFlow.kt index 814371c..c80f6c4 100644 --- a/app/src/main/java/llc/arma/ble/domain/usecase/GetAccelerometerMeasureBySerialFlow.kt +++ b/app/src/main/java/llc/arma/ble/domain/usecase/GetAccelerometerMeasureBySerialFlow.kt @@ -26,7 +26,7 @@ class GetAccelerometerMeasureBySerialFlow @Inject constructor( } enum class AccelViewMode { - ACCELERATION, PEAK_ACCELERATION, RMS, VIBRATION, ANGLE + ACCELERATION, PEAK_ACCELERATION, RMS, VIBRATION, ANGLE, ROTATIONS } enum class AccelScale(val k: Int) {