diff --git a/app/build.gradle b/app/build.gradle index 06f039e..419fccc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,15 +16,16 @@ android { // prod - production, preprod - preproduction, quality - qc, dev - development defaultConfig { - applicationId "in.sminnovations.hpostesting.quality" + applicationId "in.sminnovations.hpostesting.dev" minSdk 21 targetSdk 34 - versionCode 97 - versionName "2.1.97" + versionCode 98 + versionName "2.1.98" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } + buildTypes { release { minifyEnabled false diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a91079a..a5b66b6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -69,6 +69,15 @@ android:parentActivityName="com.example.hpostesting.presentation.MainActivity" android:theme="@style/Theme.HPOS.NoActionBar" android:windowSoftInputMode="adjustPan" /> + + + when (deviceType) { - Constants.DEVICE_TYPE_HOMOCUBE -> { + Constants.DEVICE_TYPE_HEMOCUBE -> { val i = Intent(applicationContext, HemocubeActivity::class.java) startActivity(i) } @@ -194,6 +194,11 @@ class KitScanActivity : AppCompatActivity() { val i = Intent(applicationContext, TestRightActivity::class.java) startActivity(i) } + + Constants.DEVICE_TYPE_TRUEHEME -> { + val i = Intent(applicationContext, TrueHemeActivity::class.java) + startActivity(i) + } } } } diff --git a/app/src/main/java/com/example/hpostesting/presentation/MainActivity.kt b/app/src/main/java/com/example/hpostesting/presentation/MainActivity.kt index 5afbabc..dfd8997 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/MainActivity.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/MainActivity.kt @@ -1,8 +1,10 @@ package com.example.hpostesting.presentation -import com.example.hpostesting.presentation.dashboard.DashboardActivity import android.Manifest -import android.content.* +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter import android.content.pm.PackageManager import android.hardware.usb.UsbManager import android.location.Location @@ -20,7 +22,12 @@ import com.example.hpostesting.data.constant.Constants import com.example.hpostesting.data.constant.LanguageManager import com.example.hpostesting.data.model.patient.UserData import com.example.hpostesting.data.model.test.TestType -import com.google.android.gms.location.* +import com.example.hpostesting.presentation.dashboard.DashboardActivity +import com.google.android.gms.location.FusedLocationProviderClient +import com.google.android.gms.location.LocationCallback +import com.google.android.gms.location.LocationRequest +import com.google.android.gms.location.LocationResult +import com.google.android.gms.location.LocationServices import com.google.android.material.snackbar.Snackbar import com.google.firebase.crashlytics.FirebaseCrashlytics import com.hoho.android.usbserial.driver.UsbSerialProber @@ -81,7 +88,7 @@ class MainActivity : AppCompatActivity() { DataHolder.deviceType.observe(this) { deviceType -> with(binding) { - if (deviceType == Constants.DEVICE_TYPE_HOMOCUBE) { + if (deviceType == Constants.DEVICE_TYPE_HEMOCUBE) { cvItem1.visibility = View.VISIBLE cvItem3.visibility = View.VISIBLE cvItem2.visibility = View.GONE @@ -111,8 +118,8 @@ class MainActivity : AppCompatActivity() { binding.cvItem3.visibility = View.VISIBLE binding.cvItem2.visibility = View.GONE binding.cvItem4.visibility = View.GONE - DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HOMOCUBE) - Constants.DEVICE_TYPE_HOMOCUBE + DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HEMOCUBE) + Constants.DEVICE_TYPE_HEMOCUBE } device.productId == Constants.HEMO_CUBE_V2_PRODUCT_ID && device.vendorId == Constants.HEMO_CUBE_V2_VENDOR_ID -> { @@ -120,8 +127,8 @@ class MainActivity : AppCompatActivity() { binding.cvItem3.visibility = View.VISIBLE binding.cvItem2.visibility = View.GONE binding.cvItem4.visibility = View.GONE - DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HOMOCUBE) - Constants.DEVICE_TYPE_HOMOCUBE + DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HEMOCUBE) + Constants.DEVICE_TYPE_HEMOCUBE } device.productId == 24577 && device.vendorId == 1027 -> { @@ -129,8 +136,8 @@ class MainActivity : AppCompatActivity() { binding.cvItem3.visibility = View.VISIBLE binding.cvItem2.visibility = View.GONE binding.cvItem4.visibility = View.GONE - DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HOMOCUBE) - Constants.DEVICE_TYPE_HOMOCUBE + DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_TRUEHEME) + Constants.DEVICE_TYPE_TRUEHEME } device.productId == 8963 && device.vendorId == 1659 -> { @@ -138,8 +145,8 @@ class MainActivity : AppCompatActivity() { binding.cvItem3.visibility = View.VISIBLE binding.cvItem2.visibility = View.GONE binding.cvItem4.visibility = View.GONE - DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HOMOCUBE) - Constants.DEVICE_TYPE_HOMOCUBE + DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HEMOCUBE) + Constants.DEVICE_TYPE_HEMOCUBE } device.productId == Constants.DEVICE_PRODUCT_ID && device.vendorId == Constants.DEVICE_VENDOR_ID -> { @@ -278,7 +285,6 @@ class MainActivity : AppCompatActivity() { // exception.printStackTrace() // } - fusedLocationClient.lastLocation.addOnSuccessListener { location: Location? -> location?.let { if (DataHolder.selectedTest != null) { @@ -302,9 +308,6 @@ class MainActivity : AppCompatActivity() { val rootView = findViewById(android.R.id.content) Snackbar.make(rootView, errorMessage, Snackbar.LENGTH_LONG).show() } - - - } private fun requestLocationUpdates() { 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 1868e98..f1c20b0 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 @@ -123,7 +123,7 @@ class HemoCubeFragment : Fragment() { } binding.btnPlacebuffer.setOnClickListener { - if (isBufferValueAvailable()) { + if (isBufferValueAvailable() || !Constants.BLANK_EVERY_TEST) { showBufferAlertDialog() } else { activity?.runOnUiThread { @@ -188,23 +188,6 @@ class HemoCubeFragment : Fragment() { binding.progressBar.visibility = View.GONE } -// hemoCubeViewModel.fireBaseUpload.observe(viewLifecycleOwner) { result -> -// if (result == "Success") { -// showToast(R.string.test_upload) -// handleReadingFinish() -// } -// if (result == "Local") { -// showToast(R.string.internt_not_local) -// handleReadingFinish() -// } -// if (result == "Error") { -// showToast(R.string.error_local) -// startActivity(Intent(requireActivity(), DashboardActivity::class.java)) -// } -// -// binding.progressBar.visibility = View.GONE -// } - hemoCubeViewModel.getDeviceData(sharedPreferences.getString(Constants.USER_ID, "")) hemoCubeViewModel.deviceData.observe(viewLifecycleOwner) { @@ -387,6 +370,19 @@ class HemoCubeFragment : Fragment() { processV1HardwareId(resultData) } + (resultData.contains("#LS") && this.testStatusCode < TestStatus.FIRST_EMPTY_AIR_READING_STARTED.code) -> { + // air reading 1 + this.testStatusCode = TestStatus.FIRST_EMPTY_AIR_READING_STARTED.code + hemoCubeViewModel.messages.postValue("Air reading started") + } + + (resultData.contains("#LC") && this.testStatusCode < TestStatus.FIRST_EMPTY_AIR_READING_COMPLETED.code) -> { + // air reading 1, send command to print + this.testStatusCode = TestStatus.FIRST_EMPTY_AIR_READING_COMPLETED.code + hemoCubeViewModel.messages.postValue("Air reading completed") + fetchResult() + } + resultData.contains("#BS") && this.testStatusCode < TestStatus.BUFFER_STARTED.code -> { hemoCubeViewModel.messages.postValue(getString(R.string.buffer_started)) this.testStatusCode = TestStatus.BUFFER_STARTED.code @@ -455,10 +451,59 @@ class HemoCubeFragment : Fragment() { fetchResult() } + (resultData.contains("#LS") && this.testStatusCode < TestStatus.SECOND_EMPTY_AIR_READING_STARTED.code) -> { + // air reading 2 + this.testStatusCode = TestStatus.SECOND_EMPTY_AIR_READING_STARTED.code + hemoCubeViewModel.messages.postValue("Air reading started") + } + + (resultData.contains("#LC") && this.testStatusCode < TestStatus.SECOND_EMPTY_AIR_READING_COMPLETED.code) -> { + // air reading 2, print values + this.testStatusCode = TestStatus.SECOND_EMPTY_AIR_READING_COMPLETED.code + hemoCubeViewModel.messages.postValue("Air reading completed") + fetchResult() + } + resultData.contains("REND") && this.testStatusCode < TestStatus.SAMPLE_PRINT_COMPLETED.code -> { handleSampleCompleted() } + currentResultData.contains("REND") + && this.testStatusCode >= TestStatus.FIRST_EMPTY_AIR_READING_COMPLETED.code + && this.testStatusCode < TestStatus.FIRST_EMPTY_AIR_READING_PRINT_COMPLETED.code -> { + this.testStatusCode = TestStatus.FIRST_GAIN_PRINT_COMPLETED.code + hemoCubeViewModel.messages.postValue("First air reading completed") + + val resultLines = currentResultData.split("\\s+(?=LB|LS)".toRegex()) + DataHolder.hemoCubeTestData?.apply { + led1Air1 = resultLines[5].split(' ')[1].trim().toDoubleOrNull()!! + led2Air1 = resultLines[6].split(' ')[1].trim().toDoubleOrNull()!! + led3Air1 = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!! + led4Air1 = resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!! + } + + startBufferProcess() + currentResultData = "" + } + + currentResultData.contains("REND") + && this.testStatusCode >= TestStatus.SECOND_EMPTY_AIR_READING_COMPLETED.code + && this.testStatusCode < TestStatus.SECOND_EMPTY_AIR_PRINT_COMPLETED.code -> { + this.testStatusCode = TestStatus.SECOND_EMPTY_AIR_PRINT_COMPLETED.code + hemoCubeViewModel.messages.postValue("Second air reading completed") + + val resultLines = currentResultData.split("\\s+(?=LB|LS)".toRegex()) + DataHolder.hemoCubeTestData?.apply { + led1Air2 = resultLines[5].split(' ')[1].trim().toDoubleOrNull()!! + led2Air2 = resultLines[6].split(' ')[1].trim().toDoubleOrNull()!! + led3Air2 = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!! + led4Air2 = resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!! + } + + sendSecondGainCommand() + currentResultData = "" + } + currentResultData.contains("REND") && this.testStatusCode >= TestStatus.FIRST_GAIN_COMPLETED.code && this.testStatusCode < TestStatus.FIRST_GAIN_PRINT_COMPLETED.code -> { diff --git a/app/src/main/java/com/example/hpostesting/presentation/trueheme/TrueHemeActivity.kt b/app/src/main/java/com/example/hpostesting/presentation/trueheme/TrueHemeActivity.kt new file mode 100644 index 0000000..465abb7 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/presentation/trueheme/TrueHemeActivity.kt @@ -0,0 +1,199 @@ +package com.example.hpostesting.presentation.trueheme + +import android.annotation.SuppressLint +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.data.constant.LanguageManager +import com.example.hpostesting.presentation.hemocube.HemoCubeFragment +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 +class TrueHemeActivity : 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 { + onErrorReported("permission denied for device") + 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 attachBaseContext(newBase: Context?) { + val languageCode = LanguageManager.getSavedLanguage(newBase!!) + LanguageManager.setLocale(newBase, languageCode) + super.attachBaseContext(newBase) + } + + + 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()) { + onErrorReported("No Device is Connected") + } else { + mDriver = availableDrivers[0] + mConnection = manager.openDevice(mDriver.device) + + if (mConnection == null) { + requestUserPermission(manager, mDriver.device) + } else { + setupService() + } + } + } + + + @SuppressLint("MutableImplicitPendingIntent") + private fun requestUserPermission(manager: UsbManager, device: UsbDevice) { + val mPendingIntent: PendingIntent + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { + mPendingIntent = PendingIntent.getBroadcast( + this, 0, Intent(Constants.HOMOCUBE_USB_PERMISSION), PendingIntent.FLAG_MUTABLE + ) + } else { + mPendingIntent = 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) + } + + fun setupService() { + val intent = Intent(this, UsbService::class.java) + bindService(intent, connection, Context.BIND_AUTO_CREATE) + } + + open fun reconnectDevice() { + mService.disconnect() + unbindService(connection) + viewModel.isServiceConnected = false + connectUsb(true) + } + + private fun moveToNext() { + if (supportFragmentManager.isDestroyed) return + + supportFragmentManager.beginTransaction().replace(binding.fghemocube.id, HemoCubeFragment()) + .commit() + } + + + override fun onBackPressed() { + val fragment = supportFragmentManager.findFragmentById(R.id.fghemocube) + if (fragment is HemoCubeFragment) { + fragment.handleBackButtonPress() + } else { + super.onBackPressed() + } + } + + + fun onErrorReported(msg: String) { + Toast.makeText(this, msg, Toast.LENGTH_SHORT).show() + if (!isFinishing) onBackPressed() + } + + 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/trueheme/TrueHemeFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/trueheme/TrueHemeFragment.kt new file mode 100644 index 0000000..be89edf --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/presentation/trueheme/TrueHemeFragment.kt @@ -0,0 +1,1144 @@ +package com.example.hpostesting.presentation.trueheme + +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.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.Result +import com.example.hpostesting.data.constant.Constants +import com.example.hpostesting.data.constant.HemoCubeCommands +import com.example.hpostesting.data.constant.TestStatus +import com.example.hpostesting.data.model.TestState +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.HemocubeActivity +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.FragmentTruehemeSampleBinding +import kotlin.math.abs +import kotlin.math.log10 + +@Suppress("MemberVisibilityCanBePrivate") +class TrueHemeFragment : Fragment() { + + private lateinit var binding: FragmentTruehemeSampleBinding + private val hemoCubeViewModel: TrueHemeViewModel by activityViewModels() + private lateinit var sharedPreferences: SharedPreferences + private val testDetails = DataHolder.selectedTest?.toHemoCubeTestData() + 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) + 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 calculatedPredictedDenovixRatio = 0.0 + private var validationError = false + private var deviceHardwareId = "" + private var allErrorMessages = "" + private var testStatusCode = 0.0 + private var repeatReadingCount = 0 + private var readingsPerSample = Constants.READINGS_PER_SAMPLE + private var uploadedToCloud = false + private var uploadedToMolbio = false + lateinit var testState: TestState + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, + ): View { + binding = FragmentTruehemeSampleBinding.inflate(inflater, container, false) + sharedPreferences = + requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE) + + testState = TestState( + testDetails = DataHolder.selectedTest?.toHemoCubeTestData(), + ) + + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initViews() + listenToHemoCube() + getDeviceInfo() + observeViewModel() + } + + private fun initViews() { + binding.btnSubmit.setOnClickListener { + activity?.runOnUiThread { + binding.progressBar.visibility = View.VISIBLE + binding.btnSubmit.visibility = View.GONE + } + hemoCubeViewModel.uploadHemoCubeResultToDatabase( + isOnline, true, sharedPreferences.getString(Constants.KIT_NUMBER, "") + ) + } + + binding.tvTitle2.visibility = View.GONE + binding.etAbhaId.visibility = View.GONE + binding.nameEditText.visibility = View.GONE + binding.tvTitle.visibility = View.GONE + binding.btnGo.visibility = View.GONE +// binding.btnSubmit.isEnabled = false +// binding.btnSubmit.isClickable = false + binding.btnPlacebuffer.visibility = View.GONE + + binding.tvName.text = "Name: ${testDetails?.name}\n ID: ${testDetails?._id}" + binding.tvSubtitle4.text = "Config" + + binding.btnSamplestart.setOnClickListener { + activity?.runOnUiThread { + binding.tvSubtitle4.visibility = View.VISIBLE +// binding.tvSubtitle4.text = "Sample Started" + } + startSampleProcess() + it.visibility = View.GONE + } + + binding.btnPlacebuffer.setOnClickListener { + if (isBufferValueAvailable() || !Constants.BLANK_EVERY_TEST) { + showBufferAlertDialog() + } else { + activity?.runOnUiThread { + binding.tvSubtitle4.visibility = View.VISIBLE +// binding.tvSubtitle4.text = "Buffer Started" + } + + checkAndStartProcess() + it.visibility = View.GONE + } + + } + + if (sharedPreferences.getString(Constants.USER_ID, "").toString() == "ADMIN") { + activity?.runOnUiThread { + binding.tvDeviceMessages.visibility = View.VISIBLE + } + } + } + + private fun observeViewModel() { + hemoCubeViewModel.fireBaseUpload.observe(viewLifecycleOwner) { result -> + if (result == "Success") { + uploadedToCloud = true + showToast(R.string.test_upload) + hemoCubeViewModel.resultUpload.observe(viewLifecycleOwner) { + when (it) { + is Result.Success -> { + uploadedToMolbio = true + if (Constants.MOLBIO_INTERGATION) { + it.data.data?.get(0)?.rawData?.let { it1 -> + hemoCubeViewModel.updateMolbioFlag( + it1._id + ) + } + } + handleReadingFinish() + } + is Result.Error -> { + binding.btnSubmit.visibility = View.VISIBLE + //Remove this line of code while deploying to IOCL + it.exception.let { message -> + Toast.makeText(activity, "An error occurred: $message", Toast.LENGTH_LONG) + .show() + } + handleReadingFinish() + } + + else -> {} + } + } + } + if (result == "Local") { + showToast(R.string.internt_not_local) + startActivity(Intent(requireActivity(), DashboardActivity::class.java)) + } + if (result == "Error") { + showToast(R.string.error_local) + startActivity(Intent(requireActivity(), DashboardActivity::class.java)) + } + + binding.progressBar.visibility = View.GONE + } + + hemoCubeViewModel.getDeviceData(sharedPreferences.getString(Constants.USER_ID, "")) + + hemoCubeViewModel.deviceData.observe(viewLifecycleOwner) { + currentDeviceData = it + } + + hemoCubeViewModel.networkStatusLiveData.observe(viewLifecycleOwner) { isNetworkAvailable -> + apply { + DataHolder.hemoCubeTestData?.let { + currentDeviceData?.coefficients?.let { coefficients -> +// val coefficient1 = coefficients[0] +// val coefficient2 = coefficients[1] +// val result = coefficient1 * coefficient2 + } + } + isOnline = isNetworkAvailable + } + } + + hemoCubeViewModel.messages.observe(viewLifecycleOwner) { + binding.tvSubtitle4.text = it + } + + hemoCubeViewModel.deviceMessages.observe(viewLifecycleOwner) { + binding.tvDeviceMessages.text = it + } + } + + private fun handleReadingFinish() { + if (allReadingsComplete(repeatReadingCount, readingsPerSample) && uploadedToCloud) { + activity?.runOnUiThread { + binding.btnSubmit.visibility = View.GONE + val i = Intent( + requireContext().applicationContext, DashboardActivity::class.java + ) + startActivity(i) + } + } else { + hemoCubeViewModel.messages.postValue("Reading $repeatReadingCount completed") + resetTest() + startSampleProcess() + } + } + + fun allReadingsComplete(repeatReadingCount: Int, readingsPerSample: Int): Boolean { + return repeatReadingCount >= readingsPerSample + } + + private fun resetTest() { + testStatusCode = 0.0 + allErrorMessages = "" + validationError = false + resultData = "" + currentResultData = "" + } + + private fun checkAndStartProcess() { + startBufferProcess() + } + + private fun isBufferValueAvailable(): Boolean { + return try { + if (sharedPreferences.getString( + Constants.BUFFER_VALUE_1, "" + ) != "" && sharedPreferences.getString(Constants.BUFFER_VALUE_2, "") != "" + && sharedPreferences.getString(Constants.BUFFER_VALUE_3, "") != "" + && sharedPreferences.getString(Constants.BUFFER_VALUE_4, "") != "" + ) { + sharedPreferences.getString(Constants.BUFFER_VALUE_1, "") + ?.toDouble()!! > 0.0 && sharedPreferences.getString( + Constants.BUFFER_VALUE_2, + "" + ) + ?.toDouble()!! > 0.0 && sharedPreferences.getString( + Constants.BUFFER_VALUE_3, + "" + ) + ?.toDouble()!! > 0.0 && sharedPreferences.getString( + Constants.BUFFER_VALUE_4, + "" + ) + ?.toDouble()!! > 0.0 + } else { + false + } + } catch (e: Exception) { + showToast(R.string.error_exist) + false + } + } + + private fun showBufferAlertDialog() { + val title = "WARNING" + val message = getString(R.string.do_exist) + val negativeText = getString(R.string.no) + val positiveText = getString(R.string.yes) + + UIUtils.createAlertDialog(requireContext(), + title, + message, + negativeText, + positiveText, + object : MyDialogListener { + override fun onClickNegativeButton() { + startBufferProcess() + } + + override fun onClickPositiveButton() { + activity?.runOnUiThread { + binding.btnPlacebuffer.visibility = View.GONE + binding.btnSamplestart.visibility = View.VISIBLE + binding.tvSubtitle4.text = getString(R.string.place_sample) + } + isUsingExistingBuffer = true + } + }) + } + + private fun listenToHemoCube() { + DataHolder.hemoCubeTestData = DataHolder.selectedTest?.toHemoCubeTestData() + hemoCubeViewModel.progressBar.postValue(true) + + val fullReadOutput = StringBuilder() + startListening.postValue(true) + + try { + (activity as HemocubeActivity).mService.listenToHemoCube(object : UsbServiceListener { + override fun onUsbRead(data: ByteArray?) { + data?.let { + val stringData = String(it) + fullReadOutput.append(stringData) + handleUsbData(stringData) + } + } + + override fun onUsbError(e: Exception?) { + hemoCubeViewModel.progressBar.postValue(false) + } + }) + } catch (e: Exception) { + showToast(R.string.test_ongoing) + Firebase.crashlytics.recordException(e) + } + } + + private fun getDeviceInfo() { + hemoCubeViewModel.progressBar.postValue(true) + (activity as HemocubeActivity).mService.sendAndListenToHemoCube( + HemoCubeCommands.DEVICE_CONFIGURATION_COMMAND, + 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) + } + }) + } + + fun handleUsbData(stringData: String) { + if (stringData.contains("#")) { + isTestOngoing = true + } + + resultData += stringData + currentResultData += stringData + hemoCubeViewModel.deviceMessages.postValue(currentResultData) + + when { + resultData.contains("SNE") && this.testStatusCode < TestStatus.CONFIG_COMPLETED.code -> { + processV2HardwareId(resultData) + } + + (resultData.contains("SN") && !resultData.contains("SNS") && !resultData.contains("SNE") && resultData.length >= 15) && this.testStatusCode < TestStatus.CONFIG_COMPLETED.code -> { + processV1HardwareId(resultData) + } + + (resultData.contains("#LS") && this.testStatusCode < TestStatus.FIRST_EMPTY_AIR_READING_STARTED.code) -> { + // air reading 1 + this.testStatusCode = TestStatus.FIRST_EMPTY_AIR_READING_STARTED.code + hemoCubeViewModel.messages.postValue("Air reading started") + } + + (resultData.contains("#LC") && this.testStatusCode < TestStatus.FIRST_EMPTY_AIR_READING_COMPLETED.code) -> { + // air reading 1, send command to print + this.testStatusCode = TestStatus.FIRST_EMPTY_AIR_READING_COMPLETED.code + hemoCubeViewModel.messages.postValue("Air reading completed") + fetchResult() + } + + resultData.contains("#BS") && this.testStatusCode < TestStatus.BUFFER_STARTED.code -> { + hemoCubeViewModel.messages.postValue(getString(R.string.buffer_started)) + this.testStatusCode = TestStatus.BUFFER_STARTED.code + } + + resultData.contains("#BC") && this.testStatusCode < TestStatus.BUFFER_COMPLETED.code -> { + this.testStatusCode = TestStatus.BUFFER_COMPLETED.code + activity?.runOnUiThread { + binding.tvSubtitle4.text = getString(R.string.buffer_completed) + binding.btnSamplestart.visibility = View.VISIBLE + } + } + + (resultData.contains("#SS") || resultData.contains("#SS1")) && this.testStatusCode < TestStatus.SAMPLE_STARTED.code -> { + this.testStatusCode = TestStatus.SAMPLE_STARTED.code + activity?.runOnUiThread { + binding.tvSubtitle4.text = getString(R.string.sample_started) + binding.btnSamplestart.visibility = View.GONE + } + } + + (resultData.contains("#SC") || resultData.contains("#SC1")) && this.testStatusCode < TestStatus.SAMPLE_COMPLETED.code -> { + this.testStatusCode = TestStatus.SAMPLE_COMPLETED.code + hemoCubeViewModel.messages.postValue( + getString(R.string.sample_completed) + "\n" + getString(R.string.gathering_data)) + fetchResult() + currentResultData = "" + } + + resultData.contains("ovf") -> { + hemoCubeViewModel.messages.postValue( + getString(R.string.power_bank) + ) + } + + currentResultData.contains("#SS2") && this.testStatusCode < TestStatus.FIRST_GAIN_STARTED.code -> { + this.testStatusCode = TestStatus.FIRST_GAIN_STARTED.code + hemoCubeViewModel.messages.postValue("1.3X Gain Started") + } + + currentResultData.contains("#SC2") && this.testStatusCode < TestStatus.FIRST_GAIN_COMPLETED.code -> { + this.testStatusCode = TestStatus.FIRST_GAIN_COMPLETED.code + hemoCubeViewModel.messages.postValue("1.3X Gain Completed") + fetchResult() + } + + currentResultData.contains("#SS3") && this.testStatusCode < TestStatus.SECOND_GAIN_STARTED.code -> { + this.testStatusCode = TestStatus.SECOND_GAIN_STARTED.code + hemoCubeViewModel.messages.postValue("2X Gain Started") + } + + currentResultData.contains("#SC3") && this.testStatusCode < TestStatus.SECOND_GAIN_COMPLETED.code -> { + this.testStatusCode = TestStatus.SECOND_GAIN_COMPLETED.code + hemoCubeViewModel.messages.postValue("2X Gain Completed") + fetchResult() + } + + currentResultData.contains("#SS5") && this.testStatusCode < TestStatus.FORTH_GAIN_STARTED.code -> { + this.testStatusCode = TestStatus.FORTH_GAIN_STARTED.code + hemoCubeViewModel.messages.postValue("7.6X Gain Started") + } + + currentResultData.contains("#SC5") && this.testStatusCode < TestStatus.FORTH_GAIN_COMPLETED.code -> { + this.testStatusCode = TestStatus.FORTH_GAIN_COMPLETED.code + hemoCubeViewModel.messages.postValue("7.6X Gain Completed") + fetchResult() + } + + (resultData.contains("#LS") && this.testStatusCode < TestStatus.SECOND_EMPTY_AIR_READING_STARTED.code) -> { + // air reading 2 + this.testStatusCode = TestStatus.SECOND_EMPTY_AIR_READING_STARTED.code + hemoCubeViewModel.messages.postValue("Air reading started") + } + + (resultData.contains("#LC") && this.testStatusCode < TestStatus.SECOND_EMPTY_AIR_READING_COMPLETED.code) -> { + // air reading 2, print values + this.testStatusCode = TestStatus.SECOND_EMPTY_AIR_READING_COMPLETED.code + hemoCubeViewModel.messages.postValue("Air reading completed") + fetchResult() + } + + resultData.contains("REND") && this.testStatusCode < TestStatus.SAMPLE_PRINT_COMPLETED.code -> { + handleSampleCompleted() + } + + currentResultData.contains("REND") + && this.testStatusCode >= TestStatus.FIRST_EMPTY_AIR_READING_COMPLETED.code + && this.testStatusCode < TestStatus.FIRST_EMPTY_AIR_READING_PRINT_COMPLETED.code -> { + this.testStatusCode = TestStatus.FIRST_GAIN_PRINT_COMPLETED.code + hemoCubeViewModel.messages.postValue("First air reading completed") + + val resultLines = currentResultData.split("\\s+(?=LB|LS)".toRegex()) + DataHolder.hemoCubeTestData?.apply { + led1Air1 = resultLines[5].split(' ')[1].trim().toDoubleOrNull()!! + led2Air1 = resultLines[6].split(' ')[1].trim().toDoubleOrNull()!! + led3Air1 = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!! + led4Air1 = resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!! + } + + startBufferProcess() + currentResultData = "" + } + + currentResultData.contains("REND") + && this.testStatusCode >= TestStatus.SECOND_EMPTY_AIR_READING_COMPLETED.code + && this.testStatusCode < TestStatus.SECOND_EMPTY_AIR_PRINT_COMPLETED.code -> { + this.testStatusCode = TestStatus.SECOND_EMPTY_AIR_PRINT_COMPLETED.code + hemoCubeViewModel.messages.postValue("Second air reading completed") + + val resultLines = currentResultData.split("\\s+(?=LB|LS)".toRegex()) + DataHolder.hemoCubeTestData?.apply { + led1Air2 = resultLines[5].split(' ')[1].trim().toDoubleOrNull()!! + led2Air2 = resultLines[6].split(' ')[1].trim().toDoubleOrNull()!! + led3Air2 = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!! + led4Air2 = resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!! + } + + sendSecondGainCommand() + currentResultData = "" + } + + currentResultData.contains("REND") + && this.testStatusCode >= TestStatus.FIRST_GAIN_COMPLETED.code + && this.testStatusCode < TestStatus.FIRST_GAIN_PRINT_COMPLETED.code -> { + this.testStatusCode = TestStatus.FIRST_GAIN_PRINT_COMPLETED.code + 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 >= TestStatus.FIRST_GAIN_PRINT_COMPLETED.code + && this.testStatusCode < TestStatus.SECOND_GAIN_PRINT_COMPLETED.code -> { + this.testStatusCode = TestStatus.SECOND_GAIN_PRINT_COMPLETED.code + 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 >= TestStatus.SECOND_GAIN_PRINT_COMPLETED.code + && this.testStatusCode < TestStatus.FORTH_GAIN_PRINT_COMPLETED.code -> { + this.testStatusCode = TestStatus.FORTH_GAIN_PRINT_COMPLETED.code + 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()!! + } + + finishReading() + } + } + } + + fun finishReading() { + repeatReadingCount += 1 + + if (readingsPerSample == 1) { + activity?.runOnUiThread { + binding.btnSubmit.visibility = View.VISIBLE + binding.btnSubmit.isEnabled = true + binding.btnSubmit.isClickable = true +// binding.clParent.setBackgroundColor(Color.parseColor("#edfffd")) + binding.ivCheck.visibility = View.VISIBLE + } + } else { + hemoCubeViewModel.uploadHemoCubeResultToDatabase( + isOnline, true, sharedPreferences.getString(Constants.KIT_NUMBER, "") + ) + } + } + + fun handleSampleCompleted() { + + this.testStatusCode = TestStatus.SAMPLE_PRINT_COMPLETED.code + 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) { + sharedPreferences.getString(Constants.BUFFER_VALUE_1, "")?.toDoubleOrNull()!! + } else { + bufferIntensity.toDoubleOrNull()!! + } + bufferIntensity = resultLines[2].split(' ')[1].trim() + led2BufferForDevice = if (isUsingExistingBuffer) { + sharedPreferences.getString(Constants.BUFFER_VALUE_2, "")?.toDoubleOrNull()!! + } else { + bufferIntensity.toDoubleOrNull()!! + } + bufferIntensity = resultLines[3].split(' ')[1].trim() + led3BufferForDevice = if (isUsingExistingBuffer) { + sharedPreferences.getString(Constants.BUFFER_VALUE_3, "")?.toDoubleOrNull()!! + } else { + bufferIntensity.toDoubleOrNull()!! + } + bufferIntensity = resultLines[4].split(' ')[1].trim() + led4BufferForDevice = if (isUsingExistingBuffer) { + sharedPreferences.getString(Constants.BUFFER_VALUE_4, "")?.toDoubleOrNull()!! + } else { + 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() + if (Constants.PQ_MODE) { + finishReading() + } else { + sendFirstGainCommand() + } + currentResultData = "" + } + + fun processV1HardwareId(resultData: String) { + val hardwareId = extractV1HardwareId(resultData) + + if (!hardwareId.isNullOrBlank()) { + updateDeviceId(hardwareId) + activity?.runOnUiThread { + binding.btnPlacebuffer.visibility = View.VISIBLE + } + hemoCubeViewModel.messages.postValue(getString(R.string.start)) + } else { + hemoCubeViewModel.messages.postValue("Config error") + } + + if (!Constants.DEVICE_CONFIGURATION.containsKey(deviceHardwareId)) { + assignDefaultDevice(resultData) + testState.allErrorMessages += "Calibration configuration for this device id is not found\n" + } + if (!Constants.BUFFER_INTENSITY_THRESHOLDS.containsKey(deviceHardwareId)) { + testState.allErrorMessages += "ADC thresholds for this device id are not found\n" + } + } + + fun extractV1HardwareId(input: String): String? { + val regex = Regex("SN (\\S+)") + val matchResult = regex.find(input) + + return matchResult?.groupValues?.get(1) + } + + fun processV2HardwareId(resultData: String) { + val hardwareId = extractV2HardwareId(resultData) + + if (!hardwareId.isNullOrBlank()) { + updateDeviceId(hardwareId) + activity?.runOnUiThread { + binding.btnPlacebuffer.visibility = View.VISIBLE + } + hemoCubeViewModel.messages.postValue(getString(R.string.start)) + } else { + hemoCubeViewModel.messages.postValue("Config error") + } + + if (!Constants.DEVICE_CONFIGURATION.containsKey(deviceHardwareId)) { + assignDefaultDevice(resultData) + testState.allErrorMessages += "Calibration configuration for this device id is not found\n" + } + if (!Constants.BUFFER_INTENSITY_THRESHOLDS.containsKey(deviceHardwareId)) { + testState.allErrorMessages += "ADC thresholds for this device id are not found\n" + } + } + + fun assignDefaultDevice(configData: String) { + deviceHardwareId = "HCV-000-3001" + testStatusCode = TestStatus.CONFIG_COMPLETED.code + activity?.runOnUiThread { + binding.btnPlacebuffer.visibility = View.VISIBLE + } + hemoCubeViewModel.messages.postValue(getString(R.string.start)) + } + + fun extractV2HardwareId(input: String): String? { + val pattern = Regex("SNS\\s*(.*?)\\s*SNE") + val matchResult: MatchResult? = pattern.find(input) + + return matchResult?.groups?.get(1)?.value + } + + fun updateDeviceId(hardwareId: String?) { + hardwareId?.let { + deviceHardwareId += it + with(sharedPreferences.edit()) { + putString(Constants.DEVICE_ID, deviceHardwareId) + apply() + } + testStatusCode = TestStatus.CONFIG_COMPLETED.code + } + } + + fun handleBackButtonPress() { + if (isTestOngoing) { + showToast(R.string.test_go_noback) + } else { + activity?.onBackPressed() + } + } + + private fun processResult() { + try { + hemoCubeViewModel.messages.postValue(getString(R.string.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 = led4Average / 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 + testState.allErrorMessages += "Error: Invalid Test. Improper buffer reading (low)" + activity?.runOnUiThread { + binding.errorMessage.text = getString(R.string.error_improper_buffer_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 + testState.allErrorMessages += "Error: Invalid Test. Improper buffer reading (high)" + "\n" + activity?.runOnUiThread { + binding.errorMessage.text = + getString(R.string.error_improper_buffer_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(0)?.get(0) + constant = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(0)?.get(1) + fittedAbs4 = gradient?.times(led4Average)?.plus(constant!!)!! + + val slope1 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(1)?.get(0) + val intercept1 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(1)?.get(1) + val calculatedHb1 = (led1Average - intercept1!!) / slope1!! + + val slope2 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(3)?.get(0) + val intercept = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(3)?.get(1) + val calculatedHb2 = (led2Average - intercept!!) / slope2!! + + val slope3 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(2)?.get(0) + val intercept3 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(2)?.get(1) + val calculatedHb3 = (led3Average - intercept3!!) / slope3!! + + val slope4 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(3)?.get(0) + val intercept4 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(3)?.get(1) + val calculatedHb4 = (led4Average - intercept4!!) / slope4!! + + calculatedPredictedDenovixRatio = fittedAbs3.div(fittedAbs1) + + val slope = (led1Average - led2Average) / (435 - 415) + val calculatedSlopeRatio = abs(led3Average / slope) + val slopeClass = slopeRatioClassification(calculatedSlopeRatio) + + if (fittedAbs1 <= fittedAbs2) { + validationError = true + testState.allErrorMessages += "Error: Invalid Test. Problem with de-oxygenation" + "\n" + activity?.runOnUiThread { + binding.errorMessage.text = getString(R.string.error_invalid_test) +// binding.errorMessage.visibility = View.VISIBLE + } + } + + if (fittedAbs1 < 0 || fittedAbs2 < 0 || fittedAbs3 < 0 || fittedAbs4 < 0) { + validationError = true + activity?.runOnUiThread { + binding.errorMessage.text = getString(R.string.error_negative_abs) +// binding.errorMessage.visibility = View.VISIBLE + } + } + + if ((led1Average < 0.7 || led2Average < 0.7 || led3Average < 0.7 || led4Average < 0.7) && calculatedSlopeRatio > 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 + testState.allErrorMessages += "Error: Low Hb. Repeat test" + "\n" + activity?.runOnUiThread { + binding.errorMessage.text = "Error: Low Hb. Repeat test" +// binding.errorMessage.visibility = View.VISIBLE + } + } + + DataHolder.hemoCubeTestData?.apply { + deviceId = sharedPreferences.getString(Constants.DEVICE_ID, "").toString() + led1Buffer = led1BufferForDevice + led2Buffer = led2BufferForDevice + led3Buffer = led3BufferForDevice + led4Buffer = led4BufferForDevice + appVersion = version + this.led1Sample = led1SampleForDevice + this.led2Sample = led2SampleForDevice + this.led3Sample = led3SampleForDevice + this.led4Sample = led4SampleForDevice + this.led1Average = led1Average + this.led2Average = led2Average + this.led3Average = led3Average + this.led4Average = led4Average + this.abs1 = fittedAbs1 + this.abs2 = fittedAbs2 + this.abs3 = fittedAbs3 + this.abs4 = fittedAbs4 + this.hb3 = calculatedHb3 + this.hb4 = calculatedHb4 + this.deviceRatio = deviceRatio + this.calculatedRatio = calculateRatio(deviceRatio) + this.predictedDenovixRatio = calculatedPredictedDenovixRatio + this.slopeRatio = calculatedSlopeRatio + this.coefficients = currentDeviceData?.coefficients?.get(0) + .toString() + ", " + currentDeviceData?.coefficients?.get(1).toString() + this.prdClassification = absorbanceBasedClassification(predictedDenovixRatio) + this.deviceRatioClass = deviceRatioClassification(deviceRatio) + this.slopeRatioClass = slopeClass + this.classificationResult = deviceRatioClass //findResult(calculatedRatio) + hemoCubeViewModel.messages.postValue("${this.deviceRatioClass} ${if (led4Average < 0.17) " - Low Hb" else ""}\n") + if (DataHolder.hemoCubeTestData?.testType == "HB") + hemoCubeViewModel.messages.postValue("Hb: $calculatedHb4") + this.errorMessages = testState.allErrorMessages + this.resultData = deviceLog + 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() + } + } + } catch (e: Exception) { + Toast.makeText( + requireContext(), + R.string.error_processing_device_data, + Toast.LENGTH_SHORT + ).show() + Firebase.crashlytics.recordException(e) + } + } + + fun findResult(calculatedRatio: Double?): String { + try { + hemoCubeViewModel.messages.postValue("result classification") + if (calculatedRatio != null) { + if (calculatedRatio < 0.05) + return getString(R.string.error_repeat_test_higher_volume) + if (calculatedRatio in 0.05..0.155) { + return getString(R.string.normal) + } + if (calculatedRatio in 0.155..0.175) + return getString(R.string.negative_borderline) + if (calculatedRatio in 0.175..0.22) + return getString(R.string.sickle_cell_trait) + if (calculatedRatio in 0.22..0.25) + return getString(R.string.positive_for_sickle_cell) + if (calculatedRatio in 0.25..0.35) + return getString(R.string.sickle_cell_disease) + if (calculatedRatio > 0.35) + return getString(R.string.error_repeat_test_lower_volume) + } 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 deviceRatioClassification(ratio: Double?): String { + try { + if (ratio != null) { + if (ratio in 0.2..0.29) { +// setSubtitleTextColor(R.color.green_2) + return "Normal" + } + if (ratio in 0.29..0.32) + return "Negative Borderline, Repeat Test" + if (ratio in 0.32..0.35) + return "Sickle Cell Trait" + if (ratio in 0.35..0.38) + return "Positive for Sickle Cell. HPLC for Confirmation" + if (ratio in 0.38..0.5) + return "Sickle Cell Disease" + } else { + return "Invalid" + } + } catch (e: Exception) { + handleException(e) + return "Error" + } + return "Invalid" + } + + fun setSubtitleTextColor(colorResId: Int) { + activity?.runOnUiThread { + binding.tvSubtitle4.setTextColor(ContextCompat.getColor(requireContext(), colorResId)) + } + } + + fun handleException(e: Exception) { + showToast(R.string.error_classification) + Firebase.crashlytics.recordException(e) + } + + 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") + if (predictedDenovixRatio != null) { + if (predictedDenovixRatio in 0.0..0.16) { +// activity?.runOnUiThread { +// binding.tvSubtitle4.setTextColor( +// ContextCompat.getColor( +// requireContext(), +// R.color.brightGreen +// ) +// ) +// } + return getString(R.string.normal) + } + if (predictedDenovixRatio in 0.16..0.165) + return getString(R.string.negative_borderline) + if (predictedDenovixRatio in 0.165..0.235) + return getString(R.string.sickle_cell_trait) + if (predictedDenovixRatio in 0.235..0.24) + return getString(R.string.positive_borderline) + if (predictedDenovixRatio in 0.24..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) + } + + private fun showToast(messageResId: Int) { + Toast.makeText(requireContext(), getString(messageResId), Toast.LENGTH_SHORT).show() + } + + private fun startBufferProcess() { + hemoCubeViewModel.progressBar.postValue(true) + + activity?.runOnUiThread { + binding.btnPlacebuffer.visibility = View.GONE + } + + (activity as HemocubeActivity).mService.sendAndListenToHemoCube( + HemoCubeCommands.START_BUFFER_COMMAND, + object : UsbServiceListener { + override fun onUsbRead(data: ByteArray?) {} + + override fun onUsbError(e: Exception?) { + hemoCubeViewModel.progressBar.postValue(false) + } + }) + } + + private fun startSampleProcess() { + hemoCubeViewModel.progressBar.postValue(true) + + (activity as HemocubeActivity).mService.sendAndListenToHemoCube( + HemoCubeCommands.START_SAMPLE, + object : UsbServiceListener { + override fun onUsbRead(data: ByteArray?) {} + + override fun onUsbError(e: Exception?) { + hemoCubeViewModel.progressBar.postValue(false) + } + }) + } + + 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.PRINT_COMMAND, + object : UsbServiceListener { + override fun onUsbRead(data: ByteArray?) {} + + override fun onUsbError(e: Exception?) { + hemoCubeViewModel.progressBar.postValue(false) + } + }) + } + + private fun calculateRatio(ratio: Double): Double { + val coefficient1 = currentDeviceData?.coefficients?.get(0) ?: 0.0 + val coefficient2 = currentDeviceData?.coefficients?.get(1) ?: 0.0 + return coefficient1 * ratio + coefficient2 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/presentation/trueheme/TrueHemeViewModel.kt b/app/src/main/java/com/example/hpostesting/presentation/trueheme/TrueHemeViewModel.kt new file mode 100644 index 0000000..9ebae05 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/presentation/trueheme/TrueHemeViewModel.kt @@ -0,0 +1,533 @@ +package com.example.hpostesting.presentation.trueheme + +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.BatteryManager +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import androidx.work.ExistingPeriodicWorkPolicy +import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkManager +import com.example.hpostesting.data.CsvWriter +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.datasource.LocalFileDataSource +import com.example.hpostesting.data.model.Response +import com.example.hpostesting.data.model.log.UploadLogsResponse +import com.example.hpostesting.data.model.login.LoginRequest +import com.example.hpostesting.data.model.login.LoginResponse +import com.example.hpostesting.data.model.molbioresult.MolbioV2Result +import com.example.hpostesting.data.model.molbioresult.MolbioV2ResultRequest +import com.example.hpostesting.data.model.molbioresult.MolbioV2ResultResponse +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.toHemoCubeTestData +import com.example.hpostesting.data.model.updates.CheckUpdateRequest +import com.example.hpostesting.data.model.updates.CheckUpdateResponse +import com.example.hpostesting.data.model.updates.DeviceUpdateRequest +import com.example.hpostesting.data.repository.Repository +import com.example.hpostesting.domain.CheckUpdateWorker +import com.example.hpostesting.domain.LogFileManager +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody.Companion.asRequestBody +import okhttp3.ResponseBody +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Locale +import java.util.concurrent.TimeUnit +import javax.inject.Inject + +@Suppress("MemberVisibilityCanBePrivate") +@HiltViewModel +class TrueHemeViewModel @Inject constructor( + private val hemoCubeDao: HemoCubeDao, + private val hemoCubeBufferDao: HemoCubeBufferDao, + private val repository: Repository, + private val logFileManager: LogFileManager, + private val localFileDataSource: LocalFileDataSource, + context: Context, +) : ViewModel() { + var isServiceConnected = false + val progressBar = MutableLiveData(false) + private val testDetails = DataHolder.selectedTest?.toHemoCubeTestData() + val messages = MutableLiveData() + private val sharedPreference = + context.getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE) + + private val workManager = WorkManager.getInstance(context) + +// init { +// startPeriodicCheckUpdate() +// } + + val loginResponse = MutableLiveData>() + + val resultUpload = MutableLiveData>() + + val checkUpdate = MutableLiveData>() + + val deviceUpdate = MutableLiveData>() + + val uploadLogs = 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) + } + + 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 login(loginRequest: LoginRequest) = viewModelScope.launch { + loginResponse.postValue(Result.Loading()) + repository.login(loginRequest).let { + loginResponse.postValue(it) + } + } + + fun uploadResult(molbioV2ResultRequest: MolbioV2ResultRequest) = viewModelScope.launch { + resultUpload.postValue(Result.Loading()) + repository.uploadResults(molbioV2ResultRequest).let { + resultUpload.postValue(it) + } + } + + fun checkUpdate(checkUpdateRequest: CheckUpdateRequest) = viewModelScope.launch { + checkUpdate.postValue(Result.Loading()) + repository.checkUpdate(checkUpdateRequest).let { + checkUpdate.postValue(it) + } + } + + fun deviceUpdate(deviceUpdateRequest: DeviceUpdateRequest) = viewModelScope.launch { + deviceUpdate.postValue(Result.Loading()) + repository.deviceUpdate(deviceUpdateRequest).let { + deviceUpdate.postValue(it) + } + } + + fun startPeriodicCheckUpdate() { + val periodicRequest = PeriodicWorkRequestBuilder( + repeatInterval = 1, repeatIntervalTimeUnit = TimeUnit.MINUTES + ).build() + + workManager.enqueueUniquePeriodicWork( + "checkUpdateWorker", ExistingPeriodicWorkPolicy.KEEP, periodicRequest + ) + } + + fun uploadLogs() = viewModelScope.launch { + uploadLogs.postValue(Result.Loading()) + val logFile = logFileManager.createLogFile().let { file -> + val requestBody = file?.asRequestBody("multipart/form-data".toMediaTypeOrNull()) + val multipartFile = + requestBody?.let { MultipartBody.Part.createFormData("logFile", file?.name, it) } + multipartFile?.let { partFile -> + repository.uploadLogs(partFile).let { result -> + uploadLogs.postValue(result) + } + } + } + } + + 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) + } + + else -> {} + } + } catch (e: Exception) { + Log.e("Testdb", "Exception during data upload: ${e.message}") + fireBaseUpload.postValue("Error") + } + } else { + hemoCubeBufferDao.insertAll(bufferCheckData) + fireBaseUpload.postValue("Local") + } + } + + 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") + } + } + } + } + + fun uploadHemoCubeResultToDatabaseforbuffercheckN(bufferCheckData: BufferCheckData) = + viewModelScope.launch { + addResultTestToDbforbuffercheck(bufferCheckData) + } + + fun getDeviceData(deviceId: String?) = viewModelScope.launch { + deviceData.postValue(deviceId?.let { repository.getDeviceDataById(it) }) + } + + 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?.slopeRatio = DataHolder.hemoCubeTestData?.slopeRatio + testDetails?.predictedDenovixRatio = DataHolder.hemoCubeTestData?.predictedDenovixRatio + testDetails?.calculatedRatio = DataHolder.hemoCubeTestData?.calculatedRatio + testDetails?.coefficients = DataHolder.hemoCubeTestData?.coefficients + testDetails?.incubationTime = DataHolder.hemoCubeTestData?.incubationTime.toString() + testDetails?.name = DataHolder.hemoCubeTestData?.name.toString() + testDetails?.birthYear = DataHolder.hemoCubeTestData?.birthYear.toString() + testDetails?.userImageURL = DataHolder.hemoCubeTestData?.userImageURL.toString() + testDetails?.classificationResult = DataHolder.hemoCubeTestData?.classificationResult!! + testDetails?.prdClassification = DataHolder.hemoCubeTestData?.prdClassification.toString() + testDetails?.deviceRatioClass = DataHolder.hemoCubeTestData?.deviceRatioClass.toString() + testDetails?.slopeRatioClass = DataHolder.hemoCubeTestData?.slopeRatioClass.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() + testDetails?.quickCapture = DataHolder.hemoCubeTestData?.quickCapture!! + testDetails?.solution = DataHolder.hemoCubeTestData?.solution + testDetails?.concentration = DataHolder.hemoCubeTestData?.concentration + testDetails?.volume = DataHolder.hemoCubeTestData?.volume + } + + 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 + uploadResult( + MolbioV2ResultRequest( + mutableListOf( + MolbioV2Result( + rawData = testDetails, + analysisId = testDetails._id, + analysisDate = testDetails.testTime, + analysisStatus = testDetails.classificationResult, + thresholds = Constants.BUFFER_INTENSITY_THRESHOLDS[testDetails.deviceId].toString(), + interpretation = testDetails.classificationResult, + testId = testDetails._id, + testTime = testDetails.testTime, + collectionTime = testDetails.testTime, + expiryTime = testDetails.testTime, + ) + ) + ) + ) + hemoCubeDao.insertAll(testDetails) + } + + is Response.Error -> { + Log.e("Testdb", "Error uploading data to Firestore: $response") + fireBaseUpload.postValue("Error") + hemoCubeDao.insertAll(testDetails) + } + + else -> {} + } + } catch (e: Exception) { + Log.e("Testdb", "Exception during data upload: ${e.message}") + fireBaseUpload.postValue("Error") + } + } + } + + 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 updateMolbioFlag(userId: String) = viewModelScope.launch { + hemoCubeDao.updateMolbioFlag(id = userId, true) + } + + fun addUser(userData: HemoCubeTestData) = viewModelScope.launch { + hemoCubeDao.insertAll(userData) + } + + fun deleteById(userId: String) = viewModelScope.launch { + hemoCubeDao.deleteById(id = userId) + } + + + 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 getLocalUserDataForCsv(context: Context): Boolean { + val localUserDataLiveData: LiveData> = hemoCubeDao.getAll() + + // Observe the LiveData to get the actual data when available + localUserDataLiveData.observeForever { localUserData -> + localUserData?.let { + val csvData = mutableListOf>() + + it.forEach { userData -> + csvData.add( + arrayOf( + userData._id, + userData.name, + userData.bloodGroup, + userData.birthYear, + userData.classificationResult, + userData.testTime.toString(), + userData.userImageURL + ) + ) + } + + val csvWriter = CsvWriter(context) + csvWriter.writeCsv("userData.csv", csvData) + + // Remove the observer to avoid leaks + localUserDataLiveData.removeObserver {} + } + } + + return true // Assuming success, you might want to modify this based on your actual logic + } + + 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() + } + + return batteryPct + } + + fun getBatteryTemperature(): Float? { + val batteryTemp: Float? = batteryStatus?.let { intent -> + val temperature = intent.getIntExtra( + BatteryManager.EXTRA_TEMPERATURE, + 0 + ) + temperature.toFloat() / 10 + } + + return batteryTemp + } + + fun getBatteryVoltage(context: Context): Float { + val batteryIntent = + context.registerReceiver( + null, + IntentFilter(Intent.ACTION_BATTERY_CHANGED) + ) + val voltage = batteryIntent?.getIntExtra( + BatteryManager.EXTRA_VOLTAGE, + 0 + ) ?: 0 + + // milli-volts to volts + return voltage.toFloat() / 1000 + } + + + fun getBatteryCapacity(context: Context): Int { + val batteryManager = + context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager + val currentCapacity = + batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER) + + return currentCapacity + } + + 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) + + // Calculate the estimated maximum battery capacity in mAh + val maxCapacity = currentCapacity.toFloat() / designCapacity.toFloat() * 100 + + return maxCapacity + } + + fun createCSV(hemoCubeTestData: List, appContext: Context) = + viewModelScope.launch { + val fileName = "HPOS${getCurrentDate()}.csv" + if (localFileDataSource.exportDataToCSV(fileName, hemoCubeTestData)) { + hemoCubeTestData.forEach { data -> + data.localFlag = true + hemoCubeDao.updateCSVFieldById( + data._id, + true + ) + } + } + } + + fun getCurrentDate(): String { + return SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss", Locale.getDefault() + ).format(Calendar.getInstance().time) + } +} diff --git a/app/src/main/res/layout/activity_trueheme.xml b/app/src/main/res/layout/activity_trueheme.xml new file mode 100644 index 0000000..f959890 --- /dev/null +++ b/app/src/main/res/layout/activity_trueheme.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_trueheme_sample.xml b/app/src/main/res/layout/fragment_trueheme_sample.xml new file mode 100644 index 0000000..7966a1a --- /dev/null +++ b/app/src/main/res/layout/fragment_trueheme_sample.xml @@ -0,0 +1,264 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +