Поправки по графикам

This commit is contained in:
Vineyro 2023-07-27 14:43:07 +07:00
parent d974df953d
commit 62205d27a0
21 changed files with 1440 additions and 153 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" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@ -1,8 +1,15 @@
package llc.arma.ble.app.ui
import android.Manifest
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.KeyEvent
import androidx.activity.ComponentActivity
import androidx.activity.compose.BackHandler
import androidx.activity.compose.setContent
@ -16,8 +23,6 @@ import androidx.compose.material.rememberModalBottomSheetState
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@ -34,6 +39,7 @@ import llc.arma.ble.app.ui.theme.BleTheme
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@OptIn(ExperimentalPermissionsApi::class, ExperimentalMaterialApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

View File

@ -38,7 +38,7 @@ class BottomDialogState @OptIn(ExperimentalMaterialApi::class) constructor(
content: @Composable () -> Unit
){
setContent(content)
if(sheetState?.currentValue != ModalBottomSheetValue.Expanded)
//if(sheetState?.currentValue != ModalBottomSheetValue.Expanded)
sheetState?.show()
}

View File

@ -4,20 +4,37 @@ import llc.arma.ble.app.ui.common.ViewEvent
import llc.arma.ble.app.ui.common.ViewSideEffect
import llc.arma.ble.app.ui.common.ViewState
import llc.arma.ble.app.ui.model.BleView
import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract
import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.usecase.AccelScale
import llc.arma.ble.domain.usecase.AccelViewMode
import llc.arma.ble.domain.usecase.FftAxis
import llc.arma.ble.domain.usecase.FftFrequency
import llc.arma.ble.domain.usecase.FftViewMode
class AccelerometerContract {
sealed class Event : ViewEvent {
object OnShowAccelerometerMeasure : Event()
object OnHideAccelerometerMeasure : Event()
object OnShowAccelerometerHistory : Event()
object OnShowAccelerometerSpectre : Event()
object OnHideAccelerometerSpectre : Event()
object OnHideAccelerometerHistory : Event()
data class OnAccelViewModeEdit(
val next: Next
) : Event(){
enum class Next {
ACCEL, SPECTRE
}
}
object OnSpectreEdit : Event()
object OnFftFrequencyEdit : Event()
object OnFftAxisEdit : Event()
object OnFftModeEdit : Event()
object OnPowerEdit : Event()
@ -35,6 +52,22 @@ class AccelerometerContract {
val tx: BleView.BleState.TX
) : Event()
data class OnAccelViewModelChanged(
val mode: AccelViewMode
) : Event()
data class OnFftFrequencyChanged(
val frequency: FftFrequency
) : Event()
data class OnFftAxisChanged(
val axis: FftAxis
) : Event()
data class OnFftModeChanged(
val mode: FftViewMode
) : Event()
}
sealed class State : ViewState {
@ -44,7 +77,12 @@ class AccelerometerContract {
data class Display(
val origin: Ble.Accelerometer,
val accelerometer: BleView.Accelerometer,
val writeState: WriteState?
val writeState: WriteState?,
val accelViewMode: AccelViewMode,
val accelScale: AccelScale,
val fftViewMode: FftViewMode,
val fftAxis: FftAxis,
val fftFrequency: FftFrequency
) : State() {
sealed class WriteState {
@ -70,17 +108,23 @@ class AccelerometerContract {
sealed class Effect : ViewSideEffect {
object ShowAccelerometerMeasure : Effect()
object ShowAccelerometerHistory : Effect()
object ShowPowerPicker : Effect()
object HidePowerPicker : Effect()
object ShowWriteBle : Effect()
object HideWriteBle : Effect()
data class ShowAccelEdit(
val next: Event.OnAccelViewModeEdit.Next
) : Effect()
object ShowSpectreEdit : Effect()
object ShowFftFrequencyEdit : Effect()
object ShowFftAxisEdit : Effect()
object ShowFftModeEdit : Effect()
}
}

View File

@ -15,6 +15,11 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import llc.arma.ble.app.ui.common.rememberBottomDialogState
import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.AccelFftAxisEdit
import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.AccelFftModeEdit
import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.AccelFrequencyEdit
import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.AccelSpectreEdit
import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.AccelViewEdit
import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.AccelerometerHistory
import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.AccelerometerMeasure
import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.DisplayState
@ -24,7 +29,7 @@ import llc.arma.ble.app.ui.screen.inspection.accelerometer.view.Write
import llc.arma.ble.domain.model.Ble
enum class SheetPage {
MEASURE, POWER, WRITE, HISTORY
MEASURE, POWER, WRITE, HISTORY, ACCEL_MODE_EDIT, SPECTRE_MODE_EDIT, SPECTRE_EDIT, FREQUENCY_EDIT, AXIS_EDIT, FFT_MODE_EDIT
}
@Composable
@ -54,7 +59,14 @@ fun AccelerometerScreen(
if (currentState is AccelerometerContract.State.Display) {
bottomDialog.show {
AccelerometerMeasure(ble = currentState.accelerometer.info)
AccelerometerMeasure(
ble = currentState.accelerometer.info,
accelMode = currentState.accelViewMode,
fftAxis = currentState.fftAxis,
fftMode = currentState.fftViewMode,
frequency = currentState.fftFrequency,
accelScale = currentState.accelScale
)
}
}
}
@ -63,7 +75,14 @@ fun AccelerometerScreen(
if (currentState is AccelerometerContract.State.Display) {
bottomDialog.show {
AccelerometerHistory(ble = currentState.accelerometer.info)
AccelerometerHistory(
ble = currentState.accelerometer.info,
accelMode = currentState.accelViewMode,
fftAxis = currentState.fftAxis,
fftMode = currentState.fftViewMode,
frequency = currentState.fftFrequency,
accelScale = currentState.accelScale
)
}
}
}
@ -100,6 +119,92 @@ fun AccelerometerScreen(
}
}
SheetPage.ACCEL_MODE_EDIT -> bottomDialog.show {
val currentState = viewModel.viewState.value
if(currentState is AccelerometerContract.State.Display) {
AccelViewEdit(
next = AccelerometerContract.Event.OnAccelViewModeEdit.Next.ACCEL,
state = currentState,
onEvent = {
viewModel.setEvent(it)
}
)
}
}
SheetPage.SPECTRE_MODE_EDIT -> bottomDialog.show {
val currentState = viewModel.viewState.value
if(currentState is AccelerometerContract.State.Display) {
AccelViewEdit(
next = AccelerometerContract.Event.OnAccelViewModeEdit.Next.SPECTRE,
state = currentState,
onEvent = {
viewModel.setEvent(it)
}
)
}
}
SheetPage.SPECTRE_EDIT -> bottomDialog.show {
val currentState = viewModel.viewState.value
if(currentState is AccelerometerContract.State.Display) {
AccelSpectreEdit(
state = currentState,
onEvent = {
viewModel.setEvent(it)
}
)
}
}
SheetPage.FREQUENCY_EDIT -> bottomDialog.show {
val currentState = viewModel.viewState.value
if(currentState is AccelerometerContract.State.Display) {
AccelFrequencyEdit(
state = currentState,
onEvent = {
viewModel.setEvent(it)
}
)
}
}
SheetPage.AXIS_EDIT -> bottomDialog.show {
val currentState = viewModel.viewState.value
if(currentState is AccelerometerContract.State.Display) {
AccelFftAxisEdit(
state = currentState,
onEvent = {
viewModel.setEvent(it)
}
)
}
}
SheetPage.FFT_MODE_EDIT -> bottomDialog.show {
val currentState = viewModel.viewState.value
if(currentState is AccelerometerContract.State.Display) {
AccelFftModeEdit(
state = currentState,
onEvent = {
viewModel.setEvent(it)
}
)
}
}
null -> {
bottomDialog.hide()
}
@ -117,7 +222,7 @@ fun AccelerometerScreen(
LaunchedEffect("effect"){
viewModel.effect.onEach {
when(it){
AccelerometerContract.Effect.ShowAccelerometerMeasure -> {
is AccelerometerContract.Effect.ShowAccelerometerMeasure -> launch {
sheetPage = null
delay(100)
sheetPage = SheetPage.MEASURE
@ -131,21 +236,48 @@ fun AccelerometerScreen(
delay(100)
sheetPage = SheetPage.POWER
}
is AccelerometerContract.Effect.HideWriteBle -> {
is AccelerometerContract.Effect.HideWriteBle -> launch {
sheetPage = null
delay(100)
}
is AccelerometerContract.Effect.ShowWriteBle -> {
is AccelerometerContract.Effect.ShowWriteBle -> launch {
sheetPage = null
delay(100)
sheetPage = SheetPage.WRITE
}
AccelerometerContract.Effect.ShowAccelerometerHistory -> {
is AccelerometerContract.Effect.ShowAccelerometerHistory -> launch {
sheetPage = null
delay(100)
sheetPage = SheetPage.HISTORY
}
is AccelerometerContract.Effect.ShowAccelEdit -> launch {
sheetPage = null
delay(100)
sheetPage = when(it.next){
AccelerometerContract.Event.OnAccelViewModeEdit.Next.ACCEL -> SheetPage.ACCEL_MODE_EDIT
AccelerometerContract.Event.OnAccelViewModeEdit.Next.SPECTRE -> SheetPage.SPECTRE_MODE_EDIT
}
}
is AccelerometerContract.Effect.ShowSpectreEdit -> launch {
sheetPage = null
delay(100)
sheetPage = SheetPage.SPECTRE_EDIT
}
is AccelerometerContract.Effect.ShowFftFrequencyEdit -> launch {
sheetPage = null
delay(100)
sheetPage = SheetPage.FREQUENCY_EDIT
}
is AccelerometerContract.Effect.ShowFftAxisEdit -> launch {
sheetPage = null
delay(100)
sheetPage = SheetPage.AXIS_EDIT
}
is AccelerometerContract.Effect.ShowFftModeEdit -> launch {
sheetPage = null
delay(100)
sheetPage = SheetPage.FFT_MODE_EDIT
}
}
}.launchIn(this)
}

View File

@ -1,14 +1,20 @@
package llc.arma.ble.app.ui.screen.inspection.accelerometer
import android.util.Log
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import llc.arma.ble.app.ui.common.BaseViewModel
import llc.arma.ble.app.ui.mapper.BleMapper
import llc.arma.ble.app.ui.mapper.BleViewMapper
import llc.arma.ble.app.ui.model.BleView
import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract
import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.usecase.AccelScale
import llc.arma.ble.domain.usecase.AccelViewMode
import llc.arma.ble.domain.usecase.FftAxis
import llc.arma.ble.domain.usecase.FftFrequency
import llc.arma.ble.domain.usecase.FftViewMode
import llc.arma.ble.domain.usecase.WriteBle
import javax.inject.Inject
@ -31,14 +37,118 @@ class AccelerometerViewModel @Inject constructor(
is AccelerometerContract.Event.OnShowWriteBlePreview -> reduce(viewState.value, event)
is AccelerometerContract.Event.OnHideWriteBlePreview -> reduce(viewState.value, event)
is AccelerometerContract.Event.OnWriteBle -> reduce(viewState.value, event)
is AccelerometerContract.Event.OnHideAccelerometerHistory -> reduce(viewState.value, event)
is AccelerometerContract.Event.OnShowAccelerometerHistory -> reduce(viewState.value, event)
is AccelerometerContract.Event.OnHideAccelerometerSpectre -> reduce(viewState.value, event)
is AccelerometerContract.Event.OnShowAccelerometerSpectre -> reduce(viewState.value, event)
is AccelerometerContract.Event.OnAccelViewModeEdit -> reduce(viewState.value, event)
is AccelerometerContract.Event.OnAccelViewModelChanged -> reduce(viewState.value, event)
is AccelerometerContract.Event.OnSpectreEdit -> reduce(viewState.value, event)
is AccelerometerContract.Event.OnFftFrequencyEdit -> reduce(viewState.value, event)
is AccelerometerContract.Event.OnFftAxisChanged -> reduce(viewState.value, event)
is AccelerometerContract.Event.OnFftFrequencyChanged -> reduce(viewState.value, event)
is AccelerometerContract.Event.OnFftModeChanged -> reduce(viewState.value, event)
is AccelerometerContract.Event.OnFftAxisEdit -> reduce(viewState.value, event)
is AccelerometerContract.Event.OnFftModeEdit -> reduce(viewState.value, event)
}
}
private fun reduce(
state: AccelerometerContract.State,
event: AccelerometerContract.Event.OnShowAccelerometerHistory
event: AccelerometerContract.Event.OnFftModeEdit
) {
setEffect {
AccelerometerContract.Effect.ShowFftModeEdit
}
}
private fun reduce(
state: AccelerometerContract.State,
event: AccelerometerContract.Event.OnFftAxisEdit
) {
setEffect {
AccelerometerContract.Effect.ShowFftAxisEdit
}
}
private fun reduce(
state: AccelerometerContract.State,
event: AccelerometerContract.Event.OnFftAxisChanged
) {
if(state is AccelerometerContract.State.Display){
setState {
state.copy(
fftAxis = event.axis
)
}
}
}
private fun reduce(
state: AccelerometerContract.State,
event: AccelerometerContract.Event.OnFftModeChanged
) {
if(state is AccelerometerContract.State.Display){
setState {
state.copy(
fftViewMode = event.mode
)
}
}
}
private fun reduce(
state: AccelerometerContract.State,
event: AccelerometerContract.Event.OnFftFrequencyChanged
) {
if(state is AccelerometerContract.State.Display){
setState {
state.copy(
fftFrequency = event.frequency
)
}
}
}
private fun reduce(
state: AccelerometerContract.State,
event: AccelerometerContract.Event.OnFftFrequencyEdit
) {
setEffect {
AccelerometerContract.Effect.ShowFftFrequencyEdit
}
}
private fun reduce(
state: AccelerometerContract.State,
event: AccelerometerContract.Event.OnSpectreEdit
) {
setEffect {
AccelerometerContract.Effect.ShowSpectreEdit
}
}
private fun reduce(
state: AccelerometerContract.State,
event: AccelerometerContract.Event.OnShowAccelerometerSpectre
) {
setEffect {
@ -49,7 +159,7 @@ class AccelerometerViewModel @Inject constructor(
private fun reduce(
state: AccelerometerContract.State,
event: AccelerometerContract.Event.OnHideAccelerometerHistory
event: AccelerometerContract.Event.OnHideAccelerometerSpectre
) {
@ -123,6 +233,27 @@ class AccelerometerViewModel @Inject constructor(
}
private fun reduce(
state: AccelerometerContract.State,
event: AccelerometerContract.Event.OnAccelViewModelChanged
) {
if(state is AccelerometerContract.State.Display) {
setState {
state.copy(
accelViewMode = event.mode
)
}
}
setEffect {
AccelerometerContract.Effect.HidePowerPicker
}
}
private fun reduce(
state: AccelerometerContract.State,
event: AccelerometerContract.Event.OnPowerEdit
@ -132,6 +263,15 @@ class AccelerometerViewModel @Inject constructor(
}
}
private fun reduce(
state: AccelerometerContract.State,
event: AccelerometerContract.Event.OnAccelViewModeEdit
) {
setEffect {
AccelerometerContract.Effect.ShowAccelEdit(event.next)
}
}
private fun reduce(
state: AccelerometerContract.State,
event: AccelerometerContract.Event.OnHideAccelerometerMeasure
@ -146,10 +286,16 @@ class AccelerometerViewModel @Inject constructor(
event: AccelerometerContract.Event.OnShowAccelerometerMeasure
) {
setEffect {
AccelerometerContract.Effect.ShowAccelerometerMeasure
viewModelScope.launch {
setEffect {
AccelerometerContract.Effect.ShowAccelerometerMeasure
}
}
}
private fun reduce(
@ -171,7 +317,12 @@ class AccelerometerViewModel @Inject constructor(
AccelerometerContract.State.Display(
origin = event.ble,
accelerometer = bleMapper.map(event.ble) as BleView.Accelerometer,
writeState = null
writeState = null,
accelViewMode = AccelViewMode.ACCELERATION,
fftAxis = FftAxis.AUTO,
fftFrequency = FftFrequency.F_400,
fftViewMode = FftViewMode.SPECTRE,
accelScale = AccelScale.S_2
)
}
}

View File

@ -0,0 +1,100 @@
package llc.arma.ble.app.ui.screen.inspection.accelerometer.view
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.KeyboardArrowDown
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract
import llc.arma.ble.domain.usecase.AccelViewMode
import llc.arma.ble.domain.usecase.FftAxis
import llc.arma.ble.domain.usecase.FftFrequency
import llc.arma.ble.domain.usecase.FftViewMode
@Composable
fun AccelFftAxisEdit(
state: AccelerometerContract.State.Display,
onEvent: (AccelerometerContract.Event) -> Unit,
){
var fftAxis = state.fftAxis
Column(
modifier = Modifier
) {
Text(
modifier = Modifier.padding(horizontal = 12.dp),
text = "Fft axis",
style = MaterialTheme.typography.titleLarge
)
Spacer(modifier = Modifier.height(16.dp))
FftAxis.values().forEach {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(8.dp))
.clickable { onEvent(AccelerometerContract.Event.OnFftAxisChanged(it)) }
.padding(4.dp)
) {
RadioButton(
selected = it == fftAxis,
onClick = {
onEvent(AccelerometerContract.Event.OnFftAxisChanged(it))
}
)
Text(text = it.localized)
}
}
Spacer(modifier = Modifier.height(16.dp))
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.height(50.dp),
shape = CircleShape,
color = MaterialTheme.colorScheme.primaryContainer,
onClick = {
onEvent(AccelerometerContract.Event.OnSpectreEdit)
}
) {
Box(modifier = Modifier.fillMaxSize()) {
Text(
modifier = Modifier.align(Alignment.Center),
color = MaterialTheme.colorScheme.background,
style = MaterialTheme.typography.labelLarge,
text = "Ок"
)
}
}
}
}

View File

@ -0,0 +1,100 @@
package llc.arma.ble.app.ui.screen.inspection.accelerometer.view
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.KeyboardArrowDown
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract
import llc.arma.ble.domain.usecase.AccelViewMode
import llc.arma.ble.domain.usecase.FftAxis
import llc.arma.ble.domain.usecase.FftFrequency
import llc.arma.ble.domain.usecase.FftViewMode
@Composable
fun AccelFftModeEdit(
state: AccelerometerContract.State.Display,
onEvent: (AccelerometerContract.Event) -> Unit,
){
var fftMode = state.fftViewMode
Column(
modifier = Modifier
) {
Text(
modifier = Modifier.padding(horizontal = 12.dp),
text = "Fft view mode",
style = MaterialTheme.typography.titleLarge
)
Spacer(modifier = Modifier.height(16.dp))
FftViewMode.values().forEach {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(8.dp))
.clickable { onEvent(AccelerometerContract.Event.OnFftModeChanged(it)) }
.padding(4.dp)
) {
RadioButton(
selected = it == fftMode,
onClick = {
onEvent(AccelerometerContract.Event.OnFftModeChanged(it))
}
)
Text(text = it.localized)
}
}
Spacer(modifier = Modifier.height(16.dp))
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.height(50.dp),
shape = CircleShape,
color = MaterialTheme.colorScheme.primaryContainer,
onClick = {
onEvent(AccelerometerContract.Event.OnSpectreEdit)
}
) {
Box(modifier = Modifier.fillMaxSize()) {
Text(
modifier = Modifier.align(Alignment.Center),
color = MaterialTheme.colorScheme.background,
style = MaterialTheme.typography.labelLarge,
text = "Ок"
)
}
}
}
}

View File

@ -0,0 +1,110 @@
package llc.arma.ble.app.ui.screen.inspection.accelerometer.view
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.KeyboardArrowDown
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract
import llc.arma.ble.domain.usecase.AccelViewMode
import llc.arma.ble.domain.usecase.FftAxis
import llc.arma.ble.domain.usecase.FftFrequency
import llc.arma.ble.domain.usecase.FftViewMode
/*val AccelViewMode.localized: String
get() {
return when(this){
ACCELERATION -> "Ускорение"
PEAK_ACCELERATION -> "Пиковое ускорение"
RMS -> "Среднеквадратичное ускорение"
ANGLE -> "Угол"
}
}*/
@Composable
fun AccelFrequencyEdit(
state: AccelerometerContract.State.Display,
onEvent: (AccelerometerContract.Event) -> Unit,
){
var fftFrequency = state.fftFrequency
Column(
modifier = Modifier
) {
Text(
modifier = Modifier.padding(horizontal = 12.dp),
text = "Fft frequency",
style = MaterialTheme.typography.titleLarge
)
Spacer(modifier = Modifier.height(16.dp))
FftFrequency.values().forEach {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(8.dp))
.clickable { onEvent(AccelerometerContract.Event.OnFftFrequencyChanged(it)) }
.padding(4.dp)
) {
RadioButton(
selected = it == fftFrequency,
onClick = {
onEvent(AccelerometerContract.Event.OnFftFrequencyChanged(it))
}
)
Text(text = it.localized)
}
}
Spacer(modifier = Modifier.height(16.dp))
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.height(50.dp),
shape = CircleShape,
color = MaterialTheme.colorScheme.primaryContainer,
onClick = {
onEvent(AccelerometerContract.Event.OnSpectreEdit)
}
) {
Box(modifier = Modifier.fillMaxSize()) {
Text(
modifier = Modifier.align(Alignment.Center),
color = MaterialTheme.colorScheme.background,
style = MaterialTheme.typography.labelLarge,
text = "Ок"
)
}
}
}
}

View File

@ -0,0 +1,282 @@
package llc.arma.ble.app.ui.screen.inspection.accelerometer.view
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.KeyboardArrowDown
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract
import llc.arma.ble.domain.usecase.FftAxis
import llc.arma.ble.domain.usecase.FftFrequency
import llc.arma.ble.domain.usecase.FftViewMode
val FftFrequency.localized: String
get() {
return when(this){
FftFrequency.OFF -> "откл"
FftFrequency.F_1 -> "1 Гц"
FftFrequency.F_10 -> "10 Гц"
FftFrequency.F_25 -> "25 Гц"
FftFrequency.F_50 -> "50 Гц"
FftFrequency.F_100 -> "100 Гц"
FftFrequency.F_200 -> "200 Гц"
FftFrequency.F_400 -> "400 Гц"
FftFrequency.F_1620 -> "1620 Гц"
FftFrequency.F_1344 -> "1344 Гц"
}
}
val FftAxis.localized: String
get() {
return when(this){
FftAxis.AUTO -> "Авто"
FftAxis.X -> "Ось X"
FftAxis.Y -> "Ось Y"
FftAxis.Z -> "Ось Z"
}
}
val FftViewMode.localized: String
get() {
return when(this){
FftViewMode.SPECTRE -> "Спектр"
FftViewMode.X -> "Ось X"
FftViewMode.Y -> "Ось Y"
FftViewMode.Z -> "Ось Z"
}
}
@Composable
fun AccelSpectreEdit(
state: AccelerometerContract.State.Display,
onEvent: (AccelerometerContract.Event) -> Unit,
){
val accelMode = state.accelViewMode
val fftMode = state.fftViewMode
val fftAxis = state.fftAxis
val fftFrequency = state.fftFrequency
Column(
modifier = Modifier
) {
Text(
modifier = Modifier.padding(horizontal = 12.dp),
text = "Спектр",
style = MaterialTheme.typography.titleLarge
)
Spacer(modifier = Modifier.height(16.dp))
Column(
modifier = Modifier
) {
Box(
modifier = Modifier.padding(
vertical = 8.dp,
horizontal = 8.dp
)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(RoundedCornerShape(16.dp))
.clickable {
onEvent(AccelerometerContract.Event.OnAccelViewModeEdit(next = AccelerometerContract.Event.OnAccelViewModeEdit.Next.SPECTRE))
}
.padding(8.dp)
) {
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = "Accel view mode"
)
Text(
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.bodyMedium,
text = accelMode.localized
)
}
Icon(
imageVector = Icons.Rounded.KeyboardArrowDown,
contentDescription = null
)
}
}
Box(
modifier = Modifier.padding(
vertical = 8.dp,
horizontal = 8.dp
)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(RoundedCornerShape(16.dp))
.clickable {
onEvent(AccelerometerContract.Event.OnFftModeEdit)
}
.padding(8.dp)
) {
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = "Fft view mode"
)
Text(
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.bodyMedium,
text = fftMode.localized
)
}
Icon(
imageVector = Icons.Rounded.KeyboardArrowDown,
contentDescription = null
)
}
}
Box(
modifier = Modifier.padding(
vertical = 8.dp,
horizontal = 8.dp
)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(RoundedCornerShape(16.dp))
.clickable { onEvent(AccelerometerContract.Event.OnFftAxisEdit) }
.padding(8.dp)
) {
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = "Fft axis"
)
Text(
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.bodyMedium,
text = fftAxis.localized
)
}
Icon(
imageVector = Icons.Rounded.KeyboardArrowDown,
contentDescription = null
)
}
}
Box(
modifier = Modifier.padding(
vertical = 8.dp,
horizontal = 8.dp
)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(RoundedCornerShape(16.dp))
.clickable {
onEvent(AccelerometerContract.Event.OnFftFrequencyEdit)
}
.padding(8.dp)
) {
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = "Fft frequency"
)
Text(
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.bodyMedium,
text = fftFrequency.localized
)
}
Icon(
imageVector = Icons.Rounded.KeyboardArrowDown,
contentDescription = null
)
}
}
}
Spacer(modifier = Modifier.height(16.dp))
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.height(50.dp),
shape = CircleShape,
color = MaterialTheme.colorScheme.primaryContainer,
onClick = {
onEvent(AccelerometerContract.Event.OnShowAccelerometerSpectre)
}
) {
Box(modifier = Modifier.fillMaxSize()) {
Text(
modifier = Modifier.align(Alignment.Center),
color = MaterialTheme.colorScheme.background,
style = MaterialTheme.typography.labelLarge,
text = "Продолжить"
)
}
}
}
}

