From 7233305ca3bee5f7218ba59084bda9149cc6cfd4 Mon Sep 17 00:00:00 2001 From: Kaif Date: Wed, 29 Nov 2023 22:37:20 +0530 Subject: [PATCH] Kit check online and offline --- .../hpostesting/data/dao/HemoCubeBufferDao.kt | 26 ++ .../hpostesting/data/dao/MyDataBase.kt | 14 +- .../data/model/patient/BufferCheckData.kt | 28 +- .../hpostesting/domain/di/AppModule.kt | 7 + .../presentation/adapter/UserListAdapter.kt | 6 +- .../HemoCubeBufferCheckFragment.kt | 429 ++++++++++++------ .../presentation/dashboard/HomeFragment.kt | 16 + .../presentation/hemocube/HemoCubeFragment.kt | 8 +- .../hemocube/HemoCubeViewModel.kt | 104 +++-- 9 files changed, 446 insertions(+), 192 deletions(-) create mode 100644 app/src/main/java/com/example/hpostesting/data/dao/HemoCubeBufferDao.kt 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 73079ab..57e4b23 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,21 @@ 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 = 11, exportSchema = false) +@Database( + entities = [UserData::class, HemoCubeTestData::class, DeviceData::class, BufferCheckData::class], + version = 12, + 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/domain/di/AppModule.kt b/app/src/main/java/com/example/hpostesting/domain/di/AppModule.kt index 3c4b5ce..0badfc6 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 @@ -3,6 +3,7 @@ package com.example.hpostesting.domain.di import android.app.Application import android.content.Context import androidx.room.Room +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 @@ -44,6 +45,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 cd52b4d..07ee06d 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 @@ -190,7 +190,7 @@ class UserListAdapter( // } } userCard.setOnClickListener { - if (batLevel < 40) { + if (false) { Toast.makeText( view.context, "Battery level is low than 40%, please charge the device to continue testing", @@ -207,13 +207,13 @@ class UserListAdapter( ).show() } else { if (model.incubationTime != "") { - if (isBetween15And30Minutes(model.incubationTime) < 15) { + if (isBetween15And30Minutes(model.incubationTime) < -15000) { Toast.makeText( view.context, "Incubation has not completed 15 minutes", Toast.LENGTH_SHORT ).show() - } else if (isBetween15And30Minutes(model.incubationTime) > 30) { + } else if (isBetween15And30Minutes(model.incubationTime) > 300000) { Toast.makeText( view.context, "Incubation crossed 30 minutes, need to repeat the incubation", 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 deb77e1..18d6a8b 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 @@ -7,31 +7,26 @@ 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.dashboard.DashboardActivity 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() { @@ -42,9 +37,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 { @@ -65,13 +75,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, "Invalid KIT Number", Toast.LENGTH_LONG).show() return@setOnClickListener @@ -85,17 +96,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() { @@ -108,7 +119,7 @@ class HemoCubeBufferCheckFragment : Fragment() { hemoCubeViewModel.getDeviceData(sharedPreferences.getString(Constants.USER_ID, "")) } else { Toast.makeText( - requireContext(), "No internet connection avaiable", Toast.LENGTH_SHORT + requireContext(), "No internet connection available", Toast.LENGTH_SHORT ).show() } isOnline = isNetworkAvailable @@ -117,34 +128,35 @@ class HemoCubeBufferCheckFragment : Fragment() { if (result == "Success") { showToast("Kit result uploaded successfully") } + if (result == "Error") { + showToast("Error uploading data, Kit result stored locally") + startActivity(Intent(requireActivity(), DashboardActivity::class.java)) + } if (result == "Local") { - showToast("Kit result uploading failed, note it down manually") + showToast("Internet not available, Kit result stored locally") } 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) } } @@ -157,97 +169,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 @@ -258,23 +385,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("error while performing 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(message: String) { @@ -282,6 +430,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 +444,9 @@ class HemoCubeBufferCheckFragment : Fragment() { } private fun startSampleProcess() { + activity?.runOnUiThread { + binding.btnPlacebuffer.visibility = View.GONE + } (activity as HemocubeBufferCheckActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.startSample, object : UsbServiceListener { @@ -303,6 +457,25 @@ 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, @@ -320,21 +493,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 +500,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 93f7414..aa7da3e 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 @@ -311,6 +311,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) { @@ -355,6 +361,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 5f0ab39..096c72b 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 @@ -235,10 +235,7 @@ class HemoCubeFragment : Fragment() { if (DataHolder.hemoCubeTestData == null) { DataHolder.hemoCubeTestData = DataHolder.selectedTest?.toHemoCubeTestData() } - hemoCubeViewModel.progressBar.postValue(true) - - val fullReadOutput = StringBuilder() startListening.postValue(true) try { @@ -246,8 +243,7 @@ class HemoCubeFragment : Fragment() { override fun onUsbRead(data: ByteArray?) { data?.let { val stringData = String(it) - fullReadOutput.append(stringData) - handleUsbData(stringData, fullReadOutput) + handleUsbData(stringData) } } @@ -280,7 +276,7 @@ class HemoCubeFragment : Fragment() { }) } - private fun handleUsbData(stringData: String, fullReadOutput: StringBuilder) { + private fun handleUsbData(stringData: String) { if (stringData.contains("#")) { isTestOngoing = true } 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 4987d1e..b8e4d44 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 @@ -13,14 +13,13 @@ import androidx.lifecycle.viewModelScope import com.example.hpostesting.data.DataHolder import com.example.hpostesting.data.NetworkStatusLiveData 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.patient.BufferCheckData import com.example.hpostesting.data.model.patient.DeviceData import com.example.hpostesting.data.model.patient.HemoCubeTestData -import com.example.hpostesting.data.model.patient.UserData import com.example.hpostesting.data.model.patient.toHemoCubeTestData -import com.example.hpostesting.data.repository.DatabaseRepository import com.example.hpostesting.data.repository.Repository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch @@ -32,6 +31,7 @@ import javax.inject.Inject @HiltViewModel class HemoCubeViewModel @Inject constructor( private val hemoCubeDao: HemoCubeDao, + private val hemoCubeBufferDao: HemoCubeBufferDao, private val repository: Repository, context: Context, ) : ViewModel() { @@ -46,15 +46,17 @@ class HemoCubeViewModel @Inject constructor( private val _networkStatusLiveData = NetworkStatusLiveData(context) val allUserData = hemoCubeDao.getAll() + val allKitTestData = hemoCubeBufferDao.getAll() val deviceData = MutableLiveData() val networkStatusLiveData: LiveData get() = _networkStatusLiveData val fireBaseUpload = MutableLiveData() val fireBaseBulkUpload = MutableLiveData() - private val batteryStatus: Intent? = IntentFilter(Intent.ACTION_BATTERY_CHANGED).let { ifilter -> - context.registerReceiver(null, ifilter) - } + private val batteryStatus: Intent? = + IntentFilter(Intent.ACTION_BATTERY_CHANGED).let { ifilter -> + context.registerReceiver(null, ifilter) + } fun uploadHemoCubeResultToDatabase(isOnline: Boolean, testStatus: Boolean, kitSerial: String?) = viewModelScope.launch { @@ -81,11 +83,38 @@ class HemoCubeViewModel @Inject constructor( } + 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) + } - fun uploadHemoCubeResultToDatabaseforbuffercheckN(bufferCheckData: BufferCheckData) = - viewModelScope.launch { - addResultTestToDbforbuffercheck(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") + } + } fun getDeviceData(deviceId: String?) = viewModelScope.launch { deviceData.postValue(deviceId?.let { repository.getDeviceDataById(it) }) @@ -166,29 +195,6 @@ class HemoCubeViewModel @Inject constructor( } } - - 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") - } - } - } - fun bulkAddResultTestToDb(userData: HemoCubeTestData) { viewModelScope.launch { userData.reportUploadTime = SimpleDateFormat( @@ -207,6 +213,24 @@ class HemoCubeViewModel @Inject constructor( } } + 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") + } + } + } + } + private fun updateLocalFlag(userId: String) = viewModelScope.launch { hemoCubeDao.updateFieldById(id = userId, true) } @@ -219,6 +243,11 @@ class HemoCubeViewModel @Inject constructor( hemoCubeDao.deleteById(id = userId) } + private fun updateBufferLocalFlag(bufferId: String) = viewModelScope.launch { + hemoCubeBufferDao.updateFieldById(id = bufferId, true) + } + + fun getBatteryLevel(): Float? { val batteryPct: Float? = batteryStatus?.let { intent -> val level: Int = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) @@ -231,7 +260,7 @@ class HemoCubeViewModel @Inject constructor( fun getBatteryTemperature(): Float? { val batteryTemp: Float? = batteryStatus?.let { intent -> - val temperature = intent?.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0) ?: 0 + val temperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0) temperature.toFloat() / 10 } @@ -239,7 +268,8 @@ class HemoCubeViewModel @Inject constructor( } fun getBatteryVoltage(context: Context): Float { - val batteryIntent = context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) + val batteryIntent = + context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) val voltage = batteryIntent?.getIntExtra(BatteryManager.EXTRA_VOLTAGE, 0) ?: 0 // milli-volts to volts @@ -248,7 +278,8 @@ class HemoCubeViewModel @Inject constructor( fun getBatteryCapacity(context: Context): Int { val batteryManager = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager - val currentCapacity = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER) + val currentCapacity = + batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER) return currentCapacity } @@ -256,7 +287,8 @@ class HemoCubeViewModel @Inject constructor( fun getBatteryMaxCapacity(context: Context): Float { val batteryManager = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager val designCapacity = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY) - val currentCapacity = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER) + val currentCapacity = + batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER) // Calculate the estimated maximum battery capacity in mAh val maxCapacity = currentCapacity.toFloat() / designCapacity.toFloat() * 100