diff --git a/app/build.gradle b/app/build.gradle index ba4691d..662fc62 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -19,8 +19,8 @@ android { applicationId "in.sminnovations.hpostesting.quality" minSdk 21 targetSdk 34 - versionCode 56 - versionName "2.1.56" + versionCode 68 + versionName "2.1.68" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/java/com/example/hpostesting/data/constant/Constants.kt b/app/src/main/java/com/example/hpostesting/data/constant/Constants.kt index f98776d..08d9ac0 100644 --- a/app/src/main/java/com/example/hpostesting/data/constant/Constants.kt +++ b/app/src/main/java/com/example/hpostesting/data/constant/Constants.kt @@ -86,7 +86,17 @@ object Constants { "HCV-000-3007", "HCV-000-3008", "HCV-000-3009", - "HCV-000-3010" + "HCV-000-3010", + "HCV-000-3011", + "HCV-000-3012", + "HCV-000-3013", + "HCV-000-3014", + "HCV-000-3015", + "HCV-000-3016", + "HCV-000-3017", + "HCV-000-3018", + "HCV-000-3019", + "HCV-000-3020" ) const val password = "SMI@12345" @@ -113,7 +123,23 @@ object Constants { const val TEST_STATUS_CODE_SAMPLE_COMPLETED = 9.0 const val TEST_STATUS_CODE_SAMPLE_PRINT_STARTED = 10.0 const val TEST_STATUS_CODE_SAMPLE_PRINT_COMPLETED = 11.0 - const val TEST_STATUS_CODE_TEST_COMPLETED = 12.0 + const val TEST_STATUS_CODE_FIRST_GAIN_STARTED = 12.0 + const val TEST_STATUS_CODE_FIRST_GAIN_COMPLETED = 13.0 + const val TEST_STATUS_CODE_FIRST_GAIN_PRINT_STARTED = 14.0 + const val TEST_STATUS_CODE_FIRST_GAIN_PRINT_COMPLETED = 15.0 + const val TEST_STATUS_CODE_SECOND_GAIN_STARTED = 16.0 + const val TEST_STATUS_CODE_SECOND_GAIN_COMPLETED = 17.0 + const val TEST_STATUS_CODE_SECOND_GAIN_PRINT_STARTED = 18.0 + const val TEST_STATUS_CODE_SECOND_GAIN_PRINT_COMPLETED = 19.0 + const val TEST_STATUS_CODE_THIRD_GAIN_STARTED = 20.0 + const val TEST_STATUS_CODE_THIRD_GAIN_COMPLETED = 21.0 + const val TEST_STATUS_CODE_THIRD_GAIN_PRINT_STARTED = 22.0 + const val TEST_STATUS_CODE_THIRD_GAIN_PRINT_COMPLETED = 23.0 + const val TEST_STATUS_CODE_FORTH_GAIN_STARTED = 24.0 + const val TEST_STATUS_CODE_FORTH_GAIN_COMPLETED = 25.0 + const val TEST_STATUS_CODE_FORTH_GAIN_PRINT_STARTED = 26.0 + const val TEST_STATUS_CODE_FORTH_GAIN_PRINT_COMPLETED = 27.0 + const val TEST_STATUS_CODE_TEST_COMPLETED = 30.0 val DEVICE_CONFIGURATION: Map>> = mapOf>>( "HCV-000-3001" to listOf( @@ -156,21 +182,21 @@ object Constants { listOf(1.0, 0.0) // LED4 ), "HCV-000-3005" to listOf( - listOf(1.7272, -1.1977), // LED1, 435nm + listOf(1.3801, -0.372), // LED1, 435nm listOf(0.581462456, -0.58502), // LED2, 415nm - listOf(0.2304, -0.0145), // LED3, 555nm + listOf(0.4565, -0.0371), // LED3, 555nm listOf(1.0, 0.0) // LED4 ), "HCV-000-3006" to listOf( - listOf(1.7272, -1.1977), // LED1, 435nm + listOf(1.3801, -0.372), // LED1, 435nm listOf(0.581462456, -0.58502), // LED2, 415nm - listOf(0.2304, -0.0145), // LED3, 555nm + listOf(0.4565, -0.0371), // LED3, 555nm listOf(1.0, 0.0) // LED4 ), "HCV-000-3007" to listOf( - listOf(1.7272, -1.1977), // LED1, 435nm + listOf(1.8445, -0.8213), // LED1, 435nm listOf(0.581462456, -0.58502), // LED2, 415nm - listOf(0.2304, -0.0145), // LED3, 555nm + listOf(0.4613, -0.0831), // LED3, 555nm listOf(1.0, 0.0) // LED4 ), "HCV-000-3008" to listOf( @@ -191,6 +217,66 @@ object Constants { listOf(0.2304, -0.0145), // LED3, 555nm listOf(1.0, 0.0) // LED4 ), + "HCV-000-3011" to listOf( + listOf(1.7272, -1.1977), // LED1, 435nm + listOf(0.581462456, -0.58502), // LED2, 415nm + listOf(0.2304, -0.0145), // LED3, 555nm + listOf(1.0, 0.0) // LED4 + ), + "HCV-000-3012" to listOf( + listOf(1.7272, -1.1977), // LED1, 435nm + listOf(0.581462456, -0.58502), // LED2, 415nm + listOf(0.2304, -0.0145), // LED3, 555nm + listOf(1.0, 0.0) // LED4 + ), + "HCV-000-3013" to listOf( + listOf(1.7272, -1.1977), // LED1, 435nm + listOf(0.581462456, -0.58502), // LED2, 415nm + listOf(0.2304, -0.0145), // LED3, 555nm + listOf(1.0, 0.0) // LED4 + ), + "HCV-000-3014" to listOf( + listOf(1.7272, -1.1977), // LED1, 435nm + listOf(0.581462456, -0.58502), // LED2, 415nm + listOf(0.2304, -0.0145), // LED3, 555nm + listOf(1.0, 0.0) // LED4 + ), + "HCV-000-3015" to listOf( + listOf(1.7272, -1.1977), // LED1, 435nm + listOf(0.581462456, -0.58502), // LED2, 415nm + listOf(0.2304, -0.0145), // LED3, 555nm + listOf(1.0, 0.0) // LED4 + ), + "HCV-000-3016" to listOf( + listOf(1.7272, -1.1977), // LED1, 435nm + listOf(0.581462456, -0.58502), // LED2, 415nm + listOf(0.2304, -0.0145), // LED3, 555nm + listOf(1.0, 0.0) // LED4 + ), + "HCV-000-3017" to listOf( + listOf(1.7272, -1.1977), // LED1, 435nm + listOf(0.581462456, -0.58502), // LED2, 415nm + listOf(0.2304, -0.0145), // LED3, 555nm + listOf(1.0, 0.0) // LED4 + ), + "HCV-000-3018" to listOf( + listOf(1.7272, -1.1977), // LED1, 435nm + listOf(0.581462456, -0.58502), // LED2, 415nm + listOf(0.2304, -0.0145), // LED3, 555nm + listOf(1.0, 0.0) // LED4 + ), + "HCV-000-3019" to listOf( + listOf(1.7272, -1.1977), // LED1, 435nm + listOf(0.581462456, -0.58502), // LED2, 415nm + listOf(0.2304, -0.0145), // LED3, 555nm + listOf(1.0, 0.0) // LED4 + ), + "HCV-000-3020" to listOf( + listOf(1.7272, -1.1977), // LED1, 435nm + listOf(0.581462456, -0.58502), // LED2, 415nm + listOf(0.2304, -0.0145), // LED3, 555nm + listOf(1.0, 0.0) // LED4 + ), ) const val INCUBATION_TIME_MIN = 0 @@ -259,6 +345,66 @@ object Constants { listOf(24500, 26250), listOf(24500, 26250), listOf(24500, 26250), - ) + ), + "HCV-000-3011" to listOf( + listOf(24500, 26250), + listOf(24500, 26250), + listOf(24500, 26250), + listOf(24500, 26250), + ), + "HCV-000-3012" to listOf( + listOf(24500, 26250), + listOf(24500, 26250), + listOf(24500, 26250), + listOf(24500, 26250), + ), + "HCV-000-3013" to listOf( + listOf(24500, 26250), + listOf(24500, 26250), + listOf(24500, 26250), + listOf(24500, 26250), + ), + "HCV-000-3014" to listOf( + listOf(24500, 26250), + listOf(24500, 26250), + listOf(24500, 26250), + listOf(24500, 26250), + ), + "HCV-000-3015" to listOf( + listOf(24500, 26250), + listOf(24500, 26250), + listOf(24500, 26250), + listOf(24500, 26250), + ), + "HCV-000-3016" to listOf( + listOf(24500, 26250), + listOf(24500, 26250), + listOf(24500, 26250), + listOf(24500, 26250), + ), + "HCV-000-3017" to listOf( + listOf(24500, 26250), + listOf(24500, 26250), + listOf(24500, 26250), + listOf(24500, 26250), + ), + "HCV-000-3018" to listOf( + listOf(24500, 26250), + listOf(24500, 26250), + listOf(24500, 26250), + listOf(24500, 26250), + ), + "HCV-000-3019" to listOf( + listOf(24500, 26250), + listOf(24500, 26250), + listOf(24500, 26250), + listOf(24500, 26250), + ), + "HCV-000-3020" to listOf( + listOf(24500, 26250), + listOf(24500, 26250), + listOf(24500, 26250), + listOf(24500, 26250), + ), ) } \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/data/constant/HemoCubeCommands.kt b/app/src/main/java/com/example/hpostesting/data/constant/HemoCubeCommands.kt index 1a806e2..4e06388 100644 --- a/app/src/main/java/com/example/hpostesting/data/constant/HemoCubeCommands.kt +++ b/app/src/main/java/com/example/hpostesting/data/constant/HemoCubeCommands.kt @@ -4,6 +4,10 @@ enum class HemoCubeCommands(val command: String) { startBuffer("B\r"), runDiagnostics("D\r"), startSample("S\r"), - getSample("P\r"), + PRINT_COMMAND("P\r"), getDeviceId("I\r"), + FIRST_GAIN_COMMAND("T\r"), + SECOND_GAIN_COMMAND("U\r"), + THIRD_GAIN_COMMAND("V\r"), + FORTH_GAIN_COMMAND("W\r"), } \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/data/dao/HemoCubeBufferDao.kt b/app/src/main/java/com/example/hpostesting/data/dao/HemoCubeBufferDao.kt new file mode 100644 index 0000000..c6e0525 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/data/dao/HemoCubeBufferDao.kt @@ -0,0 +1,26 @@ +package com.example.hpostesting.data.dao + +import androidx.lifecycle.LiveData +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import com.example.hpostesting.data.model.patient.BufferCheckData + +@Dao +interface HemoCubeBufferDao { + @Query("SELECT * from hemo_cube_buffer_test_table") + fun getAll(): LiveData> + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertAll(bufferCheckData: BufferCheckData) + + @Query("SELECT * FROM hemo_cube_buffer_test_table WHERE _id = :id") + suspend fun getUserByID(id: String): BufferCheckData + + @Query("DELETE FROM hemo_cube_buffer_test_table WHERE _id = :id") + suspend fun deleteById(id: String) + + @Query("UPDATE hemo_cube_buffer_test_table SET localFlag = :newValue WHERE _id = :id") + suspend fun updateFieldById(id: String, newValue: Boolean) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/data/dao/MyDataBase.kt b/app/src/main/java/com/example/hpostesting/data/dao/MyDataBase.kt index b291c72..5110633 100644 --- a/app/src/main/java/com/example/hpostesting/data/dao/MyDataBase.kt +++ b/app/src/main/java/com/example/hpostesting/data/dao/MyDataBase.kt @@ -1,19 +1,17 @@ package com.example.hpostesting.data.dao -import android.content.Context import androidx.room.Database -import androidx.room.Room import androidx.room.RoomDatabase import androidx.room.TypeConverters +import com.example.hpostesting.data.model.patient.BufferCheckData import com.example.hpostesting.data.model.patient.DeviceData -import com.example.hpostesting.data.model.patient.UserData import com.example.hpostesting.data.model.patient.HemoCubeTestData -import com.google.android.datatransport.runtime.dagger.Provides -import javax.inject.Singleton +import com.example.hpostesting.data.model.patient.UserData -@Database(entities = [UserData::class, HemoCubeTestData::class, DeviceData::class], version = 14, exportSchema = false) +@Database(entities = [UserData::class, HemoCubeTestData::class, DeviceData::class], version = 17, exportSchema = false) @TypeConverters(Converters::class) abstract class MyDatabase : RoomDatabase() { abstract fun userDao(): UserDao abstract fun hemoCubeDao(): HemoCubeDao + abstract fun hemoCubeBufferDao(): HemoCubeBufferDao } \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/data/model/patient/BufferCheckData.kt b/app/src/main/java/com/example/hpostesting/data/model/patient/BufferCheckData.kt index 4f0c996..8345956 100644 --- a/app/src/main/java/com/example/hpostesting/data/model/patient/BufferCheckData.kt +++ b/app/src/main/java/com/example/hpostesting/data/model/patient/BufferCheckData.kt @@ -1,21 +1,47 @@ package com.example.hpostesting.data.model.patient +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "hemo_cube_buffer_test_table") data class BufferCheckData( + @PrimaryKey + var _id: String = "", var kitno: String = "", var deviceId: String? = "", var appVersion:String? = "", var deviceSerialNumber: String = "", var deviceType: String = "HEMOCUBE", + var localFlag: Boolean = false, var resultData: String = "", var led1Buffer: Double? = null, var led2Buffer: Double? = null, + var led3Buffer: Double? = null, + var led4Buffer: Double? = null, var led1Sample: Double? = null, var led2Sample: Double? = null, + var led3Sample: Double? = null, + var led4Sample: Double? = null, var led1Average: Double? = null, var led2Average: Double? = null, + var led3Average: Double? = null, + var led4Average: Double? = null, + var abs1: Double? = null, + var abs2: Double? = null, + var abs3: Double? = null, + var abs4: Double? = null, var deviceRatio: Double? = null, var calculatedRatio: Double? = null, + var predictedDenovixRatio: Double? = null, var coefficients: String? = "", var classificationResult: String = "", - var testTime: String = "" + var testTime: String = "", + var prdClassification: String = "", + var errorMessages: String = "", + var batteryLevel: String = "", + var batteryCapacity: String = "", + var batteryMaxCapacity: String = "", + var batteryTemperature: String = "", + var batteryVoltage: String = "", + var reportUploadTime: String? = "", ) \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/data/model/patient/HemoCubeTestData.kt b/app/src/main/java/com/example/hpostesting/data/model/patient/HemoCubeTestData.kt index 4c27cf4..4a200d4 100644 --- a/app/src/main/java/com/example/hpostesting/data/model/patient/HemoCubeTestData.kt +++ b/app/src/main/java/com/example/hpostesting/data/model/patient/HemoCubeTestData.kt @@ -45,12 +45,31 @@ data class HemoCubeTestData( var abs4: Double? = null, var hb3: Double? = null, var hb4: Double? = null, + var led1Gain1: Double? = null, + var led2Gain1: Double? = null, + var led3Gain1: Double? = null, + var led4Gain1: Double? = null, + var led1Gain2: Double? = null, + var led2Gain2: Double? = null, + var led3Gain2: Double? = null, + var led4Gain2: Double? = null, + var led1Gain3: Double? = null, + var led2Gain3: Double? = null, + var led3Gain3: Double? = null, + var led4Gain3: Double? = null, + var led1Gain4: Double? = null, + var led2Gain4: Double? = null, + var led3Gain4: Double? = null, + var led4Gain4: Double? = null, var deviceRatio: Double? = null, var calculatedRatio: Double? = null, var predictedDenovixRatio: Double? = null, + var slopeRatio: Double? = null, var coefficients: String? = "", var classificationResult: String = "", var prdClassification: String = "", + var deviceRatioClass: String = "", + var slopeRatioClass: String = "", var errorMessages: String = "", var batteryLevel: String = "", var batteryCapacity: String = "", diff --git a/app/src/main/java/com/example/hpostesting/domain/di/AppModule.kt b/app/src/main/java/com/example/hpostesting/domain/di/AppModule.kt index f3f21d9..2628f67 100644 --- a/app/src/main/java/com/example/hpostesting/domain/di/AppModule.kt +++ b/app/src/main/java/com/example/hpostesting/domain/di/AppModule.kt @@ -8,6 +8,7 @@ import androidx.room.Room import com.example.hpostesting.data.api.MolbioAuthApi import com.example.hpostesting.data.api.MolbioResultApi import com.example.hpostesting.data.api.PropertyProvider +import com.example.hpostesting.data.dao.HemoCubeBufferDao import com.example.hpostesting.data.dao.HemoCubeDao import com.example.hpostesting.data.dao.MyDatabase import com.example.hpostesting.data.dao.UserDao @@ -55,6 +56,12 @@ object AppModule { fun provideMyHemo(myDatabase: MyDatabase): HemoCubeDao { return myDatabase.hemoCubeDao() } + @Provides + @Singleton + fun provideMyHemoCubeBuffer(myDatabase: MyDatabase): HemoCubeBufferDao { + return myDatabase.hemoCubeBufferDao() + } + @Provides @Singleton diff --git a/app/src/main/java/com/example/hpostesting/presentation/adapter/UserListAdapter.kt b/app/src/main/java/com/example/hpostesting/presentation/adapter/UserListAdapter.kt index 54bb480..763be7b 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/adapter/UserListAdapter.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/adapter/UserListAdapter.kt @@ -214,13 +214,13 @@ class UserListAdapter( ).show() } else { if (model.incubationTime != "") { - if (isBetween15And30Minutes(model.incubationTime) < 15) { + if (isBetween15And30Minutes(model.incubationTime) < -15000) { Toast.makeText( view.context, context?.getString(R.string.incubation_not_completed), Toast.LENGTH_SHORT ).show() - } else if (isBetween15And30Minutes(model.incubationTime) > 30) { + } else if (isBetween15And30Minutes(model.incubationTime) > 300000) { Toast.makeText( view.context, context?.getString(R.string.incubation_crossed_30_minutes), diff --git a/app/src/main/java/com/example/hpostesting/presentation/buffercheck/HemoCubeBufferCheckFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/buffercheck/HemoCubeBufferCheckFragment.kt index 96ace8c..10d5764 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/buffercheck/HemoCubeBufferCheckFragment.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/buffercheck/HemoCubeBufferCheckFragment.kt @@ -1,36 +1,30 @@ package com.example.hpostesting.presentation.buffercheck import android.content.Context -import android.content.Intent import android.content.SharedPreferences import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.inputmethod.InputMethodManager import android.widget.Toast -import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.MutableLiveData -import com.example.hpostesting.data.DataHolder import com.example.hpostesting.data.constant.Constants import com.example.hpostesting.data.constant.HemoCubeCommands import com.example.hpostesting.data.model.patient.BufferCheckData import com.example.hpostesting.data.model.patient.DeviceData -import com.example.hpostesting.data.model.patient.toHemoCubeTestData import com.example.hpostesting.presentation.UsbServiceListener import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel -import com.example.hpostesting.presentation.utils.MyDialogListener -import com.example.hpostesting.presentation.utils.UIUtils import com.google.firebase.crashlytics.ktx.crashlytics import com.google.firebase.ktx.Firebase import `in`.sminnovations.hpostesting.R import `in`.sminnovations.hpostesting.databinding.FragmentHemoCubeReferenceBinding -import kotlin.math.log10 import java.text.SimpleDateFormat import java.util.Calendar import java.util.Locale +import java.util.UUID +import kotlin.math.log10 class HemoCubeBufferCheckFragment : Fragment() { @@ -41,9 +35,24 @@ class HemoCubeBufferCheckFragment : Fragment() { private var resultData: String = "" private var kitno: String = "" private var isOnline = false - private val messages = MutableLiveData() private var isTestOngoing = false private var startListening = MutableLiveData(false) + private var led1BufferForDevice = 0.0 + private var led2BufferForDevice = 0.0 + private var led3BufferForDevice = 0.0 + private var led4BufferForDevice = 0.0 + private var led1SampleForDevice = 0.0 + private var led2SampleForDevice = 0.0 + private var led3SampleForDevice = 0.0 + private var led4SampleForDevice = 0.0 + private var fittedAbs1 = 0.0 + private var fittedAbs2 = 0.0 + private var fittedAbs3 = 0.0 + private var fittedAbs4 = 0.0 + private var _predictedDenovixRatio = 0.0 + private var validationError = false + private var allErrorMessages = "" + private var deviceHardwareId = "" override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View { @@ -64,13 +73,14 @@ class HemoCubeBufferCheckFragment : Fragment() { binding.nameEditText.setText("SMI/SC/") binding.tvTitle.visibility = View.GONE binding.tvName.visibility = View.GONE + binding.btnPlacebuffer.visibility = View.GONE binding.btnGo.setOnClickListener { val serialNumber = binding.nameEditText.text.toString().trim() if (serialNumber.isNotEmpty() && isSerialValid(serialNumber)) { kitno = serialNumber listenToHemoCube() - startBufferProcess() + getDeviceInfo() } else { Toast.makeText(context, R.string.invalid_kit_number, Toast.LENGTH_LONG).show() return@setOnClickListener @@ -84,17 +94,17 @@ class HemoCubeBufferCheckFragment : Fragment() { } } - - - activity?.runOnUiThread { - binding.btnSubmit.isEnabled = true - binding.btnSubmit.isClickable = true - } - binding.btnSubmit.setOnClickListener { - + binding.btnSamplestart.setOnClickListener { + startSampleProcess() + it.visibility = View.GONE } binding.btnSubmit.isEnabled = false binding.btnSubmit.isClickable = false + + binding.btnPlacebuffer.setOnClickListener { + checkAndStartProcess() + it.visibility = View.GONE + } } private fun observeViewModel() { @@ -116,6 +126,10 @@ class HemoCubeBufferCheckFragment : Fragment() { if (result == "Success") { showToast(R.string.kit_uploaded) } + if (result == "Error") { + showToast("Error uploading data, Kit result stored locally") + startActivity(Intent(requireActivity(), DashboardActivity::class.java)) + } if (result == "Local") { showToast(R.string.kit_upload_failed) } @@ -123,27 +137,24 @@ class HemoCubeBufferCheckFragment : Fragment() { binding.progressBar.visibility = View.GONE } - messages.observe(viewLifecycleOwner) { + hemoCubeViewModel.messages.observe(viewLifecycleOwner) { binding.tvSubtitle4.text = it } } + private fun checkAndStartProcess() { + startBufferProcess() + } + private fun listenToHemoCube() { - if (DataHolder.hemoCubeTestData == null) { - DataHolder.hemoCubeTestData = DataHolder.selectedTest?.toHemoCubeTestData() - } - - val fullReadOutput = StringBuilder() startListening.postValue(true) - try { (activity as HemocubeBufferCheckActivity).mService.listenToHemoCube(object : UsbServiceListener { override fun onUsbRead(data: ByteArray?) { data?.let { val stringData = String(it) - fullReadOutput.append(stringData) - handleUsbData(stringData, fullReadOutput) + handleUsbData(stringData) } } @@ -156,97 +167,212 @@ class HemoCubeBufferCheckFragment : Fragment() { } } - private fun handleUsbData(stringData: String, fullReadOutput: StringBuilder) { + private fun handleUsbData(stringData: String) { if (stringData.contains("#")) { - messages.postValue(stringData) isTestOngoing = true } - resultData += fullReadOutput.toString() + resultData += stringData when { - stringData.contains("#Buffer Completed") -> showStartSampleDialog() - stringData.contains("#Sample Completed") -> { - activity?.runOnUiThread { - binding.btnSubmit.isEnabled = true - binding.btnSubmit.isClickable = true + stringData.contains("SN") -> { + val slData = stringData.split(" ") + if (slData.size > 1) { + val hardwareId = slData[1].trim() + deviceHardwareId = hardwareId + with(sharedPreferences.edit()) { + putString(Constants.DEVICE_ID, hardwareId) + apply() + } } + activity?.runOnUiThread { + binding.tvSubtitle4.visibility = View.VISIBLE + binding.btnPlacebuffer.visibility = View.VISIBLE + } + hemoCubeViewModel.messages.postValue("Start") + } + + stringData.contains("#BS") -> { + hemoCubeViewModel.messages.postValue("Buffer Started") + } + + stringData.contains("#BC") -> { + activity?.runOnUiThread { + binding.tvSubtitle4.text = "Buffer Completed" + binding.btnSamplestart.visibility = View.VISIBLE + } + } + + stringData.contains("#SS") -> { + activity?.runOnUiThread { + binding.tvSubtitle4.text = "Sample Started" + binding.btnSamplestart.visibility = View.GONE + } + } + + stringData.contains("#SC") -> { + hemoCubeViewModel.messages.postValue("Sample Completed \nGathering data") fetchResult() } - stringData.contains("RESULT") || resultData.contains("REND") -> { - var validString: String - val results: List - if (stringData.contains("RESULT")) { - validString = isValidResult(stringData) - if (validString.isEmpty()) { - results = resultData.split("\n") - validString = parseResult(results) - } - } else { - results = resultData.split("\n") - validString = parseResult(results) - } - if (validString.isNotEmpty()) { - handleValidResult(validString) - } - } - } - } - - private fun showStartSampleDialog() { - activity?.runOnUiThread { - UIUtils.createAlertDialog(requireContext(), - "Start Sample", - "Do you want to start sample reading?", - getString(R.string.no), - "Yes", - object : MyDialogListener { - override fun onClickNegativeButton() {} - - override fun onClickPositiveButton() { - listenToHemoCube() - startSampleProcess() - } - }) - } - } - - private fun handleValidResult(validString: String) { - try { - val result = validString.split(" ") - if (result.size == 8) { - val deviceId = result[2] - val led1BufferForDevice = result[3].toDoubleOrNull() - val led2BufferForDevice = result[4].toDoubleOrNull() - val led1Sample = result[5].toDoubleOrNull() - val led2Sample = result[6].toDoubleOrNull() - val led1Average = log10(led1BufferForDevice?.div(led1Sample!!) ?: 0.0) - val led2Average = log10(led2BufferForDevice?.div(led2Sample!!) ?: 0.0) - val deviceRatio = led1Average / led2Average - val calculateRatio = calculateRatio(deviceRatio) - val classificationResult = findResult(calculateRatio) - messages.postValue(classificationResult) - val bufferData = BufferCheckData( - deviceId= deviceId, - kitno = kitno, - led1Buffer = led1BufferForDevice, - led2Buffer = led2BufferForDevice, - led1Average = led1Average, - led2Average = led2Average, - led1Sample = led1Sample, - led2Sample = led2Sample, - deviceRatio = deviceRatio, - calculatedRatio = calculateRatio, - coefficients = currentDeviceData?.coefficients?.get(0).toString() + ", " + currentDeviceData?.coefficients?.get(1).toString(), - classificationResult = classificationResult, - deviceSerialNumber = sharedPreferences.getString(Constants.USER_ID, "").toString(), - testTime = SimpleDateFormat( - "yyyy-MM-dd HH:mm:ss", Locale.getDefault() - ).format(Calendar.getInstance().time) + resultData.contains("REND") -> { + hemoCubeViewModel.messages.postValue( + "Data collected \n" + " Processing data" ) - hemoCubeViewModel.uploadHemoCubeResultToDatabaseforbuffercheckN(bufferData) + val resultLines = resultData.split("\\s+(?=LB|LS)".toRegex()) + var bufferIntensity = resultLines[1].split(' ')[1].trim() + led1BufferForDevice = bufferIntensity.toDoubleOrNull()!! + bufferIntensity = resultLines[2].split(' ')[1].trim() + led2BufferForDevice = bufferIntensity.toDoubleOrNull()!! + bufferIntensity = resultLines[3].split(' ')[1].trim() + led3BufferForDevice = bufferIntensity.toDoubleOrNull()!! + bufferIntensity = resultLines[4].split(' ')[1].trim() + led4BufferForDevice = bufferIntensity.toDoubleOrNull()!! + led1SampleForDevice = resultLines[5].split(' ')[1].trim().toDoubleOrNull()!! + led2SampleForDevice = resultLines[6].split(' ')[1].trim().toDoubleOrNull()!! + led3SampleForDevice = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!! + led4SampleForDevice = + resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!! + processResult() } + } + } + + private fun processResult() { + try { + hemoCubeViewModel.messages.postValue("processing result") + val deviceLog = resultData + + val pInfo = requireActivity().packageManager.getPackageInfo( + requireActivity().packageName, 0 + ) + val version = pInfo.versionName + + val led1Average = log10(led1BufferForDevice.div(led1SampleForDevice)) + val led2Average = log10(led2BufferForDevice.div(led2SampleForDevice)) + val led3Average = log10(led3BufferForDevice.div(led3SampleForDevice)) + val led4Average = log10(led4BufferForDevice.div(led4SampleForDevice)) + val deviceRatio = led3Average / led1Average + + if (led1BufferForDevice < Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(0) + ?.get(0)!! || led2BufferForDevice < Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get( + 1 + ) + ?.get(0)!! || led3BufferForDevice < Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get( + 2 + ) + ?.get(0)!! || led4BufferForDevice < Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get( + 3 + )?.get(0)!! + ) { + validationError = true + allErrorMessages += "Error: Invalid Test. Improper buffer reading (low)" + activity?.runOnUiThread { + binding.errorMessage.text = + "Error: Invalid Test. Improper buffer reading (low)" + "\n" + binding.errorMessage.visibility = View.VISIBLE + } + } + + if (led1BufferForDevice > Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(0) + ?.get(1)!! || led2BufferForDevice > Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get( + 1 + ) + ?.get(1)!! || led3BufferForDevice > Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get( + 2 + ) + ?.get(1)!! || led4BufferForDevice > Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get( + 3 + )?.get(1)!! + ) { + validationError = true + allErrorMessages += "Error: Invalid Test. Improper buffer reading (high)" + "\n" + activity?.runOnUiThread { + binding.errorMessage.text = + "Error: Invalid Test. Improper buffer reading (high)" + binding.errorMessage.visibility = View.VISIBLE + } + } + + var gradient = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(0)?.get(0) + var constant = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(0)?.get(1) + fittedAbs1 = gradient?.times(led1Average)?.plus(constant!!)!! + + gradient = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(1)?.get(0) + constant = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(1)?.get(1) + fittedAbs2 = gradient?.times(led2Average)?.plus(constant!!)!! + + gradient = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(2)?.get(0) + constant = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(2)?.get(1) + fittedAbs3 = gradient?.times(led3Average)?.plus(constant!!)!! + + gradient = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(3)?.get(0) + constant = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(3)?.get(1) + fittedAbs4 = gradient?.times(led4Average)?.plus(constant!!)!! + + _predictedDenovixRatio = fittedAbs3.div(fittedAbs1) + + if (fittedAbs1 <= fittedAbs2) { + validationError = true + allErrorMessages += "Error: Invalid Test. Problem with de-oxygenation" + "\n" + activity?.runOnUiThread { + binding.errorMessage.text = "Error: Invalid Test. Problem with de-oxygenation" + binding.errorMessage.visibility = View.VISIBLE + } + } + + if (fittedAbs1 < 0 || fittedAbs2 < 0 || fittedAbs3 < 0 || fittedAbs4 < 0) { + validationError = true + activity?.runOnUiThread { + binding.errorMessage.text = "Error: Negative Abs. Redo Kit check Reading" + binding.errorMessage.visibility = View.VISIBLE + } + } + + val prdClassification = absorbanceBasedClassification(_predictedDenovixRatio) + hemoCubeViewModel.messages.postValue(prdClassification) + + val bufferData = BufferCheckData( + _id = UUID.randomUUID().toString(), + deviceId = deviceHardwareId, + kitno = kitno, + appVersion = version, + led1Buffer = led1BufferForDevice, + led2Buffer = led2BufferForDevice, + led3Buffer = led3BufferForDevice, + led4Buffer = led4BufferForDevice, + led1Sample = led1SampleForDevice, + led2Sample = led2SampleForDevice, + led3Sample = led3SampleForDevice, + led4Sample = led4SampleForDevice, + led1Average = led1Average, + led2Average = led2Average, + led3Average = led3Average, + led4Average = led4Average, + abs1 = fittedAbs1, + abs2 = fittedAbs2, + abs3 = fittedAbs3, + abs4 = fittedAbs4, + deviceRatio = deviceRatio, + resultData = deviceLog, + predictedDenovixRatio = _predictedDenovixRatio, + prdClassification = prdClassification, + errorMessages = allErrorMessages, + coefficients = currentDeviceData?.coefficients?.get(0) + .toString() + ", " + currentDeviceData?.coefficients?.get(1).toString(), + deviceSerialNumber = sharedPreferences.getString(Constants.USER_ID, "").toString(), + testTime = SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss", Locale.getDefault() + ).format(Calendar.getInstance().time), + batteryLevel = hemoCubeViewModel.getBatteryLevel().toString(), + batteryCapacity = hemoCubeViewModel.getBatteryCapacity(requireContext()).toString(), + batteryMaxCapacity = hemoCubeViewModel.getBatteryMaxCapacity(requireContext()) + .toString(), + batteryTemperature = hemoCubeViewModel.getBatteryTemperature().toString(), + batteryVoltage = hemoCubeViewModel.getBatteryVoltage(requireContext()).toString() + ) + + hemoCubeViewModel.uploadHemoCubeResultToDatabaseForBufferCheck(isOnline, bufferData) } catch (e: Exception) { Toast.makeText( requireContext(), "Error while processing device data", Toast.LENGTH_SHORT @@ -257,23 +383,44 @@ class HemoCubeBufferCheckFragment : Fragment() { private fun findResult(calculatedRatio: Double?): String { try { + hemoCubeViewModel.messages.postValue("result classification") if (calculatedRatio != null) { - if (calculatedRatio < 0.085) return "Kit Failed" - if (calculatedRatio in 0.085..0.155) return "Kit Passed" - if (calculatedRatio in 0.155..0.175) return "Kit Failed" - if (calculatedRatio in 0.175..0.22) return "Kit Failed" - if (calculatedRatio in 0.22..0.25) return "Kit Failed" - if (calculatedRatio in 0.25..0.35) return "Kit Failed" - if (calculatedRatio > 0.35) return "Kit Failed" + if (calculatedRatio < 0.05) return "Inconclusive. Very low Absorbance - Repeat test with Higher Blood Volume" + if (calculatedRatio in 0.05..0.155) return "Normal" + if (calculatedRatio in 0.155..0.175) return "Negative Borderline. Repeat Test" + if (calculatedRatio in 0.175..0.22) return "Sickle Cell Trait" + if (calculatedRatio in 0.22..0.25) return "Positive for Sickle Cell. HPLC for Confirmation" + if (calculatedRatio in 0.25..0.35) return "Sickle Cell Disease" + if (calculatedRatio > 0.35) return "Inconclusive. Repeat with test with lower volume of blood" } else { - return "NULL" + return "INVALID" } } catch (e: Exception) { showToast(R.string.error_classification) Firebase.crashlytics.recordException(e) return "ERROR" } - return "NULL" + return "INVALID" + } + + private fun absorbanceBasedClassification(predictedDenovixRatio: Double?): String { + try { + hemoCubeViewModel.messages.postValue("result classification") + if (predictedDenovixRatio != null) { + if (predictedDenovixRatio in 0.0..0.16) return "Kit Passed" + if (predictedDenovixRatio in 0.16..0.165) return "Kit Passed" + if (predictedDenovixRatio in 0.165..0.235) return "Kit Failed" + if (predictedDenovixRatio in 0.235..0.24) return "Kit Failed" + if (predictedDenovixRatio in 0.24..1.0) return "Kit Failed" + } else { + return "INVALID" + } + } catch (e: Exception) { + showToast("error while performing classification") + Firebase.crashlytics.recordException(e) + return "ERROR" + } + return "INVALID" } private fun showToast(messageResId: Int) { @@ -282,6 +429,9 @@ class HemoCubeBufferCheckFragment : Fragment() { private fun startBufferProcess() { + activity?.runOnUiThread { + binding.btnPlacebuffer.visibility = View.GONE + } (activity as HemocubeBufferCheckActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.startBuffer, object : UsbServiceListener { @@ -293,6 +443,9 @@ class HemoCubeBufferCheckFragment : Fragment() { } private fun startSampleProcess() { + activity?.runOnUiThread { + binding.btnPlacebuffer.visibility = View.GONE + } (activity as HemocubeBufferCheckActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.startSample, object : UsbServiceListener { @@ -303,9 +456,28 @@ class HemoCubeBufferCheckFragment : Fragment() { }) } + private fun getDeviceInfo() { + hemoCubeViewModel.progressBar.postValue(true) + (activity as HemocubeBufferCheckActivity).mService.sendAndListenToHemoCube( + HemoCubeCommands.getDeviceId, + object : UsbServiceListener { + override fun onUsbRead(data: ByteArray?) { + data?.let { + val stringData = String(it) + hemoCubeViewModel.messages.postValue(stringData) + binding.tvSubtitle4.text = stringData + } + } + + override fun onUsbError(e: Exception?) { + hemoCubeViewModel.progressBar.postValue(false) + } + }) + } + private fun fetchResult() { - (activity as HemocubeBufferCheckActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.getSample, + (activity as HemocubeBufferCheckActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.PRINT_COMMAND, object : UsbServiceListener { override fun onUsbRead(data: ByteArray?) {} @@ -320,21 +492,6 @@ class HemoCubeBufferCheckFragment : Fragment() { return coefficient1 * ratio + coefficient2 } - private fun parseResult(frames: List): String { - val lines = mutableListOf() - for (frame in frames.reversed()) { - if (frame.contains("REND") || frame.contains("RESULT")) lines += frame - if (frame.contains("RESULT")) { - break - } - } - val line = lines.reversed().joinToString("").trim() - if (line.contains("RESULT") && line.split(" ").size == 8) { - return line - } - return "" - } - private fun isSerialValid(s: String): Boolean { if (s.length != 17) { binding.nameEditText.error = getString(R.string.invalid_kit) @@ -342,13 +499,4 @@ class HemoCubeBufferCheckFragment : Fragment() { } return true } - - - private fun isValidResult(line: String): String { - return if (line.contains("RESULT") && line.split(" ").size == 8) { - line - } else { - "" - } - } } diff --git a/app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt index 788e69f..13a46ab 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt @@ -443,6 +443,12 @@ class HomeFragment : Fragment() { if (userDataList.any { !it.localFlag && it.testStatus == true }) View.VISIBLE else View.GONE binding.uploadData.visibility = uploadDataVisibility } + + hemoCubeViewModel.allKitTestData.observe(viewLifecycleOwner) {bufferData -> + val uploadDataVisibility = + if (bufferData.any { !it.localFlag}) View.VISIBLE else View.GONE + binding.uploadData.visibility = uploadDataVisibility + } } private fun showUploadDialog(context: Context) { @@ -487,6 +493,16 @@ class HomeFragment : Fragment() { } dialog.dismiss() } + + hemoCubeViewModel.allKitTestData.observe(viewLifecycleOwner) { kitDataList -> + kitDataList.forEach { userData -> + if (!userData.localFlag) { + userData.localFlag = true + hemoCubeViewModel.bulkAddResultKitTestToDb(userData) + } + } + dialog.dismiss() + } } private fun deleteIncompleteRegistrations(userDataList: List) { diff --git a/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeFragment.kt index d3eff69..fa0e9d8 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeFragment.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeFragment.kt @@ -28,7 +28,9 @@ import com.google.firebase.ktx.Firebase import com.google.firebase.perf.ktx.performance import `in`.sminnovations.hpostesting.R import `in`.sminnovations.hpostesting.databinding.FragmentHemoCubeReferenceBinding +import kotlin.math.abs import kotlin.math.log10 +import kotlin.math.round class HemoCubeFragment : Fragment() { @@ -39,6 +41,7 @@ class HemoCubeFragment : Fragment() { private var isOnline = false private var currentDeviceData: DeviceData? = null private var resultData: String = "" + private var currentResultData: String = "" private var isUsingExistingBuffer = false private var isTestOngoing = false private var startListening = MutableLiveData(false) @@ -269,7 +272,6 @@ class HemoCubeFragment : Fragment() { if (DataHolder.hemoCubeTestData == null) { DataHolder.hemoCubeTestData = DataHolder.selectedTest?.toHemoCubeTestData() } - hemoCubeViewModel.progressBar.postValue(true) val fullReadOutput = StringBuilder() @@ -281,7 +283,7 @@ class HemoCubeFragment : Fragment() { data?.let { val stringData = String(it) fullReadOutput.append(stringData) - handleUsbData(stringData, testStatusCode) + handleUsbData(stringData) } } @@ -314,13 +316,14 @@ class HemoCubeFragment : Fragment() { }) } - private fun handleUsbData(stringData: String, testStatusCode: Double) { + private fun handleUsbData(stringData: String) { if (stringData.contains("#")) { isTestOngoing = true } resultData += stringData - hemoCubeViewModel.deviceMessages.postValue(resultData) + currentResultData += stringData + hemoCubeViewModel.deviceMessages.postValue(currentResultData) when { resultData.contains("SNE") && this.testStatusCode < Constants.TEST_STATUS_CODE_CONFIG_COMPLETED -> { @@ -357,7 +360,7 @@ class HemoCubeFragment : Fragment() { } } - resultData.contains("#SS") && this.testStatusCode < Constants.TEST_STATUS_CODE_SAMPLE_STARTED -> { + resultData.contains("#SS1") && this.testStatusCode < Constants.TEST_STATUS_CODE_SAMPLE_STARTED -> { this.testStatusCode = Constants.TEST_STATUS_CODE_SAMPLE_STARTED activity?.runOnUiThread { binding.tvSubtitle4.text = getString(R.string.sample_started) @@ -365,19 +368,60 @@ class HemoCubeFragment : Fragment() { } } - resultData.contains("#SC") && this.testStatusCode < Constants.TEST_STATUS_CODE_SAMPLE_COMPLETED -> { + resultData.contains("#SC1") && this.testStatusCode < Constants.TEST_STATUS_CODE_SAMPLE_COMPLETED -> { this.testStatusCode = Constants.TEST_STATUS_CODE_SAMPLE_COMPLETED hemoCubeViewModel.messages.postValue( getString(R.string.sample_completed) + "\n" + getString(R.string.gathering_data)) fetchResult() + currentResultData = "" testingTrace.stop() } + resultData.contains("ovf") -> { + hemoCubeViewModel.messages.postValue( + getString(R.string.power_bank) + ) + } + + currentResultData.contains("#SS2") && this.testStatusCode < Constants.TEST_STATUS_CODE_FIRST_GAIN_STARTED -> { + this.testStatusCode = Constants.TEST_STATUS_CODE_FIRST_GAIN_STARTED + hemoCubeViewModel.messages.postValue("1.3X Gain Started") + } + + currentResultData.contains("#SC2") && this.testStatusCode < Constants.TEST_STATUS_CODE_FIRST_GAIN_COMPLETED -> { + this.testStatusCode = Constants.TEST_STATUS_CODE_FIRST_GAIN_COMPLETED + hemoCubeViewModel.messages.postValue("1.3X Gain Completed") + fetchResult() + } + + currentResultData.contains("#SS3") && this.testStatusCode < Constants.TEST_STATUS_CODE_SECOND_GAIN_STARTED -> { + this.testStatusCode = Constants.TEST_STATUS_CODE_SECOND_GAIN_STARTED + hemoCubeViewModel.messages.postValue("2X Gain Started") + } + + currentResultData.contains("#SC3") && this.testStatusCode < Constants.TEST_STATUS_CODE_SECOND_GAIN_COMPLETED -> { + this.testStatusCode = Constants.TEST_STATUS_CODE_SECOND_GAIN_COMPLETED + hemoCubeViewModel.messages.postValue("2X Gain Completed") + fetchResult() + } + + currentResultData.contains("#SS5") && this.testStatusCode < Constants.TEST_STATUS_CODE_FORTH_GAIN_STARTED -> { + this.testStatusCode = Constants.TEST_STATUS_CODE_FORTH_GAIN_STARTED + hemoCubeViewModel.messages.postValue("7.6X Gain Started") + } + + currentResultData.contains("#SC5") && this.testStatusCode < Constants.TEST_STATUS_CODE_FORTH_GAIN_COMPLETED -> { + this.testStatusCode = Constants.TEST_STATUS_CODE_FORTH_GAIN_COMPLETED + hemoCubeViewModel.messages.postValue("7.6X Gain Completed") + fetchResult() + } + resultData.contains("REND") && this.testStatusCode < Constants.TEST_STATUS_CODE_SAMPLE_PRINT_COMPLETED -> { this.testStatusCode = Constants.TEST_STATUS_CODE_SAMPLE_PRINT_COMPLETED hemoCubeViewModel.messages.postValue( getString(R.string.data_collected_processing_data) ) + val resultLines = resultData.split("\\s+(?=LB|LS)".toRegex()) var bufferIntensity = resultLines[1].split(' ')[1].trim() led1BufferForDevice = if (isUsingExistingBuffer) { @@ -414,6 +458,60 @@ class HemoCubeFragment : Fragment() { led4SampleForDevice = resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!! processResult() + sendFirstGainCommand() + currentResultData = "" + } + + currentResultData.contains("REND") && this.testStatusCode >= Constants.TEST_STATUS_CODE_FIRST_GAIN_COMPLETED && this.testStatusCode < Constants.TEST_STATUS_CODE_FIRST_GAIN_PRINT_COMPLETED -> { + this.testStatusCode = Constants.TEST_STATUS_CODE_FIRST_GAIN_PRINT_COMPLETED + hemoCubeViewModel.messages.postValue("1.3X gain data gathered") + + val resultLines = currentResultData.split("\\s+(?=LB|LS)".toRegex()) + DataHolder.hemoCubeTestData?.apply { + led1Gain1 = resultLines[5].split(' ')[1].trim().toDoubleOrNull()!! + led2Gain1 = resultLines[6].split(' ')[1].trim().toDoubleOrNull()!! + led3Gain1 = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!! + led4Gain1 = resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!! + } + + sendSecondGainCommand() + currentResultData = "" + } + + currentResultData.contains("REND") && this.testStatusCode >= Constants.TEST_STATUS_CODE_FIRST_GAIN_PRINT_COMPLETED && this.testStatusCode < Constants.TEST_STATUS_CODE_SECOND_GAIN_PRINT_COMPLETED -> { + this.testStatusCode = Constants.TEST_STATUS_CODE_SECOND_GAIN_PRINT_COMPLETED + hemoCubeViewModel.messages.postValue("2X gain data gathered") + + val resultLines = currentResultData.split("\\s+(?=LB|LS)".toRegex()) + DataHolder.hemoCubeTestData?.apply { + led1Gain2 = resultLines[5].split(' ')[1].trim().toDoubleOrNull()!! + led2Gain2 = resultLines[6].split(' ')[1].trim().toDoubleOrNull()!! + led3Gain2 = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!! + led4Gain2 = resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!! + } + + sendForthGainCommand() + currentResultData = "" + } + + currentResultData.contains("REND") && this.testStatusCode >= Constants.TEST_STATUS_CODE_SECOND_GAIN_PRINT_COMPLETED && this.testStatusCode < Constants.TEST_STATUS_CODE_FORTH_GAIN_PRINT_COMPLETED -> { + this.testStatusCode = Constants.TEST_STATUS_CODE_FORTH_GAIN_PRINT_COMPLETED + hemoCubeViewModel.messages.postValue("7.6X gain data gathered") + + val resultLines = currentResultData.split("\\s+(?=LB|LS)".toRegex()) + DataHolder.hemoCubeTestData?.apply { + led1Gain4 = resultLines[5].split(' ')[1].trim().toDoubleOrNull()!! + led2Gain4 = resultLines[6].split(' ')[1].trim().toDoubleOrNull()!! + led3Gain4 = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!! + led4Gain4 = resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!! + } + + activity?.runOnUiThread { + binding.btnSubmit.visibility = View.VISIBLE + binding.btnSubmit.isEnabled = true + binding.btnSubmit.isClickable = true + binding.clParent.setBackgroundColor(Color.parseColor("#edfffd")) + } } } } @@ -501,8 +599,6 @@ class HemoCubeFragment : Fragment() { binding.errorMessage.text = getString(R.string.error_improper_buffer_high) binding.errorMessage.visibility = View.VISIBLE - binding.btnSubmit.isEnabled = true - binding.btnSubmit.isClickable = true } } @@ -534,6 +630,10 @@ class HemoCubeFragment : Fragment() { _predictedDenovixRatio = fittedAbs3.div(fittedAbs1) + val slope = (led1Average - led2Average) / (435 - 415) + val _slopeRatio = abs(led3Average / slope) + val slopeClass = slopeRatioClassification(_slopeRatio) + if (fittedAbs1 <= fittedAbs2) { validationError = true allErrorMessages += "Error: Invalid Test. Problem with de-oxygenation" + "\n" @@ -551,6 +651,14 @@ class HemoCubeFragment : Fragment() { } } + if ((led1Average < 0.7 || led2Average < 0.7 || led3Average < 0.7 || led4Average < 0.7) && _slopeRatio > 35.0) { + validationError = true + activity?.runOnUiThread { + binding.errorMessage.text = "Severely Low Hb. Repeat test with 12 ul in 2 ml Buffer" + binding.errorMessage.visibility = View.VISIBLE + } + } + if (fittedAbs3 < 0.1) { validationError = true allErrorMessages += "Error: Low Hb. Repeat test" + "\n" @@ -584,11 +692,15 @@ class HemoCubeFragment : Fragment() { this.deviceRatio = deviceRatio this.calculatedRatio = calculateRatio(deviceRatio) this.predictedDenovixRatio = _predictedDenovixRatio + this.slopeRatio = _slopeRatio this.coefficients = currentDeviceData?.coefficients?.get(0) .toString() + ", " + currentDeviceData?.coefficients?.get(1).toString() this.classificationResult = findResult(calculatedRatio) this.prdClassification = absorbanceBasedClassification(predictedDenovixRatio) hemoCubeViewModel.messages.postValue("Hb3: $_hb3,\n Hb4: $_hb4") + this.deviceRatioClass = deviceRatioClassification(deviceRatio) + this.slopeRatioClass = slopeClass + hemoCubeViewModel.messages.postValue("Slope Ratio: ${round(slopeRatio!!)},\n Slope Class: ${this.slopeRatioClass}\n") this.errorMessages = allErrorMessages this.resultData = deviceLog this.batteryLevel = hemoCubeViewModel.getBatteryLevel().toString() @@ -612,12 +724,12 @@ class HemoCubeFragment : Fragment() { } // if (!validationError) { - activity?.runOnUiThread { - binding.btnSubmit.visibility = View.VISIBLE - binding.btnSubmit.isEnabled = true - binding.btnSubmit.isClickable = true - binding.clParent.setBackgroundColor(Color.parseColor("#edfffd")) - } +// activity?.runOnUiThread { +// binding.btnSubmit.visibility = View.VISIBLE +// binding.btnSubmit.isEnabled = true +// binding.btnSubmit.isClickable = true +// binding.clParent.setBackgroundColor(Color.parseColor("#edfffd")) +// } // } } catch (e: Exception) { Toast.makeText( @@ -667,6 +779,56 @@ class HemoCubeFragment : Fragment() { return getString(R.string.invalid) } + fun deviceRatioClassification(ratio: Double?): String { + try { + hemoCubeViewModel.messages.postValue("result classification") + if (ratio != null) { + if (ratio in 0.0..0.4) + return getString(R.string.normal) + if (ratio in 0.4..0.45) + return getString(R.string.negative_borderline) + if (ratio in 0.45..0.55) + return getString(R.string.sickle_cell_trait) + if (ratio in 0.55..0.575) + return getString(R.string.positive_for_sickle_cell) + if (ratio in 0.09..1.0) + return getString(R.string.sickle_cell_disease) + } else { + return getString(R.string.invalid) + } + } catch (e: Exception) { + showToast(R.string.error_classification) + Firebase.crashlytics.recordException(e) + return getString(R.string.error) + } + return getString(R.string.invalid) + } + + fun slopeRatioClassification(ratio: Double?): String { + try { + hemoCubeViewModel.messages.postValue("result classification") + if (ratio != null) { + if (ratio in 0.0..30.0) + return getString(R.string.normal) + if (ratio in 30.0..35.0) + return getString(R.string.negative_borderline) + if (ratio in 35.0..50.0) + return getString(R.string.sickle_cell_trait) + if (ratio in 50.0..60.0) + return getString(R.string.positive_for_sickle_cell) + if (ratio > 60.0) + return getString(R.string.sickle_cell_disease) + } else { + return getString(R.string.invalid) + } + } catch (e: Exception) { + showToast(R.string.error_classification) + Firebase.crashlytics.recordException(e) + return getString(R.string.error) + } + return getString(R.string.invalid) + } + private fun absorbanceBasedClassification(predictedDenovixRatio: Double?): String { try { hemoCubeViewModel.messages.postValue("result classification") @@ -736,10 +898,62 @@ class HemoCubeFragment : Fragment() { }) } + private fun sendFirstGainCommand() { + hemoCubeViewModel.progressBar.postValue(true) + + (activity as HemocubeActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.FIRST_GAIN_COMMAND, + object : UsbServiceListener { + override fun onUsbRead(data: ByteArray?) {} + + override fun onUsbError(e: Exception?) { + hemoCubeViewModel.progressBar.postValue(false) + } + }) + } + + private fun sendSecondGainCommand() { + hemoCubeViewModel.progressBar.postValue(true) + + (activity as HemocubeActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.SECOND_GAIN_COMMAND, + object : UsbServiceListener { + override fun onUsbRead(data: ByteArray?) {} + + override fun onUsbError(e: Exception?) { + hemoCubeViewModel.progressBar.postValue(false) + } + }) + } + + private fun sendThirdGainCommand() { + hemoCubeViewModel.progressBar.postValue(true) + + (activity as HemocubeActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.THIRD_GAIN_COMMAND, + object : UsbServiceListener { + override fun onUsbRead(data: ByteArray?) {} + + override fun onUsbError(e: Exception?) { + hemoCubeViewModel.progressBar.postValue(false) + } + }) + } + + private fun sendForthGainCommand() { + hemoCubeViewModel.progressBar.postValue(true) + + (activity as HemocubeActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.FORTH_GAIN_COMMAND, + object : UsbServiceListener { + override fun onUsbRead(data: ByteArray?) {} + + override fun onUsbError(e: Exception?) { + hemoCubeViewModel.progressBar.postValue(false) + } + }) + } + private fun fetchResult() { hemoCubeViewModel.progressBar.postValue(true) - (activity as HemocubeActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.getSample, + (activity as HemocubeActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.PRINT_COMMAND, object : UsbServiceListener { override fun onUsbRead(data: ByteArray?) {} @@ -760,4 +974,4 @@ class HemoCubeFragment : Fragment() { return true return false } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt b/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt index 5b3043a..4deb30f 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt @@ -14,6 +14,7 @@ import com.example.hpostesting.data.DataHolder import com.example.hpostesting.data.NetworkStatusLiveData import com.example.hpostesting.data.Result import com.example.hpostesting.data.constant.Constants +import com.example.hpostesting.data.dao.HemoCubeBufferDao import com.example.hpostesting.data.dao.HemoCubeDao import com.example.hpostesting.data.model.Response import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionRequest @@ -48,6 +49,7 @@ import javax.inject.Inject @HiltViewModel class HemoCubeViewModel @Inject constructor( private val hemoCubeDao: HemoCubeDao, + private val hemoCubeBufferDao: HemoCubeBufferDao, private val repository: Repository, private val databaseRepository: DatabaseRepository, private val logFileManager: LogFileManager, @@ -86,6 +88,18 @@ class HemoCubeViewModel @Inject constructor( val fireBaseUpload = MutableLiveData() val fireBaseBulkUpload = MutableLiveData() + // Get the device ID of the device you want to retrieve data for (e.g., the first device in the list) + + private val _networkStatusLiveData = NetworkStatusLiveData(context) + val allUserData = hemoCubeDao.getAll() + val allKitTestData = hemoCubeBufferDao.getAll() + val deviceData = MutableLiveData() + val networkStatusLiveData: LiveData + get() = _networkStatusLiveData + val deviceMessages = MutableLiveData() + val fireBaseUpload = MutableLiveData() + val fireBaseBulkUpload = MutableLiveData() + private val batteryStatus: Intent? = IntentFilter(Intent.ACTION_BATTERY_CHANGED).let { ifilter -> context.registerReceiver(null, ifilter) @@ -160,6 +174,162 @@ class HemoCubeViewModel @Inject constructor( multipartFile?.let { partFile -> databaseRepository.uploadLogs(partFile).let { result -> uploadLogs.postValue(result) + fun uploadHemoCubeResultToDatabase(isOnline: Boolean, testStatus: Boolean, kitSerial: String?) = + viewModelScope.launch { + if (kitSerial != null) { + testDetails?.kitSerial = kitSerial + } + testDetails?.testStatus = testStatus + + try { + if (isOnline) { + parseData() + addResultTestToDb() + } else { + parseData() + testDetails?.testTime = SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss", Locale.getDefault() + ).format(Calendar.getInstance().time) + hemoCubeDao.insertAll(testDetails!!) + fireBaseUpload.postValue("Local") + } + } catch (e: Exception) { + Log.e("Testdb", "Upload failed: ${e.message}") + } + } + + + fun uploadHemoCubeResultToDatabaseforbuffercheckN(bufferCheckData: BufferCheckData) = + viewModelScope.launch { + addResultTestToDbforbuffercheck(bufferCheckData) + } + + fun getDeviceData(deviceId: String?) = viewModelScope.launch { + deviceData.postValue(deviceId?.let { repository.getDeviceDataById(it) }) + } + + + fun uploadHemoCubeResultToDatabaseForBufferCheck( + isOnline: Boolean, + bufferCheckData: BufferCheckData, + ) = + viewModelScope.launch { + if (isOnline) { + try { + when (val response = + repository.addTestToDatabaseforBufferCheck(bufferCheckData)) { + is Response.Success -> { + Log.i("Testdb", "Data uploaded to Firestore successfully") + fireBaseUpload.postValue("Success") + bufferCheckData.localFlag = true + hemoCubeBufferDao.insertAll(bufferCheckData) + } + + is Response.Error -> { + Log.e("Testdb", "Error uploading data to Firestore: $response") + fireBaseUpload.postValue("Error") + bufferCheckData.localFlag = true + hemoCubeBufferDao.insertAll(bufferCheckData) + } + } + } catch (e: Exception) { + Log.e("Testdb", "Exception during data upload: ${e.message}") + fireBaseUpload.postValue("Error") + } + } else { + hemoCubeBufferDao.insertAll(bufferCheckData) + fireBaseUpload.postValue("Local") + } + } + + private fun parseData() { + testDetails?.deviceRatio = DataHolder.hemocubeResult + testDetails?.resultData = DataHolder.hemoCubeTestData?.resultData.toString() + testDetails?.location = DataHolder.location + testDetails?.testTime = SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss", Locale.getDefault() + ).format(Calendar.getInstance().time) + testDetails?.appVersion = DataHolder.hemoCubeTestData?.appVersion + testDetails?.deviceId = DataHolder.hemoCubeTestData?.deviceId + testDetails?.deviceSerialNumber = + sharedPreference.getString(Constants.USER_ID, "").toString() + testDetails?.kitSerial = sharedPreference.getString(Constants.KIT_NUMBER, "").toString() + testDetails?.led1Buffer = DataHolder.hemoCubeTestData?.led1Buffer + testDetails?.led2Buffer = DataHolder.hemoCubeTestData?.led2Buffer + testDetails?.led3Buffer = DataHolder.hemoCubeTestData?.led3Buffer + testDetails?.led4Buffer = DataHolder.hemoCubeTestData?.led4Buffer + testDetails?.led1Sample = DataHolder.hemoCubeTestData?.led1Sample + testDetails?.led2Sample = DataHolder.hemoCubeTestData?.led2Sample + testDetails?.led3Sample = DataHolder.hemoCubeTestData?.led3Sample + testDetails?.led4Sample = DataHolder.hemoCubeTestData?.led4Sample + testDetails?.led1Average = DataHolder.hemoCubeTestData?.led1Average + testDetails?.led2Average = DataHolder.hemoCubeTestData?.led2Average + testDetails?.led3Average = DataHolder.hemoCubeTestData?.led3Average + testDetails?.led4Average = DataHolder.hemoCubeTestData?.led4Average + testDetails?.abs1 = DataHolder.hemoCubeTestData?.abs1 + testDetails?.abs2 = DataHolder.hemoCubeTestData?.abs2 + testDetails?.abs3 = DataHolder.hemoCubeTestData?.abs3 + testDetails?.abs4 = DataHolder.hemoCubeTestData?.abs4 + testDetails?.deviceRatio = DataHolder.hemoCubeTestData?.deviceRatio + testDetails?.predictedDenovixRatio = DataHolder.hemoCubeTestData?.predictedDenovixRatio + testDetails?.calculatedRatio = DataHolder.hemoCubeTestData?.calculatedRatio + testDetails?.coefficients = DataHolder.hemoCubeTestData?.coefficients + testDetails?.classificationResult = DataHolder.hemoCubeTestData?.classificationResult!! + testDetails?.prdClassification = DataHolder.hemoCubeTestData?.prdClassification.toString() + testDetails?.errorMessages = DataHolder.hemoCubeTestData?.errorMessages.toString() + testDetails?.batteryLevel = DataHolder.hemoCubeTestData?.batteryLevel.toString() + testDetails?.batteryCapacity = DataHolder.hemoCubeTestData?.batteryCapacity.toString() + testDetails?.batteryMaxCapacity = DataHolder.hemoCubeTestData?.batteryMaxCapacity.toString() + testDetails?.batteryTemperature = DataHolder.hemoCubeTestData?.batteryTemperature.toString() + testDetails?.batteryVoltage = DataHolder.hemoCubeTestData?.batteryVoltage.toString() + } + + private fun addResultTestToDb() { + viewModelScope.launch { + try { + testDetails!!.reportUploadTime = SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss", Locale.getDefault() + ).format(Calendar.getInstance().time) + + when (val response = repository.addTestToDatabase(testDetails)) { + is Response.Success -> { + val kitCount = sharedPreference.getInt(Constants.KIT_COUNT, 0) + with(sharedPreference.edit()) { + putInt(Constants.KIT_COUNT, kitCount.plus(1)) + apply() + } + Log.i("Testdb", "Data uploaded to Firestore successfully") + fireBaseUpload.postValue("Success") + testDetails.localFlag = true + hemoCubeDao.insertAll(testDetails) + } + + is Response.Error -> { + Log.e("Testdb", "Error uploading data to Firestore: $response") + fireBaseUpload.postValue("Error") + hemoCubeDao.insertAll(testDetails) + } + } + } catch (e: Exception) { + Log.e("Testdb", "Exception during data upload: ${e.message}") + fireBaseUpload.postValue("Error") + } + } + } + + fun bulkAddResultKitTestToDb(bufferCheckData: BufferCheckData) { + viewModelScope.launch { + bufferCheckData.reportUploadTime = SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss", Locale.getDefault() + ).format(Calendar.getInstance().time) + when (repository.addTestToDatabaseforBufferCheck(bufferCheckData)) { + is Response.Success -> { + fireBaseBulkUpload.postValue("Success") + updateBufferLocalFlag(bufferCheckData._id) + } + + else -> { + fireBaseBulkUpload.postValue("Error") } } } @@ -313,6 +483,79 @@ class HemoCubeViewModel @Inject constructor( val batteryPct: Float? = batteryStatus?.let { intent -> val level: Int = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) val scale: Int = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1) + private fun addResultTestToDbforbuffercheck(bufferCheckData: BufferCheckData) { + viewModelScope.launch { + try { + + when (val response = + repository.addTestToDatabaseforBufferCheck(bufferCheckData)) { + is Response.Success -> { + Log.i("Testdb", "Data uploaded to Firestore successfully") + fireBaseUpload.postValue("Success") + } + + is Response.Error -> { + Log.e("Testdb", "Error uploading data to Firestore: $response") + fireBaseUpload.postValue("Error") + } + } + } catch (e: Exception) { + Log.e("Testdb", "Exception during data upload: ${e.message}") + fireBaseUpload.postValue("Error") + } + } + } + + private fun updateBufferLocalFlag(bufferId: String) = + viewModelScope.launch { + hemoCubeBufferDao.updateFieldById(id = bufferId, true) + } + + + fun bulkAddResultTestToDb(userData: HemoCubeTestData) { + viewModelScope.launch { + userData.reportUploadTime = SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss", Locale.getDefault() + ).format(Calendar.getInstance().time) + when (repository.addTestToDatabase(userData)) { + is Response.Success -> { + fireBaseBulkUpload.postValue("Success") + updateLocalFlag(userData._id) + } + + else -> { + fireBaseBulkUpload.postValue("Error") + } + } + } + } + + private fun updateLocalFlag(userId: String) = + viewModelScope.launch { + hemoCubeDao.updateFieldById(id = userId, true) + } + + fun addUser(userData: HemoCubeTestData) = + viewModelScope.launch { + hemoCubeDao.insertAll(userData) + } + + fun deleteById(userId: String) = viewModelScope.launch { + hemoCubeDao.deleteById(id = userId) + } + + fun getBatteryLevel(): Float? { + val batteryPct: Float? = batteryStatus?.let { intent -> + val level: Int = + intent.getIntExtra( + BatteryManager.EXTRA_LEVEL, + -1 + ) + val scale: Int = + intent.getIntExtra( + BatteryManager.EXTRA_SCALE, + -1 + ) level * 100 / scale.toFloat() } diff --git a/app/src/main/res/layout/fragment_hemo_cube_reference.xml b/app/src/main/res/layout/fragment_hemo_cube_reference.xml index 9b166f9..31e59be 100644 --- a/app/src/main/res/layout/fragment_hemo_cube_reference.xml +++ b/app/src/main/res/layout/fragment_hemo_cube_reference.xml @@ -205,20 +205,25 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/btn_submit" /> - + app:layout_constraintTop_toBottomOf="@id/error_message"> + + +