View File

@ -0,0 +1,118 @@
package llc.arma.ble.app.ui.screen.inspection.accelerometer.view
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import llc.arma.ble.app.ui.model.BleView
import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract
import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract
import llc.arma.ble.domain.usecase.AccelViewMode
import llc.arma.ble.domain.usecase.AccelViewMode.*
val AccelViewMode.localized: String
get() {
return when(this){
ACCELERATION -> "Ускорение"
PEAK_ACCELERATION -> "Пиковое ускорение"
RMS -> "Среднеквадратичное ускорение"
ANGLE -> "Угол"
}
}
@Composable
fun AccelViewEdit(
next: AccelerometerContract.Event.OnAccelViewModeEdit.Next,
state: AccelerometerContract.State.Display,
onEvent: (AccelerometerContract.Event) -> Unit,
){
var value by remember(state.accelViewMode) {
mutableStateOf(state.accelViewMode)
}
Column(
modifier = Modifier
) {
Text(
modifier = Modifier.padding(horizontal = 12.dp),
text = "Accel view mode",
style = MaterialTheme.typography.titleLarge
)
Spacer(modifier = Modifier.height(16.dp))
values().forEach {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(8.dp))
.clickable { value = it }
.padding(4.dp)
) {
RadioButton(
selected = it == value,
onClick = {
value = it
}
)
Text(text = it.localized)
}
}
Spacer(modifier = Modifier.height(16.dp))
Surface(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.height(50.dp),
shape = CircleShape,
color = MaterialTheme.colorScheme.primaryContainer,
onClick = {
onEvent(AccelerometerContract.Event.OnAccelViewModelChanged(value))
when(next){
AccelerometerContract.Event.OnAccelViewModeEdit.Next.ACCEL -> {
onEvent(AccelerometerContract.Event.OnShowAccelerometerMeasure)
}
AccelerometerContract.Event.OnAccelViewModeEdit.Next.SPECTRE -> {
onEvent(AccelerometerContract.Event.OnSpectreEdit)
}
}
}
) {
Box(modifier = Modifier.fillMaxSize()) {
Text(
modifier = Modifier.align(Alignment.Center),
color = MaterialTheme.colorScheme.background,
style = MaterialTheme.typography.labelLarge,
text = "Продолжить"
)
}
}
}
}

