filter saving

This commit is contained in:
Vineyro 2024-10-18 17:00:51 +07:00
parent 666757922d
commit ab27faa832
16 changed files with 608 additions and 375 deletions

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@ -20,7 +20,7 @@ android {
minSdk 26
targetSdk 34
versionCode 41
versionName "1.4.12"
versionName "1.4.13"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {

View File

@ -52,7 +52,8 @@ class BleMapper @Inject constructor(
tx = txMapper.map(input.state.tx)
),
hostState = BleView.Host.HostState(
historyInterval = input.hostState.historyInterval
historyInterval = input.hostState.historyInterval,
readInterval = input.hostState.readInterval
)
)
}

View File

@ -52,7 +52,8 @@ class BleViewMapper @Inject constructor(
tx = txMapper.map(input.state.tx)
),
hostState = Ble.Host.HostState(
historyInterval = input.hostState.historyInterval
historyInterval = input.hostState.historyInterval,
readInterval = input.hostState.readInterval
)
)
}

View File

@ -1,6 +1,7 @@
package llc.arma.ble.app.ui.model
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import llc.arma.ble.domain.model.Ble
@ -65,8 +66,10 @@ sealed class BleView(
class HostState(
historyInterval: Long,
readInterval: Long
) {
var historyInterval by mutableStateOf(historyInterval)
var historyInterval by mutableLongStateOf(historyInterval)
var readInterval by mutableLongStateOf(readInterval)
}
}

View File

@ -31,7 +31,15 @@ class HostContract {
data object OnShowIntervalEdit : Event()
data class OnSaveIntervalChanged(val interval: Long) : Event()
data class OnSaveIntervalChanged(
val interval: Long
) : Event()
data object OnShowReadIntervalEdit : Event()
data class OnSaveReadIntervalChanged(
val interval: Long
) : Event()
data object OnNavigateUpClicked : Event()
@ -87,6 +95,10 @@ class HostContract {
data object ShowIntervalPicker : Effect()
data object HideReadIntervalPicker : Effect()
data object ShowReadIntervalPicker : Effect()
sealed class Navigation : Effect() {
data object NavigateToChangePassword : Navigation()

View File

@ -21,11 +21,12 @@ import llc.arma.ble.app.ui.common.rememberBottomDialogState
import llc.arma.ble.app.ui.screen.inspection.host.view.DisplayState
import llc.arma.ble.app.ui.screen.inspection.host.view.IntervalEdit
import llc.arma.ble.app.ui.screen.inspection.host.view.PowerEdit
import llc.arma.ble.app.ui.screen.inspection.host.view.ReadIntervalEdit
import llc.arma.ble.app.ui.screen.inspection.host.view.Write
import llc.arma.ble.domain.model.Ble
enum class SheetPage {
WRITE, POWER_EDIT, INTERVAL_EDIT
WRITE, POWER_EDIT, INTERVAL_EDIT, READ_INTERVAL_EDIT
}
@Composable
@ -72,6 +73,15 @@ fun HostScreen(
delay(100)
sheetPage = SheetPage.INTERVAL_EDIT
}
HostContract.Effect.HideReadIntervalPicker -> launch {
sheetPage = null
}
HostContract.Effect.ShowReadIntervalPicker -> launch {
sheetPage = null
delay(100)
sheetPage = SheetPage.READ_INTERVAL_EDIT
}
}
}.launchIn(this)
}
@ -122,6 +132,18 @@ fun HostScreen(
)
}
}
SheetPage.READ_INTERVAL_EDIT -> bottomDialog.show {
val currentState = viewModel.viewState.value
if(currentState is HostContract.State.Display) {
ReadIntervalEdit(
state = currentState.host,
onEvent = {
viewModel.setEvent(it)
}
)
}
}
else -> {
bottomDialog.hide()
}

View File

