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 bcef509..3212e10 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 @@ -74,4 +74,40 @@ object Constants { const val BUFFER_VALUE_3 = "BufferValue3" const val BUFFER_VALUE_4 = "BufferValue4" const val DEVICE_ID = "DEVICE_ID" + + const val BUFFER_LED_LOWER_BOUND = 21000 + const val BUFFER_LED_UPPER_BOUND = 24500 + + val DEVICE_CONFIGURATION: Map>> = mapOf>>( + "HCV-000-3001" to listOf( + listOf(1.264817, -0.85965), // LED1, 435nm + listOf(0.581462456, -0.58502), // LED2, 415nm + listOf(0.184991, 0.001704), // LED3, 555nm + listOf(1.0, 0.0) // LED4 + ), + "HCV-000-3002" to listOf( + listOf(1.264817, -0.85965), + listOf(0.581462456, -0.58502), + listOf(0.184991, 0.001704), + listOf(1.0, 0.0) + ), + ) + + const val INCUBATION_TIME_MIN = 15 + const val INCUBATION_TIME_MAX = 30 + + val BUFFER_INTENSITY_THRESHOLDS: Map>> = mapOf>>( + "HCV-000-3001" to listOf( + listOf(24000, 24500), + listOf(21000, 24500), + listOf(21000, 24500), + listOf(21000, 24500), + ), + "HCV-000-3002" to listOf( + listOf(21000, 24500), + listOf(21000, 24500), + listOf(21000, 24500), + listOf(21000, 24500), + ) + ) } \ No newline at end of file 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 10932d3..d392a37 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 @@ -5,7 +5,6 @@ import android.content.Intent import android.content.SharedPreferences import android.graphics.Color import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -20,7 +19,6 @@ 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.diagnostics.DiagnosticsActivity import com.example.hpostesting.presentation.utils.MyDialogListener import com.example.hpostesting.presentation.utils.UIUtils import com.google.firebase.crashlytics.ktx.crashlytics @@ -35,7 +33,7 @@ class HemoCubeFragment : Fragment() { private lateinit var binding: FragmentHemoCubeReferenceBinding private val hemoCubeViewModel: HemoCubeViewModel by activityViewModels() private lateinit var sharedPreferences: SharedPreferences - val testDetails = DataHolder.selectedTest?.toHemoCubeTestData() + private val testDetails = DataHolder.selectedTest?.toHemoCubeTestData() private var isOnline = false private var currentDeviceData: DeviceData? = null private var resultData: String = "" @@ -45,12 +43,19 @@ class HemoCubeFragment : Fragment() { private val testingTrace = Firebase.performance.newTrace("testing_trace") private var led1BufferForDevice = 0.0 private var led2BufferForDevice = 0.0 - private var led3BufferForDevice = 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 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 deviceHardwareId = "" override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, @@ -84,8 +89,8 @@ class HemoCubeFragment : Fragment() { binding.nameEditText.visibility = View.GONE binding.tvTitle.visibility = View.GONE binding.btnGo.visibility = View.GONE - binding.btnSubmit.isEnabled = true - binding.btnSubmit.isClickable = true + binding.btnSubmit.isEnabled = false + binding.btnSubmit.isClickable = false binding.btnPlacebuffer.visibility = View.GONE binding.tvName.text = "Name: ${testDetails?.name}\n ID: ${testDetails?._id}" @@ -94,10 +99,10 @@ class HemoCubeFragment : Fragment() { binding.btnSamplestart.setOnClickListener { activity?.runOnUiThread { binding.tvSubtitle4.visibility = View.VISIBLE - binding.tvSubtitle4.text = "Sample Started" +// binding.tvSubtitle4.text = "Sample Started" } - startSampleProcess() - it.visibility = View.GONE + startSampleProcess() + it.visibility = View.GONE } binding.btnPlacebuffer.setOnClickListener { @@ -106,10 +111,13 @@ class HemoCubeFragment : Fragment() { } else { activity?.runOnUiThread { binding.tvSubtitle4.visibility = View.VISIBLE +// binding.tvSubtitle4.text = "Buffer Started" } + checkAndStartProcess() + it.visibility = View.GONE } - it.visibility = View.GONE + } } @@ -118,18 +126,11 @@ class HemoCubeFragment : Fragment() { hemoCubeViewModel.fireBaseUpload.observe(viewLifecycleOwner) { result -> if (result == "Success") { showToast("Test Results Uploaded Successfully") - - val digitalCardFragment = DigitalCardFragment() - val transaction = parentFragmentManager.beginTransaction() - transaction.replace(R.id.fragment_container, digitalCardFragment) - transaction.addToBackStack(null) - transaction.commit() - -// val i = Intent( -// requireContext().applicationContext, DashboardActivity::class.java -// ) -// startActivity(i) -// (activity as HemocubeActivity).finish() + val i = Intent( + requireContext().applicationContext, DashboardActivity::class.java + ) + startActivity(i) + (activity as HemocubeActivity).finish() } if (result == "Local") { showToast("Internet not available, test details stored locally") @@ -290,6 +291,7 @@ class HemoCubeFragment : Fragment() { val slData = stringData.split(" ") if (slData.size > 1) { val hardwareId = slData[1].trim() + deviceHardwareId = hardwareId with(sharedPreferences.edit()) { putString(Constants.DEVICE_ID, hardwareId) apply() @@ -320,12 +322,16 @@ class HemoCubeFragment : Fragment() { } stringData.contains("#SC") -> { - binding.tvSubtitle4.text = "Sample Completed" + hemoCubeViewModel.messages.postValue("Sample Completed \nGathering data") fetchResult() testingTrace.stop() } resultData.contains("REND") -> { + hemoCubeViewModel.messages.postValue( + "Data collected \n" + + " Processing data" + ) val resultLines = resultData.split("\\s+(?=LB|LS)".toRegex()) var bufferIntensity = resultLines[1].split(' ')[1].trim() led1BufferForDevice = if (isUsingExistingBuffer) { @@ -359,7 +365,8 @@ class HemoCubeFragment : Fragment() { sampleIntensity = resultLines[7].split(' ')[1].trim() led3SampleForDevice = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!! sampleIntensity = resultLines[8].split(' ')[1].split('\r')[0].trim() - led4SampleForDevice = resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!! + led4SampleForDevice = + resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!! processResult() } } @@ -396,6 +403,7 @@ class HemoCubeFragment : Fragment() { private fun processResult() { try { + hemoCubeViewModel.messages.postValue("processing result") val deviceLog = resultData val pInfo = requireActivity().packageManager.getPackageInfo( @@ -407,11 +415,63 @@ class HemoCubeFragment : Fragment() { val led2Average = log10(led2BufferForDevice?.div(led2SampleForDevice!!) ?: 0.0) val led3Average = log10(led3BufferForDevice?.div(led3SampleForDevice!!) ?: 0.0) val led4Average = log10(led4BufferForDevice?.div(led4SampleForDevice!!) ?: 0.0) - val deviceRatio = led1Average / led3Average + val deviceRatio = led3Average / led1Average - if (led1Average < 0 || led2Average < 0) { + 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 activity?.runOnUiThread { - binding.errorMessage.text = "Warning: Negative Abs. Retake Blank Reading" + binding.errorMessage.text = "Error: Invalid Test. Improper buffer reading (low)" + 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 + 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 + 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. Retake Blank Reading" binding.errorMessage.visibility = View.VISIBLE } } @@ -431,28 +491,43 @@ class HemoCubeFragment : Fragment() { this.led2Average = led2Average this.led3Average = led3Average this.led4Average = led4Average + this.abs1 = fittedAbs1 + this.abs2 = fittedAbs2 + this.abs3 = fittedAbs3 + this.abs4 = fittedAbs4 this.deviceRatio = deviceRatio this.calculatedRatio = calculateRatio(deviceRatio) + this.predictedDenovixRatio = _predictedDenovixRatio this.coefficients = currentDeviceData?.coefficients?.get(0) .toString() + ", " + currentDeviceData?.coefficients?.get(1).toString() this.classificationResult = findResult(calculatedRatio) - hemoCubeViewModel.messages.postValue(this.classificationResult) + this.prdClassification = absorbanceBasedClassification(predictedDenovixRatio) + hemoCubeViewModel.messages.postValue(this.prdClassification) this.resultData = deviceLog - if (!isUsingExistingBuffer) { - with(sharedPreferences.edit()) { - putString(Constants.BUFFER_VALUE_1, led1BufferForDevice.toString()) - putString(Constants.BUFFER_VALUE_2, led2BufferForDevice.toString()) - putString(Constants.BUFFER_VALUE_3, led3BufferForDevice.toString()) - putString(Constants.BUFFER_VALUE_4, led4BufferForDevice.toString()) - apply() - } + this.batteryLevel = hemoCubeViewModel.getBatteryLevel().toString() + this.batteryCapacity = hemoCubeViewModel.getBatteryCapacity(requireContext()).toString() + this.batteryMaxCapacity = hemoCubeViewModel.getBatteryMaxCapacity(requireContext()).toString() + this.batteryTemperature = hemoCubeViewModel.getBatteryTemperature().toString() + this.batteryVoltage = hemoCubeViewModel.getBatteryVoltage(requireContext()).toString() + } + + if (!isUsingExistingBuffer) { + with(sharedPreferences.edit()) { + putString(Constants.BUFFER_VALUE_1, led1BufferForDevice.toString()) + putString(Constants.BUFFER_VALUE_2, led2BufferForDevice.toString()) + putString(Constants.BUFFER_VALUE_3, led3BufferForDevice.toString()) + putString(Constants.BUFFER_VALUE_4, led4BufferForDevice.toString()) + apply() } - activity?.runOnUiThread { - binding.btnSubmit.visibility = View.VISIBLE - } + } + + if (!validationError) { + 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( @@ -464,8 +539,9 @@ class HemoCubeFragment : Fragment() { } } - private fun findResult(calculatedRatio: Double?): String { + fun findResult(calculatedRatio: Double?): String { try { + hemoCubeViewModel.messages.postValue("result classification") if (calculatedRatio != null) { if (calculatedRatio < 0.05) return "Inconclusive. Very low Absorbance - Repeat test with Higher Blood Volume" @@ -482,14 +558,39 @@ class HemoCubeFragment : Fragment() { 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 "Normal" + if (predictedDenovixRatio in 0.16..0.165) + return "Negative Borderline" + if (predictedDenovixRatio in 0.165..0.235) + return "Sickle Cell Trait" + if (predictedDenovixRatio in 0.235..0.24) + return "Positive Borderline" + if (predictedDenovixRatio in 0.24..1.0) + return "Sickle Cell Disease" + } else { + return "INVALID" + } + } catch (e: Exception) { + showToast("error while performing classification") + Firebase.crashlytics.recordException(e) + return "ERROR" + } + return "INVALID" } private fun showToast(message: String) {