View File

@ -1,10 +1,9 @@
package llc.arma.ble.app.ui.screen.inspection.accelerometer.view
import android.util.Log
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.gestures.detectTransformGestures
import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
@ -16,7 +15,6 @@ import androidx.lifecycle.viewModelScope
import com.patrykandpatrick.vico.compose.axis.horizontal.bottomAxis
import com.patrykandpatrick.vico.compose.axis.vertical.startAxis
import com.patrykandpatrick.vico.compose.chart.Chart
import com.patrykandpatrick.vico.compose.chart.line.lineChart
import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
@ -28,22 +26,23 @@ import llc.arma.ble.domain.model.BleInfo
import javax.inject.Inject
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Refresh
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.style.TextAlign
import com.patrykandpatrick.vico.compose.chart.column.columnChart
import com.patrykandpatrick.vico.compose.chart.scroll.rememberChartScrollState
import com.patrykandpatrick.vico.core.axis.AxisPosition
import com.patrykandpatrick.vico.core.axis.formatter.AxisValueFormatter
import com.patrykandpatrick.vico.core.component.shape.LineComponent
import com.patrykandpatrick.vico.core.entry.ChartEntry
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import llc.arma.ble.domain.common.ProgressState
import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.usecase.GetAccelerometerHistoryBySerial
import java.util.*
import llc.arma.ble.domain.usecase.AccelScale
import llc.arma.ble.domain.usecase.AccelViewMode
import llc.arma.ble.domain.usecase.FftAxis
import llc.arma.ble.domain.usecase.FftFrequency
import llc.arma.ble.domain.usecase.FftViewMode
import llc.arma.ble.domain.usecase.GetAccelerometerSpectreBySerial
class AccelerometerEntry(
val frequency: Long,
@ -57,14 +56,19 @@ class AccelerometerEntry(
@Composable
fun AccelerometerHistory(
ble: BleInfo
ble: BleInfo,
accelMode: AccelViewMode,
fftAxis: FftAxis,
fftMode: FftViewMode,
frequency: FftFrequency,
accelScale: AccelScale
) {
val viewModel = hiltViewModel<AccelerometerHistoryViewModel>()
val state = viewModel.viewState.value
LaunchedEffect(ble.serial) {
viewModel.setEvent(AccelerometerHistoryContract.Event.OnStart(ble.serial))
LaunchedEffect(ble.serial, accelMode, fftAxis, fftMode, frequency) {
viewModel.setEvent(AccelerometerHistoryContract.Event.OnStart(ble.serial, accelMode, fftAxis, fftMode, frequency, accelScale))
}
Column(
@ -79,12 +83,11 @@ fun AccelerometerHistory(
val title = when(state){
is AccelerometerHistoryContract.State.Display -> {
when (state.loadingHistoryState) {
is ProgressState.Finished -> "История измерений (${state.loadingHistoryState.data.size})"
is ProgressState.Indeterminate -> "История измерений"
is ProgressState.Progress -> "История измерений"
is ProgressState.Finished -> "${fftMode.localized} (${state.loadingHistoryState.data.size})"
else -> fftMode.localized
}
}
AccelerometerHistoryContract.State.Exception -> "История измерений"
AccelerometerHistoryContract.State.Exception -> fftMode.localized
}
Text(
@ -95,7 +98,7 @@ fun AccelerometerHistory(
IconButton(
onClick = {
viewModel.setEvent(AccelerometerHistoryContract.Event.OnRefreshHistory(ble.serial))
viewModel.setEvent(AccelerometerHistoryContract.Event.OnRefreshHistory(ble.serial, accelMode, fftAxis, fftMode, frequency, accelScale))
},
enabled = when(state){
is AccelerometerHistoryContract.State.Display -> state.loadingHistoryState is ProgressState.Finished
@ -161,7 +164,7 @@ fun Display(
AxisValueFormatter<AxisPosition.Horizontal.Bottom> { value, chartValues ->
(chartValues.chartEntryModel.entries.firstOrNull()
?.getOrNull(value.toInt()) as? AccelerometerEntry)
?.frequency?.let { it.toFloat() / 16f }?.toString()
?.frequency?.let { String.format("%.1f", (it.toFloat() / 256f)) }
.orEmpty()
}
@ -261,11 +264,21 @@ class AccelerometerHistoryContract {
sealed class Event : ViewEvent {
data class OnStart(
val serial: String
val serial: String,
val accelMode: AccelViewMode,
val fftAxis: FftAxis,
val fftMode: FftViewMode,
val frequency: FftFrequency,
val accelScale: AccelScale
) : Event()
data class OnRefreshHistory(
val serial: String
val serial: String,
val accelMode: AccelViewMode,
val fftAxis: FftAxis,
val fftMode: FftViewMode,
val frequency: FftFrequency,
val accelScale: AccelScale
) : Event()
}
@ -290,7 +303,7 @@ class AccelerometerHistoryContract {
@HiltViewModel
class AccelerometerHistoryViewModel @Inject constructor(
private val getAccelerometerHistoryBySerial: GetAccelerometerHistoryBySerial
private val getAccelerometerSpectreBySerial: GetAccelerometerSpectreBySerial
) : BaseViewModel<AccelerometerHistoryContract.State, AccelerometerHistoryContract.Event, AccelerometerHistoryContract.Effect>() {
private var lastSerial: String? = null
@ -314,7 +327,7 @@ class AccelerometerHistoryViewModel @Inject constructor(
if(state is AccelerometerHistoryContract.State.Display) {
if(lastSerial != event.serial) {
//if(lastSerial != event.serial) {
lastSerial = event.serial
@ -322,7 +335,14 @@ class AccelerometerHistoryViewModel @Inject constructor(
AccelerometerHistoryContract.State.Display(ProgressState.Indeterminate)
}
getAccelerometerHistoryBySerial(event.serial).onEach {
getAccelerometerSpectreBySerial(
serial = event.serial,
accelMode = event.accelMode,
fftAxis = event.fftAxis,
fftMode = event.fftMode,
frequency = event.frequency,
accelScale = event.accelScale
).onEach {
it.fold(
onSuccess = {
setState {
@ -337,7 +357,7 @@ class AccelerometerHistoryViewModel @Inject constructor(
)
}.launchIn(this)
}
//}
}
@ -355,7 +375,14 @@ class AccelerometerHistoryViewModel @Inject constructor(
AccelerometerHistoryContract.State.Display(ProgressState.Indeterminate)
}
getAccelerometerHistoryBySerial(event.serial).onEach {
getAccelerometerSpectreBySerial(
serial = event.serial,
accelMode = event.accelMode,
fftAxis = event.fftAxis,
fftMode = event.fftMode,
frequency = event.frequency,
accelScale = event.accelScale
).onEach {
it.fold(
onSuccess = {
setState {

View File

@ -1,6 +1,5 @@
package llc.arma.ble.app.ui.screen.inspection.accelerometer.view
import android.util.Log
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
@ -26,7 +25,6 @@ import androidx.compose.material.icons.rounded.Refresh
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.text.style.TextAlign
import com.patrykandpatrick.vico.compose.axis.horizontal.bottomAxis
import com.patrykandpatrick.vico.compose.chart.column.columnChart
import com.patrykandpatrick.vico.compose.chart.line.lineChart
import com.patrykandpatrick.vico.compose.chart.scroll.rememberChartScrollSpec
import com.patrykandpatrick.vico.core.chart.decoration.ThresholdLine
@ -37,12 +35,22 @@ import com.patrykandpatrick.vico.core.scroll.InitialScroll
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import llc.arma.ble.domain.usecase.Accelereate
import llc.arma.ble.domain.usecase.AccelScale
import llc.arma.ble.domain.usecase.AccelViewMode
import llc.arma.ble.domain.usecase.Accelerate
import llc.arma.ble.domain.usecase.FftAxis
import llc.arma.ble.domain.usecase.FftFrequency
import llc.arma.ble.domain.usecase.FftViewMode
import llc.arma.ble.domain.usecase.GetAccelerometerMeasureBySerialFlow
@Composable
fun AccelerometerMeasure(
ble: BleInfo
ble: BleInfo,
accelScale: AccelScale,
accelMode: AccelViewMode,
fftAxis: FftAxis,
fftMode: FftViewMode,
frequency: FftFrequency
) {
val viewModel = hiltViewModel<AccelerometerMeasureViewModel>()
@ -52,7 +60,7 @@ fun AccelerometerMeasure(
viewModel.setEvent(AccelerometerMeasureContract.Event.OnStart(ble.serial))
}*/
viewModel.setEvent(AccelerometerMeasureContract.Event.OnStart(ble.serial))
viewModel.setEvent(AccelerometerMeasureContract.Event.OnStart(ble.serial, accelScale, accelMode, fftAxis, fftMode, frequency))
DisposableEffect(key1 = ble, effect = {
@ -71,22 +79,15 @@ fun AccelerometerMeasure(
verticalAlignment = Alignment.CenterVertically
) {
val title = when(state){
is AccelerometerMeasureContract.State.Display -> {
"История измерений"
}
AccelerometerMeasureContract.State.Exception -> "История измерений"
}
Text(
modifier = Modifier.weight(1f),
text = title,
text = accelMode.localized,
style = MaterialTheme.typography.titleLarge
)
IconButton(
onClick = {
viewModel.setEvent(AccelerometerMeasureContract.Event.OnRefreshHistory(ble.serial))
viewModel.setEvent(AccelerometerMeasureContract.Event.OnRefreshHistory(ble.serial, accelScale, accelMode, fftAxis, fftMode, frequency))
},
enabled = true
) {
@ -147,7 +148,7 @@ fun Display(
}
xProducer.setEntries(state.measureHistory.mapIndexed { index, measurePoint ->
FloatEntry(index.toFloat(), measurePoint.x)
FloatEntry(index.toFloat(), measurePoint.x )
})
yProducer.setEntries(state.measureHistory.mapIndexed { index, measurePoint ->
@ -259,11 +260,21 @@ class AccelerometerMeasureContract {
object StopMeasure : Event()
data class OnStart(
val serial: String
val serial: String,
val accelScale: AccelScale,
val accelMode: AccelViewMode,
val fftAxis: FftAxis,
val fftMode: FftViewMode,
val frequency: FftFrequency
) : Event()
data class OnRefreshHistory(
val serial: String
val serial: String,
val accelScale: AccelScale,
val accelMode: AccelViewMode,
val fftAxis: FftAxis,
val fftMode: FftViewMode,
val frequency: FftFrequency
) : Event()
}
@ -271,7 +282,7 @@ class AccelerometerMeasureContract {
sealed class State : ViewState {
data class Display(
val measureHistory : List<Accelereate>
val measureHistory : List<Accelerate>
) : State()
object Exception : State()
@ -326,7 +337,7 @@ class AccelerometerMeasureViewModel @Inject constructor(
event: AccelerometerMeasureContract.Event.OnStart
) {
startReadMeasure(event.serial, false)
startReadMeasure(event.serial, event.accelScale, event.accelMode, event.fftAxis, event.fftMode, event.frequency, false)
}
@ -334,10 +345,18 @@ class AccelerometerMeasureViewModel @Inject constructor(
state: AccelerometerMeasureContract.State,
event: AccelerometerMeasureContract.Event.OnRefreshHistory
) {
startReadMeasure(event.serial, true)
startReadMeasure(event.serial, event.accelScale, event.accelMode, event.fftAxis, event.fftMode, event.frequency, true)
}
private fun startReadMeasure(serial: String, restartJob: Boolean){
private fun startReadMeasure(
serial: String,
accelScale: AccelScale,
accelMode: AccelViewMode,
fftAxis: FftAxis,
fftMode: FftViewMode,
frequency: FftFrequency,
restartJob: Boolean
){
if(restartJob || measureJob == null) {
measureJob?.cancel()
@ -348,14 +367,20 @@ class AccelerometerMeasureViewModel @Inject constructor(
AccelerometerMeasureContract.State.Display(emptyList())
}
getAccelerometerMeasureBySerialFlow(serial).onEach {
getAccelerometerMeasureBySerialFlow(serial, accelScale, accelMode, fftAxis, fftMode, frequency).onEach {
it.fold(
onSuccess = {
setState {
when (this) {
is AccelerometerMeasureContract.State.Display -> {
val dataList = this.measureHistory.toMutableList().apply {
add(it)
add(
Accelerate(
x = ((9806.65f / (Short.MAX_VALUE / 2)) * it.x) * (accelScale.k / 2),
y = ((9806.65f / (Short.MAX_VALUE / 2)) * it.y) * (accelScale.k / 2),
z = ((9806.65f / (Short.MAX_VALUE / 2)) * it.z) * (accelScale.k / 2)
)
)
}
AccelerometerMeasureContract.State.Display(dataList)
}

View File

@ -9,7 +9,6 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.KeyboardArrowDown
import androidx.compose.material.icons.rounded.KeyboardArrowRight
import androidx.compose.material.icons.rounded.Refresh
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@ -19,7 +18,6 @@ import androidx.compose.ui.unit.dp
import llc.arma.ble.app.ui.model.BleView
import llc.arma.ble.app.ui.screen.BleInfoView
import llc.arma.ble.app.ui.screen.inspection.accelerometer.AccelerometerContract
import llc.arma.ble.app.ui.screen.inspection.thermometer.ThermometerContract
import llc.arma.ble.domain.model.Ble
@Composable
@ -102,9 +100,7 @@ fun DisplayState(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(RoundedCornerShape(16.dp))
.clickable {
onEvent(AccelerometerContract.Event.OnShowAccelerometerMeasure)
}
.clickable { onEvent(AccelerometerContract.Event.OnAccelViewModeEdit(next = AccelerometerContract.Event.OnAccelViewModeEdit.Next.ACCEL)) }
.padding(8.dp)
) {
@ -113,7 +109,7 @@ fun DisplayState(
) {
Text(
text = "График"
text = "Ускорение"
)
}
@ -139,7 +135,7 @@ fun DisplayState(
modifier = Modifier
.clip(RoundedCornerShape(16.dp))
.clickable {
onEvent(AccelerometerContract.Event.OnShowAccelerometerHistory)
onEvent(AccelerometerContract.Event.OnSpectreEdit)
}
.padding(8.dp)
) {
@ -149,7 +145,7 @@ fun DisplayState(
) {
Text(
text = "История"
text = "Спектр"
)
}

View File

@ -11,7 +11,6 @@ import android.os.Build
import android.os.SystemClock
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.util.toRange
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@ -24,12 +23,73 @@ import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.model.BleInfo
import llc.arma.ble.domain.model.ConnectedBleInfo
import llc.arma.ble.domain.repository.BleRepository
import llc.arma.ble.domain.usecase.Accelereate
import llc.arma.ble.domain.usecase.AccelScale
import llc.arma.ble.domain.usecase.AccelViewMode
import llc.arma.ble.domain.usecase.Accelerate
import llc.arma.ble.domain.usecase.FftAxis
import llc.arma.ble.domain.usecase.FftFrequency
import llc.arma.ble.domain.usecase.FftViewMode
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.coroutines.resume
import kotlin.random.Random
val FftFrequency.sendData: Byte
get() {
return when(this){
FftFrequency.OFF -> 0
FftFrequency.F_1 -> 1
FftFrequency.F_10 -> 2
FftFrequency.F_25 -> 3
FftFrequency.F_50 -> 4
FftFrequency.F_100 -> 5
FftFrequency.F_200 -> 6
FftFrequency.F_400 -> 7
FftFrequency.F_1620 -> 8
FftFrequency.F_1344 -> 9
}
}
val FftAxis.sendData: Byte
get() {
return when(this){
FftAxis.AUTO -> 0
FftAxis.X -> 1
FftAxis.Y -> 2
FftAxis.Z -> 3
}
}
val FftViewMode.sendData: Byte
get() {
return when(this){
FftViewMode.SPECTRE -> 0
FftViewMode.X -> 1
FftViewMode.Y -> 2
FftViewMode.Z -> 3
}
}
val AccelViewMode.sendData: Byte
get() {
return when(this){
AccelViewMode.ACCELERATION -> 0
AccelViewMode.PEAK_ACCELERATION -> 1
AccelViewMode.RMS -> 2
AccelViewMode.ANGLE -> 3
}
}
val AccelScale.sendData: Byte
get() {
return when(this){
AccelScale.S_2 -> 0
AccelScale.S_4 -> 1
AccelScale.S_8 -> 2
AccelScale.S_16 -> 3
}
}
val serviceUUID: UUID = UUID.fromString("a77db03a-9bc4-11ed-a8fc-0242ac120002")
@ -502,8 +562,13 @@ class BleRepositoryImpl @Inject constructor(
}
override suspend fun getAccelerometerHistoryBySerial(
serial: String
override suspend fun getAccelerometerSpectreBySerial(
serial: String,
accelScale: AccelScale,
accelMode: AccelViewMode,
fftAxis: FftAxis,
fftMode: FftViewMode,
frequency: FftFrequency
): Flow<Result<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, BleException>> {
var gatt: BluetoothGatt? = null
@ -518,7 +583,15 @@ class BleRepositoryImpl @Inject constructor(
device = it,
serviceId = serviceUUID,
characteristicId = accelerometerReadUUID,
writeData = byteArrayOf(4, 1)
writeData = byteArrayOf(
4,
accelMode.sendData,
accelScale.sendData,
fftMode.sendData,
fftAxis.sendData,
frequency.sendData,
1
)
).onFailure {
CoroutineScope(Dispatchers.IO).launch {
send(Result.failure(BleException.PermissionDenied))
@ -546,7 +619,7 @@ class BleRepositoryImpl @Inject constructor(
characteristicId = accelerometerReadUUID
).fold(
onSuccess = { readData ->
Log.d("a read", readData.joinToString("-"))
Log.d("read", readData.joinToString(", "))
if(readData.isNotEmpty() && readData.first() == (0).toByte()){
result = true
} else {
@ -562,7 +635,7 @@ class BleRepositoryImpl @Inject constructor(
}
gatt = it.connectGatt(app, false, ReadAccelerometerHistoryCallback(app) {
gatt = it.connectGatt(app, false, ReadAccelerometerSpectreCallback(app) {
CoroutineScope(Dispatchers.IO).launch {
send(it)
}
@ -758,7 +831,14 @@ class BleRepositoryImpl @Inject constructor(
return Result.success(Unit)
}
override fun getAccelerometerMeasureBySerialFlow(serial: String): Flow<Result<Accelereate, BleException>> {
override fun getAccelerometerMeasureBySerialFlow(
serial: String,
accelScale: AccelScale,
accelMode: AccelViewMode,
fftAxis: FftAxis,
fftMode: FftViewMode,
frequency: FftFrequency,
): Flow<Result<Accelerate, BleException>> {
return callbackFlow {
@ -769,7 +849,14 @@ class BleRepositoryImpl @Inject constructor(
gatt = it.device.connectGatt(
app,
false,
ReadAccelerometerCallback(app){ result ->
ReadAccelerometerCallback(
app = app,
accelScale = accelScale,
accelMode = accelMode,
fftAxis = fftAxis,
fftMode = fftMode,
frequency = frequency
){ result ->
CoroutineScope(Dispatchers.IO).launch {
send(result)
}
@ -778,7 +865,6 @@ class BleRepositoryImpl @Inject constructor(
}
awaitClose {
Log.d("acc", "close")
gatt?.close()
}
@ -803,8 +889,6 @@ class BleRepositoryImpl @Inject constructor(
newState: Int
) {
Log.d("read", "onConnectionStateChange $newState $status")
if(status == BluetoothGatt.GATT_SUCCESS) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
@ -947,8 +1031,6 @@ class BleRepositoryImpl @Inject constructor(
newState: Int
) {
Log.d("write", "onConnectionStateChange $status $newState")
if (status == BluetoothGatt.GATT_SUCCESS) {
if (newState == BluetoothProfile.STATE_CONNECTED) {

View File

@ -10,22 +10,25 @@ 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.launch
import llc.arma.ble.domain.Result
import llc.arma.ble.domain.common.BleException
import llc.arma.ble.domain.common.ProgressState
import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.usecase.Accelereate
import llc.arma.ble.domain.usecase.AccelScale
import llc.arma.ble.domain.usecase.AccelViewMode
import llc.arma.ble.domain.usecase.Accelerate
import llc.arma.ble.domain.usecase.FftAxis
import llc.arma.ble.domain.usecase.FftFrequency
import llc.arma.ble.domain.usecase.FftViewMode
import java.util.UUID
import java.util.stream.Collectors
class ReadAccelerometerCallback(
private val app: Application,
private val onResult: (Result<Accelereate, BleException>) -> Unit
private val accelScale: AccelScale,
private val accelMode: AccelViewMode,
private val fftAxis: FftAxis,
private val fftMode: FftViewMode,
private val frequency: FftFrequency,
private val onResult: (Result<Accelerate, BleException>) -> Unit
) : BluetoothGattCallback() {
override fun onConnectionStateChange(
@ -70,6 +73,48 @@ class ReadAccelerometerCallback(
if (checkPermission()) {
val payload = byteArrayOf(
4,
accelMode.sendData,
accelScale.sendData,
fftMode.sendData,
fftAxis.sendData,
frequency.sendData,
2
)
Log.d("payload", payload.joinToString(" - "))
gatt.writeCharacteristic(it, payload)
} else {
onResult(Result.failure(BleException.PermissionDenied))
gatt.close()
}
}
}
}
override fun onCharacteristicWrite(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
status: Int
) {
super.onCharacteristicWrite(gatt, characteristic, status)
if(status == BluetoothGatt.GATT_SUCCESS){
gatt.getService(serviceUUID)?.getCharacteristic(accelerometerReadUUID)?.let {
if (checkPermission()) {
Log.d("write", "enable notifications")
gatt.setCharacteristicNotification(it, true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
@ -93,13 +138,14 @@ class ReadAccelerometerCallback(
}
@Deprecated("Deprecated in Java")
override fun onCharacteristicChanged(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic
) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
super.onCharacteristicChanged(gatt, characteristic)
onCommonCharacteristicRead(gatt, characteristic, characteristic.value)
onCommonCharacteristicRead(characteristic.value)
}
}
@ -109,23 +155,20 @@ class ReadAccelerometerCallback(
value: ByteArray
) {
super.onCharacteristicChanged(gatt, characteristic, value)
onCommonCharacteristicRead(gatt, characteristic, value)
onCommonCharacteristicRead(value)
}
private fun onCommonCharacteristicRead(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
value: ByteArray,
){
val data = value.toList().chunked(2).map {
it[1].toInt()
it.toByteArray().get2byteShortAt(0)
}
onResult(
Result.success(
Accelereate(
Accelerate(
x = data[0].toFloat(),
y = data[1].toFloat(),
z = data[2].toFloat()
@ -150,4 +193,26 @@ class ReadAccelerometerCallback(
}
}
private fun BluetoothGatt.writeCharacteristic(
characteristic: BluetoothGattCharacteristic,
data: ByteArray
): Result<Unit, BleException>{
return if(checkPermission()){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
writeCharacteristic(characteristic, data, BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT)
}else{
characteristic.value = data
writeCharacteristic(characteristic)
}
Result.success(Unit)
} else {
Result.failure(BleException.PermissionDenied)
}
}
}

View File

@ -16,7 +16,13 @@ import llc.arma.ble.domain.model.Ble
import java.nio.ByteBuffer
import java.nio.ByteOrder.LITTLE_ENDIAN
class ReadAccelerometerHistoryCallback(
fun ByteArray.get2byteShortAt(idx: Int): Int {
val shorts = ShortArray(1)
ByteBuffer.wrap(this).order(LITTLE_ENDIAN).asShortBuffer()[shorts]
return shorts[0].toInt()//(this[0].toInt() + (this[1].toInt() shl 8)).toShort()
}
class ReadAccelerometerSpectreCallback(
private val app: Application,
private val onResult: (Result<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, BleException>) -> Unit
) : BluetoothGattCallback() {
@ -31,12 +37,6 @@ class ReadAccelerometerHistoryCallback(
((this[idx + 1].toUInt() and 0xFFu) shl 8) or
(this[idx].toUInt() and 0xFFu)
private fun ByteArray.get2byteShortAt(idx: Int): Int {
val shorts = ShortArray(1)
ByteBuffer.wrap(this).order(LITTLE_ENDIAN).asShortBuffer()[shorts]
return shorts[0].toInt()//(this[0].toInt() + (this[1].toInt() shl 8)).toShort()
}
private var readProperty: Property? = null
init {
@ -137,14 +137,12 @@ class ReadAccelerometerHistoryCallback(
onCommonCharacteristicRead(gatt, characteristic, value, status)
}
@OptIn(ExperimentalUnsignedTypes::class)
private fun onCommonCharacteristicRead(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
value: ByteArray,
status: Int
){
Log.d("read", value[0].toString())
if(status == BluetoothGatt.GATT_SUCCESS){
when(readProperty){

View File

@ -7,8 +7,12 @@ import llc.arma.ble.domain.common.ProgressState
import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.model.BleInfo
import llc.arma.ble.domain.model.ConnectedBleInfo
import llc.arma.ble.domain.usecase.Accelereate
import llc.arma.ble.domain.usecase.GetBleBySerial
import llc.arma.ble.domain.usecase.AccelScale
import llc.arma.ble.domain.usecase.AccelViewMode
import llc.arma.ble.domain.usecase.Accelerate
import llc.arma.ble.domain.usecase.FftAxis
import llc.arma.ble.domain.usecase.FftFrequency
import llc.arma.ble.domain.usecase.FftViewMode
interface BleRepository {
@ -28,8 +32,22 @@ interface BleRepository {
suspend fun changeBlePassword(password: String, serial: String): Result<Unit, BleException>
fun getAccelerometerMeasureBySerialFlow(serial: String): Flow<Result<Accelereate, BleException>>
fun getAccelerometerMeasureBySerialFlow(
serial: String,
accelScale: AccelScale,
accelMode: AccelViewMode,
fftAxis: FftAxis,
fftMode: FftViewMode,
frequency: FftFrequency
): Flow<Result<Accelerate, BleException>>
suspend fun getAccelerometerHistoryBySerial(serial: String): Flow<Result<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, BleException>>
suspend fun getAccelerometerSpectreBySerial(
serial: String,
accelScale: AccelScale,
accelMode: AccelViewMode,
fftAxis: FftAxis,
fftMode: FftViewMode,
frequency: FftFrequency
): Flow<Result<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, BleException>>
}

View File

@ -1,21 +0,0 @@
package llc.arma.ble.domain.usecase
import kotlinx.coroutines.flow.Flow
import llc.arma.ble.domain.Result
import llc.arma.ble.domain.common.BleException
import llc.arma.ble.domain.common.ProgressState
import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.repository.BleRepository
import javax.inject.Inject
class GetAccelerometerHistoryBySerial @Inject constructor(
private val bleRepository: BleRepository
) {
suspend operator fun invoke(serial: String): Flow<Result<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, BleException>> {
return bleRepository.getAccelerometerHistoryBySerial(serial)
}
}

View File

@ -3,8 +3,6 @@ package llc.arma.ble.domain.usecase
import kotlinx.coroutines.flow.Flow
import llc.arma.ble.domain.Result
import llc.arma.ble.domain.common.BleException
import llc.arma.ble.domain.common.ProgressState
import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.repository.BleRepository
import javax.inject.Inject
@ -12,15 +10,30 @@ class GetAccelerometerMeasureBySerialFlow @Inject constructor(
private val bleRepository: BleRepository
) {
operator fun invoke(serial: String): Flow<Result<Accelereate, BleException>> {
operator fun invoke(
serial: String,
accelScale: AccelScale,
accelMode: AccelViewMode,
fftAxis: FftAxis,
fftMode: FftViewMode,
frequency: FftFrequency
): Flow<Result<Accelerate, BleException>> {
return bleRepository.getAccelerometerMeasureBySerialFlow(serial)
return bleRepository.getAccelerometerMeasureBySerialFlow(serial, accelScale, accelMode, fftAxis, fftMode, frequency)
}
}
data class Accelereate(
enum class AccelViewMode {
ACCELERATION, PEAK_ACCELERATION, RMS, ANGLE
}
enum class AccelScale(val k: Int) {
S_2(2), S_4(4), S_8(8), S_16(16)
}
data class Accelerate(
val x: Float,
val y: Float,
val z: Float,

View File

@ -0,0 +1,40 @@
package llc.arma.ble.domain.usecase
import kotlinx.coroutines.flow.Flow
import llc.arma.ble.domain.Result
import llc.arma.ble.domain.common.BleException
import llc.arma.ble.domain.common.ProgressState
import llc.arma.ble.domain.model.Ble
import llc.arma.ble.domain.repository.BleRepository
import javax.inject.Inject
class GetAccelerometerSpectreBySerial @Inject constructor(
private val bleRepository: BleRepository
) {
suspend operator fun invoke(
serial: String,
accelScale: AccelScale,
accelMode: AccelViewMode,
fftAxis: FftAxis,
fftMode: FftViewMode,
frequency: FftFrequency
): Flow<Result<ProgressState<List<Ble.Accelerometer.MeasurePoint>>, BleException>> {
return bleRepository.getAccelerometerSpectreBySerial(serial, accelScale, accelMode, fftAxis, fftMode, frequency)
}
}
enum class FftFrequency {
OFF, F_1, F_10, F_25, F_50, F_100, F_200, F_400, F_1620, F_1344
}
enum class FftViewMode {
SPECTRE, X, Y, Z
}
enum class FftAxis {
AUTO, X, Y, Z
}