diff --git a/app/build.gradle b/app/build.gradle index 62ab741..77d72f2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,10 +11,10 @@ android { defaultConfig { applicationId "llc.arma.ble" - minSdk 24 + minSdk 26 targetSdk 33 - versionCode 9 - versionName "1.2.9" + versionCode 10 + versionName "1.2.10" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { @@ -87,4 +87,6 @@ dependencies { implementation "com.patrykandpatrick.vico:compose:1.7.1" implementation "com.patrykandpatrick.vico:compose-m3:1.7.1" + implementation files('libs/poishadow-all.jar') + } \ No newline at end of file diff --git a/app/libs/poishadow-all.jar b/app/libs/poishadow-all.jar new file mode 100644 index 0000000..177eba8 Binary files /dev/null and b/app/libs/poishadow-all.jar differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8d50d78..2469251 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -29,6 +29,18 @@ tools:targetApi="31" android:name=".app.framework.App"> + + + + + + () { init { diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelerometerSpectre.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelerometerSpectre.kt index 16e13ea..7de1d1c 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelerometerSpectre.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AccelerometerSpectre.kt @@ -226,6 +226,7 @@ fun Display( producer.setEntries( data.mapIndexed { index, measurePoint -> AccelerometerEntry(measurePoint.frequency, index.toFloat(), measurePoint.value) + } ) } diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterAccel.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterAccel.kt index ad72a1e..e05195c 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterAccel.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterAccel.kt @@ -345,12 +345,11 @@ fun Display( } - } } @Composable -private fun Angle( +public fun Angle( modifier: Modifier = Modifier, angle: Float ) { diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterHistory.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterHistory.kt index 220af9f..a31c832 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterHistory.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/AcceleromterHistory.kt @@ -23,6 +23,7 @@ import llc.arma.ble.app.ui.common.ViewState import llc.arma.ble.domain.model.BleInfo import javax.inject.Inject import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.CloudUpload import androidx.compose.material.icons.rounded.Refresh import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.text.style.TextAlign @@ -31,6 +32,8 @@ import com.patrykandpatrick.vico.compose.chart.line.lineChart import com.patrykandpatrick.vico.compose.chart.scroll.rememberChartScrollSpec import com.patrykandpatrick.vico.core.axis.AxisPosition import com.patrykandpatrick.vico.core.axis.formatter.AxisValueFormatter +import com.patrykandpatrick.vico.core.chart.decoration.ThresholdLine +import com.patrykandpatrick.vico.core.chart.scale.AutoScaleUp import com.patrykandpatrick.vico.core.entry.ChartEntry import com.patrykandpatrick.vico.core.entry.FloatEntry import com.patrykandpatrick.vico.core.scroll.AutoScrollCondition @@ -43,10 +46,12 @@ import llc.arma.ble.domain.common.ProgressState import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.usecase.AccelScale import llc.arma.ble.domain.usecase.AccelViewMode +import llc.arma.ble.domain.usecase.ExportToXlsx 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.GetAccelerometerHistoryBySerial +import llc.arma.ble.domain.usecase.MeasureData import java.util.Date class AccelEntry( @@ -108,6 +113,21 @@ fun AccelerometerHistory( style = MaterialTheme.typography.titleLarge ) + IconButton( + onClick = { + viewModel.setEvent(AccelerometerHistoryContract.Event.OnExport) + }, + enabled = when(state){ + is AccelerometerHistoryContract.State.Display -> state.loadingHistoryState is ProgressState.Finished + AccelerometerHistoryContract.State.Exception -> false + } + ) { + Icon( + imageVector = Icons.Rounded.CloudUpload, + contentDescription = null + ) + } + IconButton( onClick = { viewModel.setEvent(AccelerometerHistoryContract.Event.OnRefreshHistory(ble.serial, accelScale, accelMode, fftAxis, fftMode, frequency)) @@ -164,14 +184,6 @@ fun Display( } else { - val producer = remember { - ChartEntryModelProducer(listOf()) - } - - producer.setEntries(state.loadingHistoryState.data.mapIndexed { index, measurePoint -> - AccelEntry(measurePoint.frequency, index.toFloat(), measurePoint.value ) - }) - val axisValueFormatter = AxisValueFormatter { value, chartValues -> (chartValues.chartEntryModel.entries.firstOrNull() @@ -181,25 +193,174 @@ fun Display( .orEmpty() } - val lineChart = lineChart() + val xProducer = remember { + ChartEntryModelProducer(listOf()) + } - Chart( - chart = lineChart, - chartModelProducer = producer, - startAxis = startAxis(), - bottomAxis = bottomAxis( - tickLength = 0.dp, - valueFormatter = axisValueFormatter, - labelRotationDegrees = -90f, - ), - modifier = Modifier.fillMaxSize(), - chartScrollSpec = rememberChartScrollSpec( - initialScroll = InitialScroll.End, - autoScrollCondition = AutoScrollCondition.OnModelSizeIncreased, - autoScrollAnimationSpec = tween(0) + val yProducer = remember { + ChartEntryModelProducer(listOf()) + } + + val zProducer = remember { + ChartEntryModelProducer(listOf()) + } + + xProducer.setEntries(state.loadingHistoryState.data.mapIndexed { index, measurePoint -> + when(measurePoint){ + is Ble.Accelerometer.HistoryPoint.Accelerate -> { + AccelEntry(measurePoint.date, index.toFloat(), measurePoint.x ) + } + is Ble.Accelerometer.HistoryPoint.Vibration -> { + AccelEntry(measurePoint.date, index.toFloat(), measurePoint.value) + } + is Ble.Accelerometer.HistoryPoint.Angle -> { + AccelEntry(measurePoint.date, index.toFloat(), measurePoint.x ) + } + } + }) + + yProducer.setEntries(state.loadingHistoryState.data.mapIndexed { index, measurePoint -> + when(measurePoint){ + is Ble.Accelerometer.HistoryPoint.Accelerate -> { + AccelEntry(measurePoint.date, index.toFloat(), measurePoint.y ) + } + is Ble.Accelerometer.HistoryPoint.Vibration -> { + AccelEntry(measurePoint.date, index.toFloat(), measurePoint.value) + } + is Ble.Accelerometer.HistoryPoint.Angle -> { + AccelEntry(measurePoint.date, index.toFloat(), measurePoint.y ) + } + } + }) + + zProducer.setEntries(state.loadingHistoryState.data.mapIndexed { index, measurePoint -> + when(measurePoint){ + is Ble.Accelerometer.HistoryPoint.Accelerate -> { + AccelEntry(measurePoint.date, index.toFloat(), measurePoint.z ) + } + is Ble.Accelerometer.HistoryPoint.Vibration -> { + AccelEntry(measurePoint.date, index.toFloat(), measurePoint.value) + } + is Ble.Accelerometer.HistoryPoint.Angle -> { + AccelEntry(measurePoint.date, index.toFloat(), measurePoint.z ) + } + } + }) + + val lineChart = lineChart( + decorations = listOf( + ThresholdLine( + thresholdValue = 0f + ) ) ) + val lastMeasure = state.loadingHistoryState.data.lastOrNull() + + if((lastMeasure is Ble.Accelerometer.HistoryPoint.Vibration).not()) { + + Column() { + + Text(text = "Ось X:") + + Chart( + chart = lineChart, + chartModelProducer = xProducer, + startAxis = startAxis(), + bottomAxis = bottomAxis( + tickLength = 0.dp, + valueFormatter = axisValueFormatter, + labelRotationDegrees = -90f, + ), + modifier = Modifier + .fillMaxWidth() + .weight(1f), + autoScaleUp = AutoScaleUp.None, + diffAnimationSpec = tween(0), + chartScrollSpec = rememberChartScrollSpec( + initialScroll = InitialScroll.End, + autoScrollCondition = AutoScrollCondition.OnModelSizeIncreased, + autoScrollAnimationSpec = tween(0) + ) + ) + + Text(text = "Ось Y:") + Chart( + chart = lineChart, + chartModelProducer = yProducer, + startAxis = startAxis(), + bottomAxis = bottomAxis( + tickLength = 0.dp, + valueFormatter = axisValueFormatter, + labelRotationDegrees = -90f, + ), + modifier = Modifier + .fillMaxWidth() + .weight(1f), + autoScaleUp = AutoScaleUp.None, + diffAnimationSpec = tween(0), + chartScrollSpec = rememberChartScrollSpec( + initialScroll = InitialScroll.End, + autoScrollCondition = AutoScrollCondition.OnModelSizeIncreased, + autoScrollAnimationSpec = tween(0) + ) + ) + + Text(text = "Ось Z:") + Chart( + chart = lineChart, + chartModelProducer = zProducer, + startAxis = startAxis(), + bottomAxis = bottomAxis( + tickLength = 0.dp, + valueFormatter = axisValueFormatter, + labelRotationDegrees = -90f, + ), + modifier = Modifier + .fillMaxWidth() + .weight(1f), + autoScaleUp = AutoScaleUp.None, + diffAnimationSpec = tween(0), + chartScrollSpec = rememberChartScrollSpec( + initialScroll = InitialScroll.End, + autoScrollCondition = AutoScrollCondition.OnModelSizeIncreased, + autoScrollAnimationSpec = tween(0) + ) + ) + + } + + } else { + + Column { + + Text(text = "Вибрация:") + + Chart( + chart = lineChart, + chartModelProducer = xProducer, + startAxis = startAxis(), + bottomAxis = bottomAxis( + tickLength = 0.dp, + valueFormatter = axisValueFormatter, + labelRotationDegrees = -90f, + ), + modifier = Modifier + .fillMaxWidth() + .weight(1f), + autoScaleUp = AutoScaleUp.None, + diffAnimationSpec = tween(0), + chartScrollSpec = rememberChartScrollSpec( + initialScroll = InitialScroll.End, + autoScrollCondition = AutoScrollCondition.OnModelSizeIncreased, + autoScrollAnimationSpec = tween(0) + ) + ) + + } + + } + } } @@ -261,6 +422,8 @@ class AccelerometerHistoryContract { object StopMeasure : Event() + object OnExport : Event() + data class OnStart( val serial: String, val accelScale: AccelScale, @@ -284,7 +447,7 @@ class AccelerometerHistoryContract { sealed class State : ViewState { data class Display( - val loadingHistoryState : ProgressState> + val loadingHistoryState : ProgressState> ) : State() object Exception : State() @@ -302,6 +465,7 @@ class AccelerometerHistoryContract { @HiltViewModel class AccelerometerHistoryViewModel @Inject constructor( private val getAccelerometerHistoryBySerial: GetAccelerometerHistoryBySerial, + private val exportToXlsx: ExportToXlsx ) : BaseViewModel() { var measureJob: Job? = null @@ -317,15 +481,28 @@ class AccelerometerHistoryViewModel @Inject constructor( is AccelerometerHistoryContract.Event.OnStart -> reduce(viewState.value, event) is AccelerometerHistoryContract.Event.OnRefreshHistory -> reduce(viewState.value, event) is AccelerometerHistoryContract.Event.StopMeasure -> reduce(viewState.value, event) + is AccelerometerHistoryContract.Event.OnExport -> reduce(viewState.value, event) } } + private fun reduce( + state: AccelerometerHistoryContract.State, + event: AccelerometerHistoryContract.Event.OnExport + ) { + + if(state is AccelerometerHistoryContract.State.Display){ + if(state.loadingHistoryState is ProgressState.Finished){ + exportToXlsx.invoke(state.loadingHistoryState.data) + } + } + + } + private fun reduce( state: AccelerometerHistoryContract.State, event: AccelerometerHistoryContract.Event.StopMeasure ) { - measureJob?.cancel() measureJob = null diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/IntervalEdit.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/IntervalEdit.kt index 6d76677..e58a4c8 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/IntervalEdit.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/IntervalEdit.kt @@ -22,7 +22,7 @@ fun IntervalEdit( ){ var value by remember(state.accelerometerState.historyInterval) { - mutableStateOf((state.accelerometerState.historyInterval / 1000 / 60 / 60).toInt()) + mutableIntStateOf((state.accelerometerState.historyInterval).toInt()) } val maxInterval = 10 * 24 * 60 * 60 * 1000 diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/Write.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/Write.kt index 9687ee1..c8f88bd 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/Write.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/accelerometer/view/Write.kt @@ -142,13 +142,17 @@ fun Write( modifier = Modifier.weight(1f) ) { + val hours = it / 1000 / 60 / 60 + val minutes = (it - ( hours * 1000 * 60 * 60 )) / 1000 / 60 + Text( - text = "Интервал измерний" + text = "Интервал измерений" ) + Text( color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.bodyMedium, - text = "${it / 1000 / 60 / 60} ч." + text = "$hours ч. $minutes мин." ) } diff --git a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/thermometer/view/Write.kt b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/thermometer/view/Write.kt index 9f87084..fd506bf 100644 --- a/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/thermometer/view/Write.kt +++ b/app/src/main/java/llc/arma/ble/app/ui/screen/inspection/thermometer/view/Write.kt @@ -137,13 +137,17 @@ fun Write( modifier = Modifier.weight(1f) ) { + val hours = it / 1000 / 60 / 60 + val minutes = (it - ( hours * 1000 * 60 * 60 )) / 1000 / 60 + Text( - text = "Интервал измерний" + text = "Интервал измерений" ) + Text( color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.bodyMedium, - text = "${it / 1000 / 60 / 60} ч." + text = "$hours ч. $minutes мин." ) } diff --git a/app/src/main/java/llc/arma/ble/data/BleRepositoryImpl.kt b/app/src/main/java/llc/arma/ble/data/BleRepositoryImpl.kt index dd2ce87..ffafb68 100644 --- a/app/src/main/java/llc/arma/ble/data/BleRepositoryImpl.kt +++ b/app/src/main/java/llc/arma/ble/data/BleRepositoryImpl.kt @@ -542,7 +542,6 @@ class BleRepositoryImpl @Inject constructor( serviceId = serviceUUID, characteristicId = accelerometerReadUUID, ).fold( - onSuccess = { Log.d("history", it.joinToString { it.toString() }) val scale = when(it[1].toInt()){ @@ -559,6 +558,7 @@ class BleRepositoryImpl @Inject constructor( 1 -> AccelViewMode.PEAK_ACCELERATION 2 -> AccelViewMode.RMS 3 -> AccelViewMode.VIBRATION + 4 -> AccelViewMode.ANGLE else -> { return Result.failure(BleException.UnexpectedResponse) } @@ -572,7 +572,6 @@ class BleRepositoryImpl @Inject constructor( onFailure = { return Result.failure(BleException.UnexpectedResponse) } - ) } false -> Ble.Accelerometer.History.Disabled @@ -738,17 +737,31 @@ class BleRepositoryImpl @Inject constructor( override suspend fun getAccelerometerHistoryBySerial( serial: String - ): Flow>, BleException>> { + ): Flow>, BleException>> { var gatt: BluetoothGatt? = null return callbackFlow { - deviceCache[serial]?.device?.let { device -> + deviceCache[serial]?.let { result -> + + val device = result.device if (checkPermission()) { - val scale = writeCharacteristic( + val state = readAccelState(result).fold( + onSuccess = { + + }, + onFailure = { + null + } + ) + + var scale: AccelScale? = null + var mode: AccelViewMode? = null + + writeCharacteristic( device = device, serviceId = serviceUUID, characteristicId = accelerometerReadUUID, @@ -761,13 +774,21 @@ class BleRepositoryImpl @Inject constructor( characteristicId = accelerometerReadUUID, ).fold( onSuccess = { - when(it[1].toInt()){ + scale = when(it[1].toInt()){ 0 -> AccelScale.S_2 1 -> AccelScale.S_4 2 -> AccelScale.S_8 3 -> AccelScale.S_16 else -> null } + mode = when(it[0].toInt()){ + 0 -> AccelViewMode.ACCELERATION + 1 -> AccelViewMode.PEAK_ACCELERATION + 2 -> AccelViewMode.RMS + 3 -> AccelViewMode.VIBRATION + 4 -> AccelViewMode.ANGLE + else -> null + } }, onFailure = { null } ) @@ -777,12 +798,12 @@ class BleRepositoryImpl @Inject constructor( } ) - if(scale != null) { + if(scale != null && mode != null) { gatt = device.connectGatt( app, false, - ReadAccelerometerHistoryCallback(scale, app) { + ReadAccelerometerHistoryCallback(mode!!, scale!!, app) { CoroutineScope(Dispatchers.IO).launch { send(it) } diff --git a/app/src/main/java/llc/arma/ble/data/EmailRepositoryImpl.kt b/app/src/main/java/llc/arma/ble/data/EmailRepositoryImpl.kt new file mode 100644 index 0000000..fab9f76 --- /dev/null +++ b/app/src/main/java/llc/arma/ble/data/EmailRepositoryImpl.kt @@ -0,0 +1,41 @@ +package llc.arma.ble.data + +import android.app.Application +import android.content.Intent +import androidx.core.content.FileProvider +import llc.arma.ble.R +import llc.arma.ble.domain.repository.EmailRepository +import llc.arma.ble.domain.repository.XlsxRepository +import llc.arma.ble.domain.usecase.MeasureData +import org.apache.poi.ss.SpreadsheetVersion +import org.apache.poi.ss.usermodel.WorkbookFactory +import org.apache.poi.ss.util.AreaReference +import org.apache.poi.ss.util.CellReference +import org.apache.poi.util.IOUtils +import org.apache.poi.xssf.usermodel.XSSFSheet +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.util.UUID +import javax.inject.Inject + +class EmailRepositoryImpl @Inject constructor( + private val application: Application +) : EmailRepository { + + override fun sendFile(file: File) { + val uri = FileProvider.getUriForFile(application, "llc.arma.ble.fileprovider", file) + + val sendIntent = Intent(Intent.ACTION_SEND) + sendIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + sendIntent.type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + sendIntent.putExtra(Intent.EXTRA_SUBJECT, "Measure history") + sendIntent.putExtra(Intent.EXTRA_STREAM, uri) + application.startActivity( + Intent.createChooser(sendIntent, "Send email...").apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK + } + ) + } + +} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/data/ReadAccelerometerCallback.kt b/app/src/main/java/llc/arma/ble/data/ReadAccelerometerCallback.kt index 8d0b5e4..6b2420b 100644 --- a/app/src/main/java/llc/arma/ble/data/ReadAccelerometerCallback.kt +++ b/app/src/main/java/llc/arma/ble/data/ReadAccelerometerCallback.kt @@ -261,7 +261,7 @@ fun calculateAngle( } -fun calculateZAngle( +public fun calculateZAngle( x: Float, y: Float ): Float { diff --git a/app/src/main/java/llc/arma/ble/data/ReadAccelerometerHistoryCallback.kt b/app/src/main/java/llc/arma/ble/data/ReadAccelerometerHistoryCallback.kt index cde8cf4..dca14e3 100644 --- a/app/src/main/java/llc/arma/ble/data/ReadAccelerometerHistoryCallback.kt +++ b/app/src/main/java/llc/arma/ble/data/ReadAccelerometerHistoryCallback.kt @@ -14,11 +14,13 @@ import llc.arma.ble.domain.common.BleException import llc.arma.ble.domain.common.ProgressState import llc.arma.ble.domain.model.Ble import llc.arma.ble.domain.usecase.AccelScale +import llc.arma.ble.domain.usecase.AccelViewMode class ReadAccelerometerHistoryCallback( + private val mode: AccelViewMode, private val scale: AccelScale, private val app: Application, - private val onResult: (Result>, BleException>) -> Unit + private val onResult: (Result>, BleException>) -> Unit ) : BluetoothGattCallback() { enum class Property { @@ -218,11 +220,37 @@ class ReadAccelerometerHistoryCallback( onResult( Result.success( ProgressState.Finished( - resultTemperaturePackage.withIndex().map { - Ble.Accelerometer.MeasurePoint( - frequency = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), - value = (it.value * scale.k) / Short.MAX_VALUE - ) + when(mode){ + AccelViewMode.ACCELERATION, + AccelViewMode.PEAK_ACCELERATION, + AccelViewMode.RMS -> { + resultTemperaturePackage.chunked(3).withIndex().map { + Ble.Accelerometer.HistoryPoint.Angle( + date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), + x = (it.value[0] * scale.k) / Short.MAX_VALUE, + y = (it.value[1] * scale.k) / Short.MAX_VALUE, + z = (it.value[2] * scale.k) / Short.MAX_VALUE + ) + } + } + AccelViewMode.ANGLE -> { + resultTemperaturePackage.chunked(3).withIndex().map { + Ble.Accelerometer.HistoryPoint.Angle( + date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), + x = calculateZAngle(it.value[2], it.value[1]) * 180f / Math.PI.toFloat(), + y = calculateZAngle(it.value[2], it.value[0]) * 180f / Math.PI.toFloat(), + z = calculateZAngle(it.value[0], it.value[1]) * 180f / Math.PI.toFloat() + ) + } + } + AccelViewMode.VIBRATION -> { + resultTemperaturePackage.withIndex().map { + Ble.Accelerometer.HistoryPoint.Vibration( + date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), + value = (it.value * scale.k) / Short.MAX_VALUE + ) + } + } } ) ) @@ -261,11 +289,37 @@ class ReadAccelerometerHistoryCallback( onResult( Result.success( ProgressState.Finished( - resultTemperaturePackage.withIndex().map { - Ble.Accelerometer.MeasurePoint( - frequency = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), - value = (it.value * scale.k) / Short.MAX_VALUE - ) + when(mode){ + AccelViewMode.ACCELERATION, + AccelViewMode.PEAK_ACCELERATION, + AccelViewMode.RMS -> { + resultTemperaturePackage.chunked(3).withIndex().map { + Ble.Accelerometer.HistoryPoint.Angle( + date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), + x = (it.value[0] * scale.k) / Short.MAX_VALUE, + y = (it.value[1] * scale.k) / Short.MAX_VALUE, + z = (it.value[2] * scale.k) / Short.MAX_VALUE + ) + } + } + AccelViewMode.ANGLE -> { + resultTemperaturePackage.chunked(3).withIndex().map { + Ble.Accelerometer.HistoryPoint.Angle( + date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), + x = calculateZAngle(it.value[2], it.value[1]) * 180f / Math.PI.toFloat(), + y = calculateZAngle(it.value[2], it.value[0]) * 180f / Math.PI.toFloat(), + z = calculateZAngle(it.value[0], it.value[1]) * 180f / Math.PI.toFloat() + ) + } + } + AccelViewMode.VIBRATION -> { + resultTemperaturePackage.withIndex().map { + Ble.Accelerometer.HistoryPoint.Vibration( + date = lastMeasureSystemTime!! - (((resultTemperaturePackage.size - 1) - it.index) * bleMeasureInterval!!), + value = (it.value * scale.k) / Short.MAX_VALUE + ) + } + } } ) ) diff --git a/app/src/main/java/llc/arma/ble/data/WriteAccelerometerCallback.kt b/app/src/main/java/llc/arma/ble/data/WriteAccelerometerCallback.kt index 69815af..83253e0 100644 --- a/app/src/main/java/llc/arma/ble/data/WriteAccelerometerCallback.kt +++ b/app/src/main/java/llc/arma/ble/data/WriteAccelerometerCallback.kt @@ -64,36 +64,33 @@ class WriteAccelerometerCallback( status: Int ){ - if(request.tx != null || request.saveHistory != null) { + Log.d("write", "${request.tx != null} ${request.saveHistory != null} ${request.historyInterval != null}") + + if(request.tx != null || request.saveHistory != null || request.historyInterval != null) { fun UInt.to4ByteArrayInLittleEndian(): ByteArray = (3 downTo 0).map { (this shr (it * Byte.SIZE_BITS)).toByte() }.toByteArray() - var uuid: Pair? = null + var uuid: Triple? = null uuid = request.historyInterval?.let { - this.request = request.copy( - historyInterval = null - ) - - Pair( + Triple( intervalWriteUUID, mutableListOf(3).apply { addAll((it).toUInt().to4ByteArrayInLittleEndian().reversed().toList()) - }.toByteArray() + }.toByteArray(), + request.copy( + historyInterval = null + ) ) } uuid = request.saveHistory?.let { - this.request = request.copy( - saveHistory = null - ) - - Pair( + Triple( saveEnabledWriteUUID, mutableListOf(4).apply { add(if (it is Ble.Accelerometer.History.Enabled) 1 else 0) @@ -101,17 +98,16 @@ class WriteAccelerometerCallback( add(it.mode.sendData) add(it.scale.sendData) } - }.toByteArray() + }.toByteArray(), + request.copy( + saveHistory = null + ) ) } ?: uuid uuid = request.tx?.let { - this.request = request.copy( - tx = null - ) - - Pair( + Triple( txWriteUUID, byteArrayOf( when (it) { @@ -125,6 +121,9 @@ class WriteAccelerometerCallback( Ble.BleState.TX.PLUS_3 -> 3 Ble.BleState.TX.PLUS_4 -> 4 } + ), + request.copy( + tx = null ) ) @@ -138,6 +137,8 @@ class WriteAccelerometerCallback( gatt.writeCharacteristic(it, uuid.second) + request = uuid.third + return } diff --git a/app/src/main/java/llc/arma/ble/data/WriteThermometerCallback.kt b/app/src/main/java/llc/arma/ble/data/WriteThermometerCallback.kt index 833d6b3..a9fe747 100644 --- a/app/src/main/java/llc/arma/ble/data/WriteThermometerCallback.kt +++ b/app/src/main/java/llc/arma/ble/data/WriteThermometerCallback.kt @@ -75,43 +75,37 @@ class WriteThermometerCallback( (this shr (it * Byte.SIZE_BITS)).toByte() }.toByteArray() - var uuid: Pair? = null + var uuid: Triple? = null uuid = request.historyInterval?.let { - this.request = request.copy( - historyInterval = null - ) - - Pair( + Triple( intervalWriteUUID, mutableListOf(3).apply { addAll((it).toUInt().to4ByteArrayInLittleEndian().reversed().toList()) - }.toByteArray() + }.toByteArray(), + request.copy( + historyInterval = null + ) ) } uuid = request.saveHistory?.let { - this.request = request.copy( - saveHistory = null - ) - - Pair( + Triple( saveEnabledWriteUUID, mutableListOf(4).apply { add(if (it) 1 else 0) - }.toByteArray() + }.toByteArray(), + request.copy( + saveHistory = null + ) ) } ?: uuid uuid = request.tx?.let { - this.request = request.copy( - tx = null - ) - - Pair( + Triple( txWriteUUID, byteArrayOf( when (it) { @@ -125,6 +119,9 @@ class WriteThermometerCallback( Ble.BleState.TX.PLUS_3 -> 3 Ble.BleState.TX.PLUS_4 -> 4 } + ), + request.copy( + tx = null ) ) @@ -137,7 +134,7 @@ class WriteThermometerCallback( }?.let { gatt.writeCharacteristic(it, uuid.second) - + request = uuid.third return } diff --git a/app/src/main/java/llc/arma/ble/data/XlsxRepositoryImpl.kt b/app/src/main/java/llc/arma/ble/data/XlsxRepositoryImpl.kt new file mode 100644 index 0000000..298c706 --- /dev/null +++ b/app/src/main/java/llc/arma/ble/data/XlsxRepositoryImpl.kt @@ -0,0 +1,88 @@ +package llc.arma.ble.data + +import android.app.Application +import android.icu.text.SimpleDateFormat +import llc.arma.ble.R +import llc.arma.ble.domain.model.Ble +import llc.arma.ble.domain.repository.XlsxRepository +import org.apache.poi.ss.usermodel.WorkbookFactory +import org.apache.poi.util.IOUtils +import org.apache.poi.xssf.usermodel.XSSFSheet +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.util.Date +import java.util.UUID +import javax.inject.Inject + +class XlsxRepositoryImpl @Inject constructor( + private val application: Application +) : XlsxRepository { + + + + override fun exportToXls(data: List): File { + + val formatter = SimpleDateFormat("dd.MM.yyyy HH:mm") + + val file = File(application.filesDir, "${UUID.randomUUID()}.xlsx") + file.createNewFile() + when(data.firstOrNull()){ + is Ble.Accelerometer.HistoryPoint.Vibration -> { + IOUtils.copy(application.resources.openRawResource(R.raw.measure_single_axis), FileOutputStream(file)) + } + else -> { + IOUtils.copy(application.resources.openRawResource(R.raw.measure_multiple_axis), FileOutputStream(file)) + } + } + + + val fileIn = FileInputStream(file) + val workbook = WorkbookFactory.create(fileIn) + val worksheet = workbook.getSheetAt(0) as XSSFSheet + + data.withIndex().forEach { + + val row = worksheet.createRow(it.index + 1) + val dateX = row.createCell(0) + val dateY = row.createCell(2) + val dateZ = row.createCell(4) + val x = row.createCell(1) + val y = row.createCell(3) + val z = row.createCell(5) + + when(val value = it.value){ + is Ble.Accelerometer.HistoryPoint.Angle -> { + dateX.setCellValue(formatter.format(Date(value.date))) + dateY.setCellValue(formatter.format(Date(value.date))) + dateZ.setCellValue(formatter.format(Date(value.date))) + x.setCellValue(value.x.toDouble()) + y.setCellValue(value.y.toDouble()) + z.setCellValue(value.z.toDouble()) + } + is Ble.Accelerometer.HistoryPoint.Vibration -> { + dateX.setCellValue(formatter.format(Date(value.date))) + x.setCellValue(value.value.toDouble()) + } + + is Ble.Accelerometer.HistoryPoint.Accelerate -> { + dateX.setCellValue(formatter.format(Date(value.date))) + dateY.setCellValue(formatter.format(Date(value.date))) + dateZ.setCellValue(formatter.format(Date(value.date))) + x.setCellValue(value.x.toDouble()) + y.setCellValue(value.y.toDouble()) + z.setCellValue(value.z.toDouble()) + } + + } + } + + fileIn.close() + val saveFos = FileOutputStream(file) + workbook.write(saveFos) + workbook.close() + saveFos.close() + return file + } + +} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/domain/model/Ble.kt b/app/src/main/java/llc/arma/ble/domain/model/Ble.kt index 5bced8e..4c82b99 100644 --- a/app/src/main/java/llc/arma/ble/domain/model/Ble.kt +++ b/app/src/main/java/llc/arma/ble/domain/model/Ble.kt @@ -31,18 +31,34 @@ sealed class Ble( val historyInterval: Long? ) + sealed class HistoryPoint { + + class Vibration ( + val date: Long, + val value: Float + ) : HistoryPoint() + + class Accelerate ( + val date: Long, + val x: Float, + val y: Float, + val z: Float + ) : HistoryPoint() + + class Angle ( + val date: Long, + val x: Float, + val y: Float, + val z: Float + ) : HistoryPoint() + + } + class MeasurePoint ( val frequency: Long, val value: Float ) - class HistoryPoint ( - val time: Long, - val x: Float, - val y: Float, - val z: Float - ) - data class AccelerometerState( val saveHistory: History, val historyInterval: Long diff --git a/app/src/main/java/llc/arma/ble/domain/repository/BleRepository.kt b/app/src/main/java/llc/arma/ble/domain/repository/BleRepository.kt index 550bd04..cdac918 100644 --- a/app/src/main/java/llc/arma/ble/domain/repository/BleRepository.kt +++ b/app/src/main/java/llc/arma/ble/domain/repository/BleRepository.kt @@ -50,6 +50,6 @@ interface BleRepository { frequency: FftFrequency ): Flow>, BleException>> - suspend fun getAccelerometerHistoryBySerial(serial: String): Flow>, BleException>> + suspend fun getAccelerometerHistoryBySerial(serial: String): Flow>, BleException>> } \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/domain/repository/EmailRepository.kt b/app/src/main/java/llc/arma/ble/domain/repository/EmailRepository.kt new file mode 100644 index 0000000..fbb5170 --- /dev/null +++ b/app/src/main/java/llc/arma/ble/domain/repository/EmailRepository.kt @@ -0,0 +1,9 @@ +package llc.arma.ble.domain.repository + +import java.io.File + +interface EmailRepository { + + fun sendFile(file: File) + +} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/domain/repository/XlsxRepository.kt b/app/src/main/java/llc/arma/ble/domain/repository/XlsxRepository.kt new file mode 100644 index 0000000..3d308b5 --- /dev/null +++ b/app/src/main/java/llc/arma/ble/domain/repository/XlsxRepository.kt @@ -0,0 +1,11 @@ +package llc.arma.ble.domain.repository + +import llc.arma.ble.domain.model.Ble +import llc.arma.ble.domain.usecase.MeasureData +import java.io.File + +interface XlsxRepository { + + fun exportToXls(data: List): File + +} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/domain/usecase/ExportToXlsx.kt b/app/src/main/java/llc/arma/ble/domain/usecase/ExportToXlsx.kt new file mode 100644 index 0000000..1755a0d --- /dev/null +++ b/app/src/main/java/llc/arma/ble/domain/usecase/ExportToXlsx.kt @@ -0,0 +1,34 @@ +package llc.arma.ble.domain.usecase + +import android.app.Application +import llc.arma.ble.R +import llc.arma.ble.domain.model.Ble +import llc.arma.ble.domain.repository.EmailRepository +import llc.arma.ble.domain.repository.XlsxRepository +import org.apache.poi.ss.SpreadsheetVersion +import org.apache.poi.ss.usermodel.WorkbookFactory +import org.apache.poi.ss.util.AreaReference +import org.apache.poi.ss.util.CellReference +import org.apache.poi.util.IOUtils +import org.apache.poi.xssf.usermodel.XSSFCell +import org.apache.poi.xssf.usermodel.XSSFSheet +import org.apache.poi.xssf.usermodel.XSSFTable +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.util.UUID +import javax.inject.Inject + +class ExportToXlsx @Inject constructor( + private val xlsxRepository: XlsxRepository, + private val emailRepository: EmailRepository +) { + + operator fun invoke(data: List){ + + val file = xlsxRepository.exportToXls(data) + emailRepository.sendFile(file) + + } + +} \ No newline at end of file diff --git a/app/src/main/java/llc/arma/ble/domain/usecase/GetAccelerometerHistoryBySerial.kt b/app/src/main/java/llc/arma/ble/domain/usecase/GetAccelerometerHistoryBySerial.kt index e7496b2..a3b0f56 100644 --- a/app/src/main/java/llc/arma/ble/domain/usecase/GetAccelerometerHistoryBySerial.kt +++ b/app/src/main/java/llc/arma/ble/domain/usecase/GetAccelerometerHistoryBySerial.kt @@ -12,7 +12,7 @@ class GetAccelerometerHistoryBySerial @Inject constructor( private val bleRepository: BleRepository ) { - suspend operator fun invoke(serial: String): Flow>, BleException>> { + suspend operator fun invoke(serial: String): Flow>, BleException>> { return bleRepository.getAccelerometerHistoryBySerial(serial) diff --git a/app/src/main/res/raw/measure_multiple_axis.xlsx b/app/src/main/res/raw/measure_multiple_axis.xlsx new file mode 100644 index 0000000..198220a Binary files /dev/null and b/app/src/main/res/raw/measure_multiple_axis.xlsx differ diff --git a/app/src/main/res/raw/measure_single_axis.xlsx b/app/src/main/res/raw/measure_single_axis.xlsx new file mode 100644 index 0000000..370a150 Binary files /dev/null and b/app/src/main/res/raw/measure_single_axis.xlsx differ diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml new file mode 100644 index 0000000..ef021c3 --- /dev/null +++ b/app/src/main/res/xml/provider_paths.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file