@ -35,9 +35,39 @@ class HostViewModel @Inject constructor(
is HostContract.Event.OnShowHostBleTable -> reduce(viewState.value, event)
is HostContract.Event.OnSaveIntervalChanged -> reduce(viewState.value, event)
is HostContract.Event.OnShowIntervalEdit -> reduce(viewState.value, event)
is HostContract.Event.OnSaveReadIntervalChanged -> reduce(viewState.value, event)
is HostContract.Event.OnShowReadIntervalEdit -> reduce(viewState.value, event)
}
}
private fun reduce(
state: HostContract.State,
event: HostContract.Event.OnSaveReadIntervalChanged
) {
if(state is HostContract.State.Display) {
state.host.hostState.readInterval = event.interval
}
setEffect {
HostContract.Effect.HideReadIntervalPicker
}
}
private fun reduce(
state: HostContract.State,
event: HostContract.Event.OnShowReadIntervalEdit
) {
setEffect {
HostContract.Effect.ShowReadIntervalPicker
}
}
private fun reduce(
state: HostContract.State,
event: HostContract.Event.OnSaveIntervalChanged
@ -195,7 +225,8 @@ class HostViewModel @Inject constructor(
val writeRequest = Ble.Host.WriteRequest(
tx = if(newBle.state.tx == state.origin.state.tx) null else newBle.state.tx,
interval = if(newBle.hostState.historyInterval == state.origin.hostState.historyInterval) null else newBle.hostState.historyInterval
interval = if(newBle.hostState.historyInterval == state.origin.hostState.historyInterval) null else newBle.hostState.historyInterval,
readInterval = if(newBle.hostState.readInterval == state.origin.hostState.readInterval) null else newBle.hostState.readInterval,
)
setState {

View File

@ -67,12 +67,12 @@ fun DisplayState(
onEvent(HostContract.Event.OnPowerEdit)
}
val hours =
ble.hostState.historyInterval / llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInHour
val minutes =
(ble.hostState.historyInterval - (hours * llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInHour)) / llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInMinute
val seconds =
(ble.hostState.historyInterval - (hours * llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInHour) - (minutes * llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInMinute)) / llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInSecond
var hours =
ble.hostState.historyInterval / millisInHour
var minutes =
(ble.hostState.historyInterval - (hours * millisInHour)) / millisInMinute
var seconds =
(ble.hostState.historyInterval - (hours * millisInHour) - (minutes * millisInMinute)) / millisInSecond
BleMenuItem(
title = "Интервал измерений",
@ -82,6 +82,20 @@ fun DisplayState(
onEvent(HostContract.Event.OnShowIntervalEdit)
}
hours = ble.hostState.readInterval / millisInHour
minutes =
(ble.hostState.readInterval - (hours * millisInHour)) / millisInMinute
seconds =
(ble.hostState.readInterval - (hours * millisInHour) - (minutes * millisInMinute)) / millisInSecond
BleMenuItem(
title = "Интервал чтения",
subtitle = "$hours ч. $minutes мин. $seconds сек.",
icon = rememberVectorPainter(Icons.Rounded.KeyboardArrowDown)
) {
onEvent(HostContract.Event.OnShowReadIntervalEdit)
}
BleMenuItem(
title = "График измерений",
icon = rememberVectorPainter(Icons.Rounded.KeyboardArrowRight)

View File

@ -47,27 +47,6 @@ fun IntervalEdit(
mutableIntStateOf((state.hostState.historyInterval).toInt())
}
val maxInterval = 10 * 24 * 60 * 60 * 1000
val minInterval = 10_000
if(value > maxInterval){
value = maxInterval
}
if(value < minInterval){
value = minInterval
}
val maxSeconds = maxInterval / millisInSecond
val maxMinutes = maxInterval / millisInMinute
val maxHours = maxInterval / millisInHour
val maxDays = maxInterval / millisInDay
val dayValue = value / millisInDay
val hourValue = (value - (dayValue * millisInDay)) / millisInHour
val minutesValue = (value - (dayValue * millisInDay) - (hourValue * millisInHour)) / millisInMinute
val secondsValue = (value - (dayValue * millisInDay) - (hourValue * millisInHour) - (minutesValue * millisInMinute)) / millisInSecond
Column(
modifier = Modifier
) {
@ -80,66 +59,11 @@ fun IntervalEdit(
Spacer(modifier = Modifier.height(16.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.align(Alignment.CenterHorizontally)
DurationPicker(
modifier = Modifier.align(Alignment.CenterHorizontally),
value = value
) {
NumberPicker(
range = -1..maxDays,
value = dayValue,
onValueChanged = {
value = (it * millisInDay) + (hourValue * millisInHour) + (minutesValue * millisInMinute) + (secondsValue * millisInSecond)
}
)
Spacer(modifier = Modifier.width(8.dp))
Text(text = "Д.")
Spacer(modifier = Modifier.width(16.dp))
NumberPicker(
range = -1..maxHours,
value = hourValue,
onValueChanged = {
value = (it * millisInHour) + (dayValue * millisInDay) + (minutesValue * millisInMinute) + (secondsValue * millisInSecond)
}
)
Spacer(modifier = Modifier.width(8.dp))
Text(text = "Ч.")
Spacer(modifier = Modifier.width(16.dp))
NumberPicker(
range = -1..maxMinutes,
value = minutesValue,
onValueChanged = {
value = (secondsValue * millisInSecond) + (it * millisInMinute) + (dayValue * millisInDay) + (hourValue * millisInHour)
}
)
Spacer(modifier = Modifier.width(8.dp))
Text(text = "М.")
Spacer(modifier = Modifier.width(16.dp))
NumberPicker(
range = -1..maxSeconds,
value = secondsValue,
onValueChanged = {
value = (it * millisInSecond) + (minutesValue * millisInMinute) + (dayValue * millisInDay) + (hourValue * millisInHour)
}
)
Spacer(modifier = Modifier.width(8.dp))
Text(text = "С.")
value = it
}
Spacer(modifier = Modifier.height(16.dp))
@ -177,6 +101,116 @@ fun IntervalEdit(
}
@Composable
fun DurationPicker(
modifier: Modifier,
maxInterval: Int = 10 * 24 * 60 * 60 * 1000,
minInterval: Int = 10_000,
seconds: Boolean = true,
minutes: Boolean = true,
hours: Boolean = true,
days: Boolean = true,
value: Int,
onChanged: (duration: Int) -> Unit
){
if(value > maxInterval){
onChanged(maxInterval)
}
if(value < minInterval){
onChanged(minInterval)
}
val maxSeconds = maxInterval / millisInSecond
val maxMinutes = maxInterval / millisInMinute
val maxHours = maxInterval / millisInHour
val maxDays = maxInterval / millisInDay
val dayValue = value / millisInDay
val hourValue = (value - (dayValue * millisInDay)) / millisInHour
val minutesValue = (value - (dayValue * millisInDay) - (hourValue * millisInHour)) / millisInMinute
val secondsValue = (value - (dayValue * millisInDay) - (hourValue * millisInHour) - (minutesValue * millisInMinute)) / millisInSecond
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
) {
if(days) {
NumberPicker(
range = -1..maxDays,
value = dayValue,
onValueChanged = {
onChanged((it * millisInDay) + (hourValue * millisInHour) + (minutesValue * millisInMinute) + (secondsValue * millisInSecond))
}
)
Spacer(modifier = Modifier.width(8.dp))
Text(text = "Д.")
}
if(hours) {
Spacer(modifier = Modifier.width(16.dp))
NumberPicker(
range = -1..maxHours,
value = hourValue,
onValueChanged = {
onChanged((it * millisInHour) + (dayValue * millisInDay) + (minutesValue * millisInMinute) + (secondsValue * millisInSecond))
}
)
Spacer(modifier = Modifier.width(8.dp))
Text(text = "Ч.")
}
if(minutes) {
Spacer(modifier = Modifier.width(16.dp))
NumberPicker(
range = -1..maxMinutes,
value = minutesValue,
onValueChanged = {
onChanged((secondsValue * millisInSecond) + (it * millisInMinute) + (dayValue * millisInDay) + (hourValue * millisInHour))
}
)
Spacer(modifier = Modifier.width(8.dp))
Text(text = "М.")
}
if(seconds) {
Spacer(modifier = Modifier.width(16.dp))
NumberPicker(
range = -1..maxSeconds,
value = secondsValue,
onValueChanged = {
onChanged((it * millisInSecond) + (minutesValue * millisInMinute) + (dayValue * millisInDay) + (hourValue * millisInHour))
}
)
Spacer(modifier = Modifier.width(8.dp))
Text(text = "С.")
}
}
}
const val millisInSecond = 1000
const val millisInMinute = millisInSecond * 60
const val millisInHour = millisInMinute * 60

View File

@ -0,0 +1,102 @@
package llc.arma.ble.app.ui.screen.inspection.host.view
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.SizeTransform
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.KeyboardArrowDown
import androidx.compose.material.icons.rounded.KeyboardArrowUp
import androidx.compose.material3.FilledIconButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import llc.arma.ble.app.ui.model.BleView
import llc.arma.ble.app.ui.screen.inspection.host.HostContract
@Composable
fun ReadIntervalEdit(
state: BleView.Host,
onEvent: (HostContract.Event) -> Unit,
){
var value by remember(state.hostState.readInterval) {
mutableIntStateOf((state.hostState.readInterval).toInt())
}
Column(
modifier = Modifier
) {
Text(
modifier = Modifier.padding(horizontal = 12.dp),
text = "Интервал чтения",
style = MaterialTheme.typography.titleLarge
)
Spacer(modifier = Modifier.height(16.dp))
DurationPicker(
modifier = Modifier.align(Alignment.CenterHorizontally),
value = value
) {
value = it
}
Spacer(modifier = Modifier.height(16.dp))
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.height(50.dp),
shape = CircleShape,
color = MaterialTheme.colorScheme.primaryContainer,
onClick = {
onEvent(
HostContract.Event.OnSaveReadIntervalChanged(
value.toLong()
)
)
}
) {
Box(modifier = Modifier.fillMaxSize()) {
Text(
modifier = Modifier.align(Alignment.Center),
color = MaterialTheme.colorScheme.background,
style = MaterialTheme.typography.labelLarge,
text = "Применить"
)
}
}
}
}

View File

@ -49,7 +49,7 @@ fun Write(
when (state) {
is HostContract.State.Display.WriteState.DisplayPreview -> {
if(state.writeRequest.tx != null || state.writeRequest.interval != null ) {
if(state.writeRequest.tx != null || state.writeRequest.interval != null || state.writeRequest.readInterval !== null) {
state.writeRequest.tx?.let {
Box(
@ -110,9 +110,51 @@ fun Write(
text = "Интервал измерений"
)
val hours = it / llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInHour
val minutes = (it - (hours * llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInHour)) / llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInMinute
val seconds = (it - (hours * llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInHour) - (minutes * llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInMinute)) / llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInSecond
val hours = it / millisInHour
val minutes = (it - (hours * millisInHour)) / millisInMinute
val seconds = (it - (hours * millisInHour) - (minutes * millisInMinute)) / millisInSecond
Text(
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.bodyMedium,
text = "$hours ч. $minutes мин. $seconds сек."
)
}
}
}
}
state.writeRequest.readInterval?.let {
Box(
modifier = Modifier.padding(
vertical = 0.dp,
horizontal = 8.dp
)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(RoundedCornerShape(16.dp))
.padding(8.dp)
) {
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = "Интервал чтения"
)
val hours = it / millisInHour
val minutes = (it - (hours * millisInHour)) / millisInMinute
val seconds = (it - (hours * millisInHour) - (minutes * millisInMinute)) / millisInSecond
Text(
color = MaterialTheme.colorScheme.secondary,

View File

@ -35,6 +35,7 @@ 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.client.main.service.ClientBleGattCharacteristic
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.BleScannerCallbackType
@ -65,9 +66,6 @@ val passwordWriteUUID: UUID = UUID.fromString("a77db6f2-9bc4-11ed-a8fc-0242ac120
val txWriteUUID: UUID = UUID.fromString("00002a07-0000-1000-8000-00805f9b34fb")
val flashWriteUUID: UUID = UUID.fromString("a77db6f2-9bc4-11ed-a8fc-0242ac120002")
@Singleton
class BleRepositoryImpl @Inject constructor(
private val app: Application,
@ -135,200 +133,136 @@ class BleRepositoryImpl @Inject constructor(
serial: String
): Result<Flow<Ble>, BleException> {
resultList[serial]?.let { result ->
val initialBle = resultList[serial]
return when(result.type) {
BleInfo.Type.ACCELEROMETER -> {
return if(initialBle == null){
val tState = readAccelState(
result.serial,
result.tableStatus
).fold(
onFailure = {
return Result.failure(it)
},
onSuccess = {
it
}
)
Result.failure(BleException.UnexpectedResponse)
Result.success(
flow {
while (true) {
resultList[serial]?.let { newResult ->
emit(
Ble.Accelerometer(
info = newResult.copy(
rssi = if((SystemClock.elapsedRealtime() - newResult.scanTime) > 15_000) {
null
} else {
newResult.rssi
fun BleInfo.updateBleInfo(): BleInfo {
return copy(
rssi = if((SystemClock.elapsedRealtime() - scanTime) > 15_000) {
null
} else { rssi }
)
}
),
state = Ble.BleState(
tx = Ble.BleState.TX.fromByte(result.tx.toByte())
fun BleInfo.updateState(): Ble.BleState {
return Ble.BleState(
tx = Ble.BleState.TX.fromByte(tx.toByte())
?: Ble.BleState.TX.ZERO
),
accelerometerState = tState
)
)
}
delay(1_000)
val firstResult = when(initialBle.type){
BleInfo.Type.HOST -> {
}
}
val tState = readHostState(
initialBle.serial
).fold(
onFailure = { return Result.failure(it) },
onSuccess = { it }
)
Ble.Host(
info = initialBle.updateBleInfo(),
state = initialBle.updateState(),
hostState = tState
)
}
BleInfo.Type.BEACON -> {
Result.success(
flow {
while (true) {
resultList[serial]?.let { newResult ->
val state = Ble.BleState(
tx = Ble.BleState.TX.fromByte(result.tx.toByte())
?: Ble.BleState.TX.ZERO
)
emit(
Ble.Beacon(
info = newResult.copy(
rssi = if((SystemClock.elapsedRealtime() - newResult.scanTime) > 15_000) {
null
} else {
newResult.rssi
}
),
state = state
)
)
}
delay(1_000)
}
}
info = initialBle.updateBleInfo(),
state = initialBle.updateState(),
)
}
BleInfo.Type.THERMOMETER -> {
val tState = readThermometerState(result.serial, result.tableStatus).fold(
onFailure = {
return Result.failure(it)
},
onSuccess = {
it
}
val tState = readThermometerState(
initialBle.serial,
initialBle.tableStatus
).fold(
onFailure = { return Result.failure(it) },
onSuccess = { it }
)
Result.success(
flow {
while (true) {
resultList[serial]?.let { newResult ->
val state = Ble.BleState(
tx = Ble.BleState.TX.fromByte(result.tx.toByte())
?: Ble.BleState.TX.ZERO
)
emit(
Ble.Thermometer(
info = newResult.copy(
rssi = if((SystemClock.elapsedRealtime() - newResult.scanTime) > 15_000) {
null
} else {
newResult.rssi
}
),
state = state,
info = initialBle.updateBleInfo(),
state = initialBle.updateState(),
thermometerState = tState
)
}
BleInfo.Type.ACCELEROMETER -> {
val tState = readAccelState(
initialBle.serial,
initialBle.tableStatus
).fold(
onFailure = { return Result.failure(it) },
onSuccess = { it }
)
Ble.Accelerometer(
info = initialBle.updateBleInfo(),
state = initialBle.updateState(),
accelerometerState = tState
)
}
delay(1_000)
}
}
)
}
BleInfo.Type.HOST -> {
val tState = readHostState(result.serial).fold(
onFailure = {
return Result.failure(it)
},
onSuccess = {
it
}
)
Result.success(
flow {
while (true){
while (true) {
resultList[serial]?.let { ble ->
resultList[serial]?.let { newResult ->
firstResult.state = ble.updateState()
firstResult.info = ble.updateBleInfo()
val state = Ble.BleState(
tx = Ble.BleState.TX.fromByte(result.tx.toByte())
?: Ble.BleState.TX.ZERO
emit(when(firstResult){
is Ble.Accelerometer -> {
Ble.Accelerometer(
info = ble.updateBleInfo(),
state = ble.updateState(),
accelerometerState = firstResult.accelerometerState
)
emit(
Ble.Host(
info = newResult.copy(
rssi = if((SystemClock.elapsedRealtime() - newResult.scanTime) > 15_000) {
null
} else {
newResult.rssi
}
),
state = state,
hostState = tState
is Ble.Beacon -> {
Ble.Beacon(
info = ble.updateBleInfo(),
state = ble.updateState()
)
}
is Ble.Host -> {
Ble.Host(
info = ble.updateBleInfo(),
state = ble.updateState(),
hostState = firstResult.hostState
)
}
is Ble.Thermometer -> {
Ble.Thermometer(
info = ble.updateBleInfo(),
state = ble.updateState(),
thermometerState = firstResult.thermometerState
)
}
})
}
delay(1_000)
}
}
)
}
}
}
return Result.failure(BleException.UnexpectedResponse)
}
@ -345,28 +279,25 @@ class BleRepositoryImpl @Inject constructor(
try {
val service = connection.discoverServices()
.findService(serviceUUID) ?: return Result.failure(BleException.UnexpectedResponse)
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.get4byteUIntAt(0).toLong()
}else{
0
}
}
val interval = characteristic.readWriteInterval().fold(
onFailure = { return Result.failure(it) },
onSuccess = { it }
)
connection.close()
@ -406,33 +337,31 @@ class BleRepositoryImpl @Inject constructor(
try {
val service = connection.discoverServices()
.findService(serviceUUID) ?: return Result.failure(BleException.UnexpectedResponse)
val service = connection.discoverServices().findService(serviceUUID)
?: return Result.failure(BleException.UnexpectedResponse)
val characteristic = service.findCharacteristic(intervalReadUUID)
?: return Result.failure(BleException.UnexpectedResponse)
if(characteristic == null){
service.characteristics.forEach {
Log.d("ble", "characteristic ${it.uuid} ")
}
Log.d("ble", "${intervalReadUUID} not found")
return Result.failure(BleException.UnexpectedResponse)
}
val interval = characteristic.readWriteInterval().fold(
onFailure = { return Result.failure(it) },
onSuccess = { it }
)
/*characteristic.write(DataByteArray.from(2, 0, 0, 0))
characteristic.write(DataByteArray.from(3, 0, 0, 0))
val interval = characteristic.read().value.let {
val readTimeout = characteristic.read().value.let {
if(it.size == 4){
it.get4byteUIntAt(0).toLong()
}else{
} else {
0
}
}
}*/
return Result.success(
Ble.Host.HostState(
historyInterval = interval
historyInterval = interval,
readInterval = interval
)
)
@ -453,6 +382,32 @@ class BleRepositoryImpl @Inject constructor(
}
private suspend fun ClientBleGattCharacteristic.readWriteInterval(
): Result<Long, BleException> {
return if(app.checkPermission().not()) {
Result.failure(BleException.PermissionDenied)
} else {
write(DataByteArray.from(3, 0, 0, 0))
val interval = read().value.let {
if (it.size == 4) {
it.get4byteUIntAt(0).toLong()
} else {
0
}
}
Result.success(interval)
}
}
private suspend fun readAccelState(
address: String,
timer: BleInfo.HistoryTableStatus
@ -471,15 +426,10 @@ class BleRepositoryImpl @Inject constructor(
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.get4byteUIntAt(0).toLong()
} else {
0
}
}
val interval = characteristic.readWriteInterval().fold(
onFailure = { return Result.failure(it) },
onSuccess = { it }
)
val historySettingsParams = when(timer){
BleInfo.HistoryTableStatus.EMPTY,
@ -561,7 +511,7 @@ class BleRepositoryImpl @Inject constructor(
}
override suspend fun addBleToHostTableBySerial(serial: String, ble: List<String>): Result<Int, BleException> {
return editBleToHostTable(serial, ble, app)
return editBleHostTable(serial, ble, app)
}
override suspend fun getHostHistoryBySerial(
@ -747,6 +697,20 @@ class BleRepositoryImpl @Inject constructor(
}
request.readInterval?.let {
service.findCharacteristic(intervalWriteUUID)!!.write(
DataByteArray.from(
*mutableListOf<Byte>(2).apply {
addAll(
(it / 1000).toUInt().to4ByteArrayInLittleEndian().reversed().toList()
)
}.toByteArray()
)
)
}
service.findCharacteristic(
flashWriteUUID
)!!.write(
@ -790,9 +754,9 @@ class BleRepositoryImpl @Inject constructor(
try {
val services = connection.discoverServices()
val service = services.findService(serviceUUID) ?: return Result.failure(
BleException.UnexpectedResponse
)
val service = services.findService(serviceUUID)
?: return Result.failure(BleException.UnexpectedResponse)
Log.d("write", request.toString())

View File

@ -1,8 +1,12 @@
package llc.arma.ble.data.repository
import android.Manifest
import android.annotation.SuppressLint
import android.app.Application
import android.content.pm.PackageManager
import android.util.Log
import androidx.annotation.RequiresPermission
import androidx.core.app.ActivityCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
@ -18,12 +22,11 @@ import llc.arma.ble.domain.model.Ble
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.service.ClientBleGattCharacteristic
import okio.ByteString.Companion.decodeHex
import java.nio.ByteBuffer
import java.util.BitSet
import java.util.Locale
@SuppressLint("MissingPermission")
@RequiresPermission(allOf = ["android.permission.BLUETOOTH_CONNECT"])
suspend fun readTable(
characteristic: ClientBleGattCharacteristic,
startRequest: ByteArray,
@ -172,20 +175,34 @@ fun readHostHistory(
var periods = mutableListOf<Pair<Boolean, List<String>>>()
var periodBle = mutableListOf<String>()
var hasHit = false
do {
val bleIdTableCell = firstTablePackage.drop(bleTableOffset).take(2).toByteArray()
val bleIdTableCell =
firstTablePackage.drop(bleTableOffset).take(2).toByteArray()
println("cell ${bleIdTableCell.toHexString()} ${bleIdTableCell.joinToString()}")
val intervalEnd = "ff0f"
val intervalEndAndHit = "fe0f"
if(bleIdTableCell.contentEquals(byteArrayOf(-1, 15)).not()) {
if (bleIdTableCell.contentEquals(intervalEnd.hexToByteArray())) {
if(bleIdTableCell.contentEquals(byteArrayOf(-2, 15))){
bleTableOffset += 2
hasHit = true
if(periodBle.isEmpty()) bleTableOffset += 2
periods.add(Pair(false, periodBle))
periodBle = mutableListOf()
continue
}
if (bleIdTableCell.contentEquals(intervalEndAndHit.hexToByteArray())) {
bleTableOffset += 2
if(periodBle.isEmpty()) bleTableOffset += 2
periods.add(Pair(true, periodBle))
periodBle = mutableListOf()
continue
}
val innerIndex = getInnerIndex(bleIdTableCell[1])
@ -204,6 +221,9 @@ fun readHostHistory(
val devDataSize = getDevDataSize(devTypeByte)
bleTableOffset += 2
if(innerIndex == 0)
bleTableOffset += 2
if (devDataSize != 0) {
val payload = getBleIdIndex(
firstTablePackage.drop(bleTableOffset).take(devDataSize)
@ -214,24 +234,6 @@ fun readHostHistory(
periodBle.add(serial)
} else {
bleTableOffset += 2
}
var nextIndex = 0
if(bleTableOffset <= firstTablePackage.size - 2){
nextIndex = getInnerIndex(firstTablePackage.drop(bleTableOffset)[1])
}
if(nextIndex == 0){
periods.add(Pair(hasHit, periodBle))
periodBle = mutableListOf()
hasHit = false
}
} while (bleTableOffset < firstTablePackage.size)
emit(
@ -290,7 +292,7 @@ suspend fun readHostBleTable(
?.findCharacteristic(hostHistoryReadUUID)
?: throw IllegalStateException()
characteristic.write(DataByteArray.from(2))
characteristic.write(DataByteArray.from(7))
var value = characteristic.read().value
@ -346,7 +348,7 @@ suspend fun readHostBleTable(
}
@OptIn(ExperimentalStdlibApi::class)
suspend fun editBleToHostTable(
suspend fun editBleHostTable(
address: String,
addBleAddress: List<String>,
app: Application,
@ -366,7 +368,9 @@ suspend fun editBleToHostTable(
characteristic.write(DataByteArray.from(12, 1))
val writeCount = addBleAddress.chunked(40).sumOf { bleAddressBatch ->
Log.i("ScanRecord", "write")
val writeCount = addBleAddress.chunked(20).sumOf { bleAddressBatch ->
val countPayload = ByteBuffer.allocate(2).putShort(bleAddressBatch.size.toShort()).array().reversed().toByteArray()

View File

@ -7,7 +7,6 @@ import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.google.gson.Gson
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.serialization.decodeFromString

View File

@ -4,14 +4,15 @@ import llc.arma.ble.domain.usecase.AccelScale
import llc.arma.ble.domain.usecase.AccelViewMode
sealed class Ble(
val info: BleInfo
var info: BleInfo,
var state: BleState,
) {
class Accelerometer(
info: BleInfo,
val state: BleState,
state: BleState,
val accelerometerState: AccelerometerState
): Ble(info) {
): Ble(info, state) {
sealed class HistorySettings {
@ -99,8 +100,8 @@ sealed class Ble(
class Beacon(
info: BleInfo,
val state: BleState
) : Ble(info){
state: BleState,
) : Ble(info, state){
data class WriteRequest(
val tx: BleState.TX?
@ -110,9 +111,9 @@ sealed class Ble(
class Host(
info: BleInfo,
val state: BleState,
state: BleState,
val hostState: HostState
) : Ble(info){
) : Ble(info, state){
class HistoryPoint(
val date: Long,
@ -121,21 +122,23 @@ sealed class Ble(
)
data class HostState(
val historyInterval: Long
val historyInterval: Long,
val readInterval: Long
)
data class WriteRequest(
val tx: BleState.TX?,
val interval: Long?
val interval: Long?,
val readInterval: Long?,
)
}
class Thermometer(
info: BleInfo,
val state: BleState,
state: BleState,
val thermometerState: ThermometerState
) : Ble(info) {
) : Ble(info, state) {
class HistoryPoint(
val date: Long,