From 9b38a5dff78c679187f75d7d0787fd906357f2e6 Mon Sep 17 00:00:00 2001 From: mohamedkaif356 Date: Fri, 22 Sep 2023 11:40:17 +0530 Subject: [PATCH] Buffer check --- app/build.gradle | 4 +- app/src/main/AndroidManifest.xml | 8 + .../HemoCubeBufferCheckFragment.kt | 310 ++++++++++++++++++ .../HemocubeBufferCheckActivity.kt | 164 +++++++++ .../presentation/dashboard/GalleryFragment.kt | 7 +- app/src/main/res/layout/fragment_gallery.xml | 20 +- app/src/main/res/values/strings.xml | 1 + 7 files changed, 501 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/com/example/hpostesting/presentation/buffercheck/HemoCubeBufferCheckFragment.kt create mode 100644 app/src/main/java/com/example/hpostesting/presentation/buffercheck/HemocubeBufferCheckActivity.kt diff --git a/app/build.gradle b/app/build.gradle index 1158de0..b207d3c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,8 +18,8 @@ android { applicationId "in.sminnovations.hpostesting.prod" minSdk 21 targetSdk 34 - versionCode 11 - versionName "2.1.13" + versionCode 14 + versionName "2.1.14" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 11d8e5e..3c91302 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -33,6 +33,13 @@ android:parentActivityName="com.example.hpostesting.presentation.MainActivity" android:theme="@style/Theme.HPOS.NoActionBar" android:windowSoftInputMode="adjustPan" /> + + \ No newline at end of file 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 new file mode 100644 index 0000000..e8fb957 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/presentation/buffercheck/HemoCubeBufferCheckFragment.kt @@ -0,0 +1,310 @@ +package com.example.hpostesting.presentation.buffercheck + +import android.content.Context +import android.content.SharedPreferences +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +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.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 + +class HemoCubeBufferCheckFragment : Fragment() { + + private lateinit var binding: FragmentHemoCubeReferenceBinding + private val hemoCubeViewModel: HemoCubeViewModel by activityViewModels() + private lateinit var sharedPreferences: SharedPreferences + private var currentDeviceData: DeviceData? = null + private var resultData: String = "" + private val messages = MutableLiveData() + private var isTestOngoing = false + private var startListening = MutableLiveData(false) + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, + ): View { + binding = FragmentHemoCubeReferenceBinding.inflate(inflater, container, false) + sharedPreferences = + requireContext().getSharedPreferences("PREFERENCE_NAME", Context.MODE_PRIVATE) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initViews() + observeViewModel() + } + + private fun initViews() { + binding.btnKit.visibility = View.GONE + binding.btnSubmit.visibility = View.GONE + binding.etBloodGroup.visibility = View.GONE + listenToHemoCube() + startBufferProcess() + } + + private fun observeViewModel() { + + hemoCubeViewModel.deviceData.observe(viewLifecycleOwner) { + currentDeviceData = it + } + + hemoCubeViewModel.networkStatusLiveData.observe(viewLifecycleOwner) { isNetworkAvailable -> + if (isNetworkAvailable) { + hemoCubeViewModel.getDeviceData(sharedPreferences.getString(Constants.USER_ID, "")) + } else { + Toast.makeText( + requireContext(), + "No internet connection avaiable", + Toast.LENGTH_SHORT + ).show() + } + } + + messages.observe(viewLifecycleOwner) { + binding.tvSubtitle4.text = it + } + } + + 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) + } + } + + override fun onUsbError(e: Exception?) { + } + }) + } catch (e: Exception) { + showToast("test is going on") + Firebase.crashlytics.recordException(e) + } + } + + private fun handleUsbData(stringData: String, fullReadOutput: StringBuilder) { + if (stringData.contains("#")) { + messages.postValue(stringData) + isTestOngoing = true + } + + resultData += fullReadOutput.toString() + + when { + stringData.contains("ERROR 500") -> showError500Dialog() + stringData.contains("ERROR 501") -> showError501Dialog() + stringData.contains("#Buffer Completed") -> showStartSampleDialog() + stringData.contains("#Sample Completed") -> 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 showError500Dialog() { + UIUtils.createAlertDialog(requireContext(), + "BUFFER ERROR", + "Do you want to continue with existing buffer?", + getString(R.string.noo), + "Yes", + object : MyDialogListener { + override fun onClickNegativeButton() {} + + override fun onClickPositiveButton() { + listenToHemoCube() + startBufferProcess() + } + }) + } + + + private fun showError501Dialog() { + UIUtils.createAlertDialog(requireContext(), + "SAMPLE ERROR", + "Do you want to continue with Sample?", + getString(R.string.noo), + "Yes", + object : MyDialogListener { + override fun onClickNegativeButton() {} + + override fun onClickPositiveButton() { + listenToHemoCube() + startSampleProcess() + } + }) + } + + 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 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 calculatedRatio = calculateRatio(deviceRatio) + val classificationResult = findResult(calculatedRatio) + messages.postValue(classificationResult) + } + } catch (e: Exception) { + Toast.makeText( + requireContext(), "Error while processing device data", Toast.LENGTH_SHORT + ).show() + Firebase.crashlytics.recordException(e) + } + } + + private fun findResult(calculatedRatio: Double?): String { + try { + 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" + } else { + return "NULL" + } + } catch (e: Exception) { + showToast("error while performing classification") + Firebase.crashlytics.recordException(e) + return "ERROR" + } + return "NULL" + } + + private fun showToast(message: String) { + Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show() + } + + private fun startBufferProcess() { + + (activity as HemocubeBufferCheckActivity).mService.sendAndListenToHemoCube( + HemoCubeCommands.startBuffer, + object : UsbServiceListener { + override fun onUsbRead(data: ByteArray?) {} + + override fun onUsbError(e: Exception?) { + } + }) + } + + private fun startSampleProcess() { + + (activity as HemocubeBufferCheckActivity).mService.sendAndListenToHemoCube( + HemoCubeCommands.startSample, + object : UsbServiceListener { + override fun onUsbRead(data: ByteArray?) {} + + override fun onUsbError(e: Exception?) { + } + }) + } + + private fun fetchResult() { + + (activity as HemocubeBufferCheckActivity).mService.sendAndListenToHemoCube( + HemoCubeCommands.getSample, + object : UsbServiceListener { + override fun onUsbRead(data: ByteArray?) {} + + override fun onUsbError(e: Exception?) { + } + }) + } + + private fun calculateRatio(ratio: Double): Double { + val coefficient1 = currentDeviceData?.coefficients?.get(0) ?: 0.0 + val coefficient2 = currentDeviceData?.coefficients?.get(1) ?: 1.0 + 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 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/buffercheck/HemocubeBufferCheckActivity.kt b/app/src/main/java/com/example/hpostesting/presentation/buffercheck/HemocubeBufferCheckActivity.kt new file mode 100644 index 0000000..6375d39 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/presentation/buffercheck/HemocubeBufferCheckActivity.kt @@ -0,0 +1,164 @@ +package com.example.hpostesting.presentation.buffercheck + +import android.app.PendingIntent +import android.content.BroadcastReceiver +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.ServiceConnection +import android.hardware.usb.UsbDevice +import android.hardware.usb.UsbDeviceConnection +import android.hardware.usb.UsbManager +import android.os.Bundle +import android.os.IBinder +import android.util.Log +import android.view.Menu +import android.widget.Toast +import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import androidx.core.view.get +import com.example.hpostesting.data.DataHolder +import com.example.hpostesting.data.constant.Constants +import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel +import com.example.hpostesting.presentation.testRight.UsbService +import com.hoho.android.usbserial.driver.UsbSerialDriver +import com.hoho.android.usbserial.driver.UsbSerialProber +import dagger.hilt.android.AndroidEntryPoint +import `in`.sminnovations.hpostesting.R +import `in`.sminnovations.hpostesting.databinding.ActivityHemocubeBinding + +@AndroidEntryPoint +open class HemocubeBufferCheckActivity : AppCompatActivity() { + private lateinit var binding: ActivityHemocubeBinding + private val viewModel by viewModels() + private var myMenu: Menu? = null + + private lateinit var mDriver: UsbSerialDriver + private var mConnection: UsbDeviceConnection? = null + lateinit var mService: UsbService + + private val TAG = "HemoCube" + + private val broadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + + synchronized(this) { + val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE) + + if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { + device?.apply { + connectUsb(true) + DataHolder.usbConnected.postValue(true) + } + } else { + DataHolder.usbConnected.postValue(true) + } + } + + } + } + private val connection = object : ServiceConnection { + override fun onServiceConnected(className: ComponentName, service: IBinder) { + val binder = service as UsbService.UsbServiceBinder + mService = binder.getService() + viewModel.isServiceConnected = true + mConnection.let { mService.connect(mDriver, mConnection!!) } + moveToNext() + } + + override fun onServiceDisconnected(arg0: ComponentName) { + viewModel.isServiceConnected = false + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityHemocubeBinding.inflate(layoutInflater) + setContentView(binding.root) + setSupportActionBar(binding.myToolbar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + setupListener() + connectUsb(false) + } + + private fun setupListener() { + DataHolder.usbConnected.observe(this) { + Log.d("USB OBSERVE", "HemoCube called -> $it") + if (it) { + myMenu?.get(0)?.icon = + ContextCompat.getDrawable(this, R.drawable.ic_baseline_usb_24) + } else { + myMenu?.get(0)?.icon = + ContextCompat.getDrawable(this, R.drawable.ic_baseline_usb_off_24) + Toast.makeText(this, "Device is Disconnected", Toast.LENGTH_SHORT).show() + } + } + } + + open fun connectUsb(permissionGranted: Boolean) { + Log.d(TAG, "connectUsb() called, permission variable = $permissionGranted") + val manager = getSystemService(Context.USB_SERVICE) as UsbManager + val availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager) + + if (availableDrivers.isEmpty()) { + Toast.makeText(this, "No device found", Toast.LENGTH_SHORT).show() + } else { + mDriver = availableDrivers[0] + mConnection = manager.openDevice(mDriver.device) + + if (mConnection == null) { + requestUserPermission(manager, mDriver.device) + } else { + setupService() + } + } + } + + + private fun requestUserPermission(manager: UsbManager, device: UsbDevice) { + val mPendingIntent: PendingIntent = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { + PendingIntent.getBroadcast( + this, 0, Intent(Constants.HOMOCUBE_USB_PERMISSION), PendingIntent.FLAG_MUTABLE + ) + } else { + PendingIntent.getBroadcast( + this, + 0, + Intent(Constants.HOMOCUBE_USB_PERMISSION), + PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE + ) + } + + val filter = IntentFilter(Constants.HOMOCUBE_USB_PERMISSION) + registerReceiver(broadcastReceiver, filter) + manager.requestPermission(device, mPendingIntent) + } + + private fun setupService() { + val intent = Intent(this, UsbService::class.java) + bindService(intent, connection, Context.BIND_AUTO_CREATE) + } + + private fun moveToNext() { + if (supportFragmentManager.isDestroyed) return + + supportFragmentManager.beginTransaction().replace(binding.fghemocube.id, HemoCubeBufferCheckFragment()) + .commit() + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.my_menu, menu) + return true + } + + override fun onDestroy() { + super.onDestroy() + if (viewModel.isServiceConnected) { + mService.disconnect() + unbindService(connection) + viewModel.isServiceConnected = false + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/presentation/dashboard/GalleryFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/dashboard/GalleryFragment.kt index 1911ca0..9307d1b 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/dashboard/GalleryFragment.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/dashboard/GalleryFragment.kt @@ -1,12 +1,12 @@ package com.example.hpostesting.presentation.dashboard +import android.content.Intent import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModelProvider -//import com.example.hpostesting.presentation.dashboard.ui.gallery.GalleryViewModel +import com.example.hpostesting.presentation.buffercheck.HemocubeBufferCheckActivity import `in`.sminnovations.hpostesting.databinding.FragmentGalleryBinding class GalleryFragment : Fragment() { @@ -32,6 +32,9 @@ class GalleryFragment : Fragment() { // galleryViewModel.text.observe(viewLifecycleOwner) { //// textView.text = it // } + binding.btnSubmit.setOnClickListener { + startActivity(Intent(requireContext(), HemocubeBufferCheckActivity::class.java)) + } return root } diff --git a/app/src/main/res/layout/fragment_gallery.xml b/app/src/main/res/layout/fragment_gallery.xml index 2a4b95d..6f43343 100644 --- a/app/src/main/res/layout/fragment_gallery.xml +++ b/app/src/main/res/layout/fragment_gallery.xml @@ -4,19 +4,21 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".presentation.dashboard.GalleryFragment"> + tools:context="com.example.hpostesting.presentation.dashboard.GalleryFragment"> - + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 15023b1..5ec2c82 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -193,5 +193,6 @@ Select Blood Group All registered user are tested successfully Start Incubation + Check Buffer \ No newline at end of file