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"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <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" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View File

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

View File

@ -52,7 +52,8 @@ class BleMapper @Inject constructor(
tx = txMapper.map(input.state.tx) tx = txMapper.map(input.state.tx)
), ),
hostState = BleView.Host.HostState( 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) tx = txMapper.map(input.state.tx)
), ),
hostState = Ble.Host.HostState( 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 package llc.arma.ble.app.ui.model
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.model.Ble
@ -65,8 +66,10 @@ sealed class BleView(
class HostState( class HostState(
historyInterval: Long, 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 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() data object OnNavigateUpClicked : Event()
@ -87,6 +95,10 @@ class HostContract {
data object ShowIntervalPicker : Effect() data object ShowIntervalPicker : Effect()
data object HideReadIntervalPicker : Effect()
data object ShowReadIntervalPicker : Effect()
sealed class Navigation : Effect() { sealed class Navigation : Effect() {
data object NavigateToChangePassword : Navigation() 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.DisplayState
import llc.arma.ble.app.ui.screen.inspection.host.view.IntervalEdit 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.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.app.ui.screen.inspection.host.view.Write
import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.model.Ble
enum class SheetPage { enum class SheetPage {
WRITE, POWER_EDIT, INTERVAL_EDIT WRITE, POWER_EDIT, INTERVAL_EDIT, READ_INTERVAL_EDIT
} }
@Composable @Composable
@ -72,6 +73,15 @@ fun HostScreen(
delay(100) delay(100)
sheetPage = SheetPage.INTERVAL_EDIT 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) }.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 -> { else -> {
bottomDialog.hide() bottomDialog.hide()
} }

View File

@ -35,9 +35,39 @@ class HostViewModel @Inject constructor(
is HostContract.Event.OnShowHostBleTable -> reduce(viewState.value, event) is HostContract.Event.OnShowHostBleTable -> reduce(viewState.value, event)
is HostContract.Event.OnSaveIntervalChanged -> reduce(viewState.value, event) is HostContract.Event.OnSaveIntervalChanged -> reduce(viewState.value, event)
is HostContract.Event.OnShowIntervalEdit -> 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( private fun reduce(
state: HostContract.State, state: HostContract.State,
event: HostContract.Event.OnSaveIntervalChanged event: HostContract.Event.OnSaveIntervalChanged
@ -195,7 +225,8 @@ class HostViewModel @Inject constructor(
val writeRequest = Ble.Host.WriteRequest( val writeRequest = Ble.Host.WriteRequest(
tx = if(newBle.state.tx == state.origin.state.tx) null else newBle.state.tx, tx = if(newBle.state.tx == state.origin.state.tx) null else newBle.state.tx,
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 { setState {

View File

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

View File

@ -47,27 +47,6 @@ fun IntervalEdit(
mutableIntStateOf((state.hostState.historyInterval).toInt()) 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( Column(
modifier = Modifier modifier = Modifier
) { ) {
@ -80,66 +59,11 @@ fun IntervalEdit(
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
DurationPicker(
Row( modifier = Modifier.align(Alignment.CenterHorizontally),
verticalAlignment = Alignment.CenterVertically, value = value
modifier = Modifier.align(Alignment.CenterHorizontally)
) { ) {
value = it
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 = "С.")
} }
Spacer(modifier = Modifier.height(16.dp)) 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 millisInSecond = 1000
const val millisInMinute = millisInSecond * 60 const val millisInMinute = millisInSecond * 60
const val millisInHour = millisInMinute * 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) { when (state) {
is HostContract.State.Display.WriteState.DisplayPreview -> { 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 { state.writeRequest.tx?.let {
Box( Box(
@ -110,9 +110,51 @@ fun Write(
text = "Интервал измерений" text = "Интервал измерений"
) )
val hours = it / llc.arma.ble.app.ui.screen.inspection.accelerometer.view.millisInHour val hours = it / 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 minutes = (it - (hours * millisInHour)) / 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 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( Text(
color = MaterialTheme.colorScheme.secondary, 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 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.client.main.service.ClientBleGattCharacteristic
import no.nordicsemi.android.kotlin.ble.core.scanner.BleNumOfMatches 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.BleScanMode
import no.nordicsemi.android.kotlin.ble.core.scanner.BleScannerCallbackType 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 txWriteUUID: UUID = UUID.fromString("00002a07-0000-1000-8000-00805f9b34fb")
val flashWriteUUID: UUID = UUID.fromString("a77db6f2-9bc4-11ed-a8fc-0242ac120002") val flashWriteUUID: UUID = UUID.fromString("a77db6f2-9bc4-11ed-a8fc-0242ac120002")
@Singleton @Singleton
class BleRepositoryImpl @Inject constructor( class BleRepositoryImpl @Inject constructor(
private val app: Application, private val app: Application,
@ -135,200 +133,136 @@ class BleRepositoryImpl @Inject constructor(
serial: String serial: String
): Result<Flow<Ble>, BleException> { ): Result<Flow<Ble>, BleException> {
resultList[serial]?.let { result -> val initialBle = resultList[serial]
return when(result.type) { return if(initialBle == null){
BleInfo.Type.ACCELEROMETER -> {
val tState = readAccelState( Result.failure(BleException.UnexpectedResponse)
result.serial,
result.tableStatus
).fold(
onFailure = {
return Result.failure(it)
},
onSuccess = {
it
}
)
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 { } else {
newResult.rssi
fun BleInfo.updateBleInfo(): BleInfo {
return copy(
rssi = if((SystemClock.elapsedRealtime() - scanTime) > 15_000) {
null
} else { rssi }
)
} }
),
state = Ble.BleState( fun BleInfo.updateState(): Ble.BleState {
tx = Ble.BleState.TX.fromByte(result.tx.toByte()) return Ble.BleState(
tx = Ble.BleState.TX.fromByte(tx.toByte())
?: Ble.BleState.TX.ZERO ?: 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 -> { 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( Ble.Beacon(
info = newResult.copy( info = initialBle.updateBleInfo(),
rssi = if((SystemClock.elapsedRealtime() - newResult.scanTime) > 15_000) { state = initialBle.updateState(),
null
} else {
newResult.rssi
}
),
state = state
)
)
}
delay(1_000)
}
}
) )
} }
BleInfo.Type.THERMOMETER -> { BleInfo.Type.THERMOMETER -> {
val tState = readThermometerState(result.serial, result.tableStatus).fold( val tState = readThermometerState(
onFailure = { initialBle.serial,
return Result.failure(it) initialBle.tableStatus
}, ).fold(
onSuccess = { onFailure = { return Result.failure(it) },
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( Ble.Thermometer(
info = newResult.copy( info = initialBle.updateBleInfo(),
rssi = if((SystemClock.elapsedRealtime() - newResult.scanTime) > 15_000) { state = initialBle.updateState(),
null
} else {
newResult.rssi
}
),
state = state,
thermometerState = tState 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( Result.success(
flow { 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( emit(when(firstResult){
tx = Ble.BleState.TX.fromByte(result.tx.toByte()) is Ble.Accelerometer -> {
?: Ble.BleState.TX.ZERO 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
} }
is Ble.Beacon -> {
), Ble.Beacon(
state = state, info = ble.updateBleInfo(),
hostState = tState 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) delay(1_000)
} }
} }
) )
} }
}
}
return Result.failure(BleException.UnexpectedResponse)
} }
@ -345,28 +279,25 @@ class BleRepositoryImpl @Inject constructor(
try { try {
val service = connection.discoverServices() val service = connection.discoverServices().findService(serviceUUID)
.findService(serviceUUID) ?: return Result.failure(BleException.UnexpectedResponse) ?: return Result.failure(BleException.UnexpectedResponse)
var characteristic = service.findCharacteristic(temperatureReadUUID) var characteristic = service.findCharacteristic(temperatureReadUUID)
?: return Result.failure(BleException.UnexpectedResponse) ?: return Result.failure(BleException.UnexpectedResponse)
characteristic.write(DataByteArray.from(1, 1)) characteristic.write(DataByteArray.from(1, 1))
delay(2_000) delay(2_000)
val temperature = characteristic.read().value.toUByteArray().toTemperature() val temperature = characteristic.read().value.toUByteArray().toTemperature()
characteristic = service.findCharacteristic(intervalReadUUID) characteristic = service.findCharacteristic(intervalReadUUID)
?: return Result.failure(BleException.UnexpectedResponse) ?: return Result.failure(BleException.UnexpectedResponse)
characteristic.write(DataByteArray.from(3, 0, 0, 0, )) val interval = characteristic.readWriteInterval().fold(
onFailure = { return Result.failure(it) },
val interval = characteristic.read().value.let { onSuccess = { it }
if(it.size == 4){ )
it.get4byteUIntAt(0).toLong()
}else{
0
}
}
connection.close() connection.close()
@ -406,33 +337,31 @@ class BleRepositoryImpl @Inject constructor(
try { try {
val service = connection.discoverServices() val service = connection.discoverServices().findService(serviceUUID)
.findService(serviceUUID) ?: return Result.failure(BleException.UnexpectedResponse) ?: return Result.failure(BleException.UnexpectedResponse)
val characteristic = service.findCharacteristic(intervalReadUUID) val characteristic = service.findCharacteristic(intervalReadUUID)
?: return Result.failure(BleException.UnexpectedResponse)
if(characteristic == null){ val interval = characteristic.readWriteInterval().fold(
service.characteristics.forEach { onFailure = { return Result.failure(it) },
Log.d("ble", "characteristic ${it.uuid} ") onSuccess = { it }
} )
Log.d("ble", "${intervalReadUUID} not found")
return Result.failure(BleException.UnexpectedResponse)
}
/*characteristic.write(DataByteArray.from(2, 0, 0, 0))
characteristic.write(DataByteArray.from(3, 0, 0, 0)) val readTimeout = characteristic.read().value.let {
val interval = characteristic.read().value.let {
if(it.size == 4){ if(it.size == 4){
it.get4byteUIntAt(0).toLong() it.get4byteUIntAt(0).toLong()
}else{ } else {
0 0
} }
} }*/
return Result.success( return Result.success(
Ble.Host.HostState( 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( private suspend fun readAccelState(
address: String, address: String,
timer: BleInfo.HistoryTableStatus timer: BleInfo.HistoryTableStatus
@ -471,15 +426,10 @@ class BleRepositoryImpl @Inject constructor(
var characteristic = service.findCharacteristic(intervalReadUUID) var characteristic = service.findCharacteristic(intervalReadUUID)
?: return Result.failure(BleException.UnexpectedResponse) ?: return Result.failure(BleException.UnexpectedResponse)
characteristic.write(DataByteArray.from(3, 0, 0, 0, )) val interval = characteristic.readWriteInterval().fold(
onFailure = { return Result.failure(it) },
val interval = characteristic.read().value.let { onSuccess = { it }
if(it.size == 4){ )
it.get4byteUIntAt(0).toLong()
} else {
0
}
}
val historySettingsParams = when(timer){ val historySettingsParams = when(timer){
BleInfo.HistoryTableStatus.EMPTY, BleInfo.HistoryTableStatus.EMPTY,
@ -561,7 +511,7 @@ class BleRepositoryImpl @Inject constructor(
} }
override suspend fun addBleToHostTableBySerial(serial: String, ble: List<String>): Result<Int, BleException> { 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( 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( service.findCharacteristic(
flashWriteUUID flashWriteUUID
)!!.write( )!!.write(
@ -790,9 +754,9 @@ class BleRepositoryImpl @Inject constructor(
try { try {
val services = connection.discoverServices() 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()) Log.d("write", request.toString())

View File

@ -1,8 +1,12 @@
package llc.arma.ble.data.repository package llc.arma.ble.data.repository
import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Application import android.app.Application
import android.content.pm.PackageManager
import android.util.Log import android.util.Log
import androidx.annotation.RequiresPermission
import androidx.core.app.ActivityCompat
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay 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.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.client.main.service.ClientBleGattCharacteristic import no.nordicsemi.android.kotlin.ble.client.main.service.ClientBleGattCharacteristic
import okio.ByteString.Companion.decodeHex
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.util.BitSet import java.util.BitSet
import java.util.Locale import java.util.Locale
@SuppressLint("MissingPermission") @RequiresPermission(allOf = ["android.permission.BLUETOOTH_CONNECT"])
suspend fun readTable( suspend fun readTable(
characteristic: ClientBleGattCharacteristic, characteristic: ClientBleGattCharacteristic,
startRequest: ByteArray, startRequest: ByteArray,
@ -172,20 +175,34 @@ fun readHostHistory(
var periods = mutableListOf<Pair<Boolean, List<String>>>() var periods = mutableListOf<Pair<Boolean, List<String>>>()
var periodBle = mutableListOf<String>() var periodBle = mutableListOf<String>()
var hasHit = false
do { 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 bleTableOffset += 2
hasHit = true if(periodBle.isEmpty()) bleTableOffset += 2
periods.add(Pair(false, periodBle))
periodBle = mutableListOf()
continue 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]) val innerIndex = getInnerIndex(bleIdTableCell[1])
@ -204,6 +221,9 @@ fun readHostHistory(
val devDataSize = getDevDataSize(devTypeByte) val devDataSize = getDevDataSize(devTypeByte)
bleTableOffset += 2 bleTableOffset += 2
if(innerIndex == 0)
bleTableOffset += 2
if (devDataSize != 0) { if (devDataSize != 0) {
val payload = getBleIdIndex( val payload = getBleIdIndex(
firstTablePackage.drop(bleTableOffset).take(devDataSize) firstTablePackage.drop(bleTableOffset).take(devDataSize)
@ -214,24 +234,6 @@ fun readHostHistory(
periodBle.add(serial) 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) } while (bleTableOffset < firstTablePackage.size)
emit( emit(
@ -290,7 +292,7 @@ suspend fun readHostBleTable(
?.findCharacteristic(hostHistoryReadUUID) ?.findCharacteristic(hostHistoryReadUUID)
?: throw IllegalStateException() ?: throw IllegalStateException()
characteristic.write(DataByteArray.from(2)) characteristic.write(DataByteArray.from(7))
var value = characteristic.read().value var value = characteristic.read().value
@ -346,7 +348,7 @@ suspend fun readHostBleTable(
} }
@OptIn(ExperimentalStdlibApi::class) @OptIn(ExperimentalStdlibApi::class)
suspend fun editBleToHostTable( suspend fun editBleHostTable(
address: String, address: String,
addBleAddress: List<String>, addBleAddress: List<String>,
app: Application, app: Application,
@ -366,7 +368,9 @@ suspend fun editBleToHostTable(
characteristic.write(DataByteArray.from(12, 1)) 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() 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.edit
import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore import androidx.datastore.preferences.preferencesDataStore
import com.google.gson.Gson
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString

View File

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