kotlin ble migration
This commit is contained in:
parent
a059cacda9
commit
f65023b4c7
|
|
@ -11,8 +11,6 @@ import kotlinx.coroutines.launch
|
||||||
import llc.arma.ble.app.ui.common.BaseViewModel
|
import llc.arma.ble.app.ui.common.BaseViewModel
|
||||||
import llc.arma.ble.domain.usecase.ExportToXlsx
|
import llc.arma.ble.domain.usecase.ExportToXlsx
|
||||||
import llc.arma.ble.domain.usecase.GetBleAroundFlow
|
import llc.arma.ble.domain.usecase.GetBleAroundFlow
|
||||||
import llc.arma.ble.domain.usecase.GetConnectedBleDevices
|
|
||||||
import llc.arma.ble.domain.usecase.MeasureData
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,17 @@
|
||||||
package llc.arma.ble.data
|
package llc.arma.ble.data
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.bluetooth.*
|
import android.bluetooth.*
|
||||||
import android.bluetooth.le.ScanCallback
|
import android.bluetooth.le.ScanCallback
|
||||||
import android.bluetooth.le.ScanResult
|
import android.bluetooth.le.ScanResult
|
||||||
import android.bluetooth.le.ScanSettings
|
import android.bluetooth.le.ScanSettings
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.app.ActivityCompat
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
import kotlinx.coroutines.flow.filter
|
|
||||||
import kotlinx.coroutines.flow.first
|
|
||||||
import kotlinx.coroutines.flow.flow
|
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.Result
|
||||||
import llc.arma.ble.domain.common.BleException
|
import llc.arma.ble.domain.common.BleException
|
||||||
import llc.arma.ble.domain.common.ProgressState
|
import llc.arma.ble.domain.common.ProgressState
|
||||||
|
|
@ -37,22 +28,13 @@ import llc.arma.ble.domain.usecase.FftFrequency
|
||||||
import llc.arma.ble.domain.usecase.FftViewMode
|
import llc.arma.ble.domain.usecase.FftViewMode
|
||||||
import no.nordicsemi.android.common.core.DataByteArray
|
import no.nordicsemi.android.common.core.DataByteArray
|
||||||
import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt
|
import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt
|
||||||
import no.nordicsemi.android.kotlin.ble.core.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.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 no.nordicsemi.android.kotlin.ble.scanner.aggregator.BleScanResultAggregator
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
import kotlin.coroutines.resume
|
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
import kotlin.math.atan
|
import kotlin.math.atan
|
||||||
import kotlin.math.pow
|
|
||||||
import kotlin.math.sqrt
|
|
||||||
|
|
||||||
|
|
||||||
val FftFrequency.sendData: Byte
|
val FftFrequency.sendData: Byte
|
||||||
|
|
@ -728,6 +710,8 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
frequency: FftFrequency
|
frequency: FftFrequency
|
||||||
): Flow<Result<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, BleException>> {
|
): Flow<Result<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, BleException>> {
|
||||||
|
|
||||||
|
//return readAccelerometerSpectre(serial, app, accelScale, accelMode, fftAxis, fftMode, frequency)
|
||||||
|
|
||||||
var gatt: BluetoothGatt? = null
|
var gatt: BluetoothGatt? = null
|
||||||
|
|
||||||
return callbackFlow {
|
return callbackFlow {
|
||||||
|
|
@ -769,7 +753,9 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
serial: String
|
serial: String
|
||||||
): Flow<Result<ProgressState<List<Ble.Thermometer.MeasurePoint>>, BleException>> {
|
): Flow<Result<ProgressState<List<Ble.Thermometer.MeasurePoint>>, BleException>> {
|
||||||
|
|
||||||
var gatt: BluetoothGatt? = null
|
return readThermometerHistory(serial, app)
|
||||||
|
|
||||||
|
/*var gatt: BluetoothGatt? = null
|
||||||
|
|
||||||
return callbackFlow {
|
return callbackFlow {
|
||||||
|
|
||||||
|
|
@ -799,7 +785,7 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
gatt?.close()
|
gatt?.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1199,15 +1185,15 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
|
|
||||||
when(accelMode){
|
when(accelMode){
|
||||||
ANGLE -> {
|
ANGLE -> {
|
||||||
x = calculateZAngle(
|
x = calculateAngle(
|
||||||
data[2].toFloat(),
|
data[2].toFloat(),
|
||||||
data[1].toFloat()
|
data[1].toFloat()
|
||||||
) * 180f / Math.PI.toFloat()
|
) * 180f / Math.PI.toFloat()
|
||||||
y = calculateZAngle(
|
y = calculateAngle(
|
||||||
data[2].toFloat(),
|
data[2].toFloat(),
|
||||||
data[0].toFloat()
|
data[0].toFloat()
|
||||||
) * 180f / Math.PI.toFloat()
|
) * 180f / Math.PI.toFloat()
|
||||||
z = calculateZAngle(
|
z = calculateAngle(
|
||||||
data[0].toFloat(),
|
data[0].toFloat(),
|
||||||
data[1].toFloat()
|
data[1].toFloat()
|
||||||
) * 180f / Math.PI.toFloat()
|
) * 180f / Math.PI.toFloat()
|
||||||
|
|
@ -1287,16 +1273,6 @@ class BleRepositoryImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun calculateAngle(
|
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,
|
x: Float,
|
||||||
y: Float
|
y: Float
|
||||||
): Float {
|
): Float {
|
||||||
|
|
|
||||||
|
|
@ -170,15 +170,15 @@ fun readAccelerometerHistory(
|
||||||
resultTemperaturePackage.chunked(3).withIndex().map {
|
resultTemperaturePackage.chunked(3).withIndex().map {
|
||||||
Ble.Accelerometer.HistoryPoint.Angle(
|
Ble.Accelerometer.HistoryPoint.Angle(
|
||||||
date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!),
|
date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!),
|
||||||
x = calculateZAngle(
|
x = calculateAngle(
|
||||||
it.value[2],
|
it.value[2],
|
||||||
it.value[1]
|
it.value[1]
|
||||||
) * 180f / Math.PI.toFloat(),
|
) * 180f / Math.PI.toFloat(),
|
||||||
y = calculateZAngle(
|
y = calculateAngle(
|
||||||
it.value[2],
|
it.value[2],
|
||||||
it.value[0]
|
it.value[0]
|
||||||
) * 180f / Math.PI.toFloat(),
|
) * 180f / Math.PI.toFloat(),
|
||||||
z = calculateZAngle(
|
z = calculateAngle(
|
||||||
it.value[0],
|
it.value[0],
|
||||||
it.value[1]
|
it.value[1]
|
||||||
) * 180f / Math.PI.toFloat()
|
) * 180f / Math.PI.toFloat()
|
||||||
|
|
|
||||||
|
|
@ -214,6 +214,8 @@ class ReadAccelerometerSpectreCallback(
|
||||||
status: Int
|
status: Int
|
||||||
){
|
){
|
||||||
|
|
||||||
|
Log.d("value", value.joinToString(separator = ""))
|
||||||
|
|
||||||
if(status == BluetoothGatt.GATT_SUCCESS){
|
if(status == BluetoothGatt.GATT_SUCCESS){
|
||||||
when(readProperty){
|
when(readProperty){
|
||||||
Property.DATA_SIZE -> {
|
Property.DATA_SIZE -> {
|
||||||
|
|
@ -454,25 +456,24 @@ class ReadAccelerometerSpectreCallback(
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
@OptIn(ExperimentalUnsignedTypes::class)
|
@OptIn(ExperimentalUnsignedTypes::class)
|
||||||
fun readAccelerometerSpectre(
|
fun readAccelerometerSpectre(
|
||||||
address: String,
|
address: String,
|
||||||
mode: AccelViewMode,
|
|
||||||
scale: AccelScale,
|
|
||||||
app: Application,
|
app: Application,
|
||||||
|
accelScale: AccelScale,
|
||||||
|
accelMode: AccelViewMode,
|
||||||
|
fftAxis: FftAxis,
|
||||||
|
fftMode: FftViewMode,
|
||||||
|
frequency: FftFrequency,
|
||||||
): Flow<Result<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, BleException>> {
|
): Flow<Result<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, BleException>> {
|
||||||
|
|
||||||
return flow {
|
return flow {
|
||||||
var lastMeasureSystemTime: Long? = null
|
|
||||||
|
|
||||||
var bleMeasureInterval: Long? = null
|
var initialValue: Long? = null
|
||||||
var bleRealTime: Long? = null
|
var frequencyInterval: Long? = null
|
||||||
var bleLastMeasureTime: Long? = null
|
|
||||||
|
|
||||||
val resultTemperaturePackage: MutableList<Float> = mutableListOf()
|
val resultAccelerometerPackage: MutableList<Float> = mutableListOf()
|
||||||
|
|
||||||
val result = mutableListOf<List<UByte>>()
|
|
||||||
|
|
||||||
var expectedDataSize: Int? = null
|
var expectedDataSize: Int? = null
|
||||||
|
|
||||||
|
|
@ -484,15 +485,45 @@ fun readAccelerometerSpectre(
|
||||||
ClientBleGatt.connect(app, address, CoroutineScope(Dispatchers.Default))
|
ClientBleGatt.connect(app, address, CoroutineScope(Dispatchers.Default))
|
||||||
|
|
||||||
val characteristic = connection.discoverServices()
|
val characteristic = connection.discoverServices()
|
||||||
|
.findService(serviceUUID)
|
||||||
|
?.findCharacteristic(accelerometerReadUUID)
|
||||||
|
?: throw IllegalStateException()
|
||||||
|
|
||||||
|
val historyCharacteristic = connection.discoverServices()
|
||||||
.findService(serviceUUID)
|
.findService(serviceUUID)
|
||||||
?.findCharacteristic(accelerometerHistoryReadUUID)
|
?.findCharacteristic(accelerometerHistoryReadUUID)
|
||||||
|
?: throw IllegalStateException()
|
||||||
|
|
||||||
if(characteristic != null) {
|
Log.d("notification", "-1")
|
||||||
|
|
||||||
|
Log.d("notification", "0")
|
||||||
|
|
||||||
|
characteristic.write(DataByteArray(byteArrayOf(
|
||||||
|
4,
|
||||||
|
accelMode.sendData,
|
||||||
|
accelScale.sendData,
|
||||||
|
fftMode.sendData,
|
||||||
|
fftAxis.sendData,
|
||||||
|
frequency.sendData,
|
||||||
|
2
|
||||||
|
)))
|
||||||
|
|
||||||
|
Log.d("notification", "1")
|
||||||
|
|
||||||
|
val notifications = characteristic.getNotifications()
|
||||||
|
|
||||||
|
notifications.collect {
|
||||||
|
|
||||||
|
|
||||||
characteristic.write(DataByteArray.from(2))
|
|
||||||
|
|
||||||
var value = characteristic.read().value
|
Log.d("notification", "0")
|
||||||
|
|
||||||
|
historyCharacteristic.write(DataByteArray.from(2))
|
||||||
|
|
||||||
|
var value = historyCharacteristic.read().value
|
||||||
|
|
||||||
|
Log.d("value", value.joinToString(separator = ""))
|
||||||
|
Log.d("value", value.get2byteUIntAt(0).toString())
|
||||||
|
|
||||||
if (value.contentEquals(byteArrayOf(0, 0))) {
|
if (value.contentEquals(byteArrayOf(0, 0))) {
|
||||||
|
|
||||||
|
|
@ -510,118 +541,74 @@ fun readAccelerometerSpectre(
|
||||||
addAll(value.toList())
|
addAll(value.toList())
|
||||||
}.toByteArray()
|
}.toByteArray()
|
||||||
|
|
||||||
characteristic.write(DataByteArray(writeData))
|
historyCharacteristic.write(DataByteArray(writeData))
|
||||||
value = characteristic.read().value
|
value = historyCharacteristic.read().value
|
||||||
var nextPackageDataCount = value.get2byteUIntAt(2)
|
var nextPackageDataCount = value.get2byteUIntAt(2)
|
||||||
|
|
||||||
while (nextPackageDataCount.toInt() != 0) {
|
while (nextPackageDataCount.toInt() != 0) {
|
||||||
|
|
||||||
val temperatureDataArray = if (value[0] == 250.toByte()) {
|
val accelerometerDataArray = if (value[0] == 250.toByte()) {
|
||||||
|
|
||||||
bleMeasureInterval = value.get4byteUIntAt(4).toLong()
|
initialValue = value.get4byteUIntAt(8).toLong()
|
||||||
bleLastMeasureTime = value.get4byteUIntAt(8).toLong()
|
frequencyInterval = value.get4byteUIntAt(4).toLong()
|
||||||
bleRealTime = value.get4byteUIntAt(12).toLong()
|
|
||||||
|
|
||||||
lastMeasureSystemTime =
|
value.asList().subList(16, value.size)
|
||||||
System.currentTimeMillis() - ((bleRealTime!! - bleLastMeasureTime!!) * 1_000)
|
|
||||||
|
|
||||||
value.toUByteArray().asList().subList(16, value.size)
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
value.asList().subList(4, value.size)
|
||||||
value.toUByteArray().asList().subList(4, value.size)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.add(value.toUByteArray().toList())
|
|
||||||
nextPackageDataCount = value.get2byteUIntAt(2)
|
nextPackageDataCount = value.get2byteUIntAt(2)
|
||||||
|
|
||||||
resultTemperaturePackage.addAll(
|
resultAccelerometerPackage.addAll(
|
||||||
temperatureDataArray.chunked(2).map {
|
accelerometerDataArray.chunked(2).map {
|
||||||
it.toUByteArray().toByteArray().get2byteShortAt().toFloat()
|
it.toByteArray().get2byteShortAt().toFloat()
|
||||||
}.toMutableList()
|
}.toMutableList()
|
||||||
)
|
)
|
||||||
|
|
||||||
Log.d(
|
expectedDataSize = nextPackageDataCount.toInt() + resultAccelerometerPackage.size
|
||||||
"received data size",
|
|
||||||
(temperatureDataArray.chunked(2).size).toString()
|
|
||||||
)
|
|
||||||
Log.d("next data size", nextPackageDataCount.toString())
|
|
||||||
|
|
||||||
expectedDataSize =
|
emit(Result.success(ProgressState.Progress(0f / expectedDataSize!!.toFloat())))
|
||||||
nextPackageDataCount.toInt() + resultTemperaturePackage.size
|
emit(Result.success(ProgressState.Progress(resultAccelerometerPackage.size.toFloat() / expectedDataSize!!.toFloat())))
|
||||||
|
|
||||||
emit(Result.success(ProgressState.Progress(0f / expectedDataSize.toFloat())))
|
historyCharacteristic.write(DataByteArray.from(5))
|
||||||
emit(Result.success(ProgressState.Progress(resultTemperaturePackage.size.toFloat() / expectedDataSize.toFloat())))
|
value = historyCharacteristic.read().value
|
||||||
|
|
||||||
characteristic.write(DataByteArray.from(5))
|
|
||||||
value = characteristic.read().value
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
Result.success(
|
Result.success(
|
||||||
ProgressState.Finished(
|
ProgressState.Finished(
|
||||||
when (mode) {
|
resultAccelerometerPackage.withIndex().map {
|
||||||
AccelViewMode.ROTATIONS,
|
Ble.Accelerometer.MeasurePoint(
|
||||||
AccelViewMode.ACCELERATION,
|
frequency = frequencyInterval!! * it.index + initialValue!!,
|
||||||
AccelViewMode.PEAK_ACCELERATION,
|
value = (it.value * accelScale.k) / Short.MAX_VALUE
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
characteristic.write(DataByteArray(byteArrayOf(
|
||||||
|
4,
|
||||||
|
accelMode.sendData,
|
||||||
} else {
|
accelScale.sendData,
|
||||||
|
fftMode.sendData,
|
||||||
emit(Result.failure(BleException.UnexpectedResponse))
|
fftAxis.sendData,
|
||||||
|
frequency.sendData,
|
||||||
|
2
|
||||||
|
)))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} catch (err: Throwable) {
|
} catch (err: Throwable) {
|
||||||
|
|
||||||
|
err.printStackTrace()
|
||||||
emit(Result.failure(BleException.UnexpectedResponse))
|
emit(Result.failure(BleException.UnexpectedResponse))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -636,4 +623,4 @@ fun readAccelerometerSpectre(
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}*/
|
}
|
||||||
|
|
@ -9,10 +9,18 @@ import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.app.ActivityCompat
|
import 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.Result
|
||||||
import llc.arma.ble.domain.common.BleException
|
import llc.arma.ble.domain.common.BleException
|
||||||
import llc.arma.ble.domain.common.ProgressState
|
import llc.arma.ble.domain.common.ProgressState
|
||||||
import llc.arma.ble.domain.model.Ble
|
import llc.arma.ble.domain.model.Ble
|
||||||
|
import llc.arma.ble.domain.usecase.AccelScale
|
||||||
|
import llc.arma.ble.domain.usecase.AccelViewMode
|
||||||
|
import no.nordicsemi.android.common.core.DataByteArray
|
||||||
|
import no.nordicsemi.android.kotlin.ble.client.main.callback.ClientBleGatt
|
||||||
|
|
||||||
class ReadTemperatureHistoryCallback(
|
class ReadTemperatureHistoryCallback(
|
||||||
private val app: Application,
|
private val app: Application,
|
||||||
|
|
@ -320,3 +328,134 @@ class ReadTemperatureHistoryCallback(
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUnsignedTypes::class)
|
||||||
|
fun readThermometerHistory(
|
||||||
|
address: String,
|
||||||
|
app: Application,
|
||||||
|
): Flow<Result<ProgressState<List<Ble.Thermometer.MeasurePoint>>, BleException>> {
|
||||||
|
|
||||||
|
return flow {
|
||||||
|
|
||||||
|
var lastMeasureSystemTime: Long? = null
|
||||||
|
|
||||||
|
var bleMeasureInterval: Long? = null
|
||||||
|
var bleRealTime: Long? = null
|
||||||
|
var bleLastMeasureTime: Long? = null
|
||||||
|
|
||||||
|
val resultTemperaturePackage: MutableList<Float> = 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(temperatureHistoryReadUUID)
|
||||||
|
?: throw IllegalStateException()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
nextPackageDataCount = value.get2byteUIntAt(2)
|
||||||
|
|
||||||
|
resultTemperaturePackage.addAll(
|
||||||
|
temperatureDataArray.chunked(2).map {
|
||||||
|
it.toUByteArray().toTemperature()
|
||||||
|
}.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(
|
||||||
|
resultTemperaturePackage.withIndex().map {
|
||||||
|
Ble.Thermometer.MeasurePoint(
|
||||||
|
date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!),
|
||||||
|
value = it.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} catch (err: Throwable) {
|
||||||
|
|
||||||
|
emit(Result.failure(BleException.UnexpectedResponse))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
emit(Result.failure(BleException.PermissionDenied))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,230 +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 kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
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 WriteThermometerCallback(
|
|
||||||
private val app: Application,
|
|
||||||
private var request: Ble.Thermometer.WriteRequest,
|
|
||||||
private val onResult: (Result<Unit, BleException>) -> Unit
|
|
||||||
) : BluetoothGattCallback() {
|
|
||||||
|
|
||||||
private var flashed = false
|
|
||||||
|
|
||||||
override fun onConnectionStateChange(
|
|
||||||
gatt: BluetoothGatt,
|
|
||||||
status: Int,
|
|
||||||
newState: Int
|
|
||||||
) {
|
|
||||||
super.onConnectionStateChange(gatt, status, newState)
|
|
||||||
|
|
||||||
if(app.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 || 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<UUID, ByteArray, Ble.Thermometer.WriteRequest>? = null
|
|
||||||
|
|
||||||
uuid = request.historyInterval?.let {
|
|
||||||
|
|
||||||
Triple(
|
|
||||||
intervalWriteUUID,
|
|
||||||
mutableListOf<Byte>(3).apply {
|
|
||||||
addAll((it).toUInt().to4ByteArrayInLittleEndian().reversed().toList())
|
|
||||||
}.toByteArray(),
|
|
||||||
request.copy(
|
|
||||||
historyInterval = null
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
uuid = request.saveHistory?.let {
|
|
||||||
|
|
||||||
Triple(
|
|
||||||
saveEnabledWriteUUID,
|
|
||||||
mutableListOf<Byte>(4).apply {
|
|
||||||
add(if (it) 1 else 0)
|
|
||||||
}.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(app.checkPermission()) {
|
|
||||||
|
|
||||||
if(status == BluetoothGatt.GATT_SUCCESS || flashed) {
|
|
||||||
|
|
||||||
onCycle(gatt, status)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
onResult(Result.failure(BleException.UnexpectedResponse))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
onResult(Result.failure(BleException.PermissionDenied))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun BluetoothGatt.writeCharacteristic(
|
|
||||||
characteristic: BluetoothGattCharacteristic,
|
|
||||||
data: ByteArray
|
|
||||||
): Result<Unit, BleException> {
|
|
||||||
|
|
||||||
return if(app.checkPermission()){
|
|
||||||
|
|
||||||
Log.d("write", data.asUByteArray().joinToString(" ") { it.toString(16).padStart(2, '0') })
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
||||||
writeCharacteristic(characteristic, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT)
|
|
||||||
}else{
|
|
||||||
|
|
||||||
characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
|
|
||||||
characteristic.value = data
|
|
||||||
writeCharacteristic(characteristic)
|
|
||||||
}
|
|
||||||
|
|
||||||
Result.success(Unit)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Result.failure(BleException.PermissionDenied)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue