From 0179173860da4daf49d5b19ce7a712753bd2002f Mon Sep 17 00:00:00 2001 From: sathwikcs <85106054+sathwikcs@users.noreply.github.com> Date: Mon, 6 Jan 2025 17:51:54 +0530 Subject: [PATCH] optimised to retry usb --- app/build.gradle | 1 + .../presentation/dashboard/GalleryFragment.kt | 1 + .../usb_teminal/UsbTerminalActivity.kt | 72 +++- .../usb_teminal/UsbTerminalFragment.kt | 308 ++++++++---------- .../usb_teminal/UsbTerminalViewModel.kt | 2 - .../main/res/drawable/baseline_send_24.xml | 5 + .../main/res/layout/fragment_usb_terminal.xml | 6 + 7 files changed, 202 insertions(+), 193 deletions(-) create mode 100644 app/src/main/res/drawable/baseline_send_24.xml diff --git a/app/build.gradle b/app/build.gradle index b880c43..44729d6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -63,6 +63,7 @@ android { dependencies { implementation "com.google.dagger:hilt-android:2.46" implementation 'androidx.activity:activity:1.8.0' + implementation 'androidx.compose.ui:ui-android:1.7.6' kapt "com.google.dagger:hilt-android-compiler:2.46" implementation 'androidx.core:core-ktx:1.12.0' 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 a4dd620..c146740 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 @@ -116,6 +116,7 @@ class GalleryFragment : Fragment() { binding.btnUsbTerminal.setOnClickListener { startActivity(Intent(requireContext(), UsbTerminalActivity::class.java)) } + binding.btnSubmit.setOnClickListener { startActivity(Intent(requireContext(), HemocubeBufferCheckActivity::class.java)) } diff --git a/app/src/main/java/com/example/hpostesting/presentation/usb_teminal/UsbTerminalActivity.kt b/app/src/main/java/com/example/hpostesting/presentation/usb_teminal/UsbTerminalActivity.kt index c0657b1..9e58ab2 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/usb_teminal/UsbTerminalActivity.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/usb_teminal/UsbTerminalActivity.kt @@ -36,11 +36,7 @@ import androidx.core.content.ContextCompat import androidx.core.view.get import com.example.hpostesting.data.constant.Constants import com.example.hpostesting.data.constant.DataHolder -import com.example.hpostesting.data.constant.HemoCubeCommands import com.example.hpostesting.data.constant.LanguageManager -import com.example.hpostesting.presentation.deviceinfo.DeviceFragment -import com.example.hpostesting.presentation.utils.DeviceCommunicationHandler -import com.example.hpostesting.presentation.utils.UsbServiceListener import com.example.hpostesting.util.UsbService import com.hoho.android.usbserial.driver.UsbSerialDriver import com.hoho.android.usbserial.driver.UsbSerialProber @@ -59,25 +55,37 @@ class UsbTerminalActivity : AppCompatActivity() { private var mConnection: UsbDeviceConnection? = null lateinit var mService: UsbService private var deviceId = "" + private var executedCommands: MutableList = mutableListOf() + private var isResuming = false private val TAG = "Calibration" 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 { + if (UsbManager.ACTION_USB_DEVICE_DETACHED == intent.action) { + // Handle USB disconnection + Log.d(TAG, "USB device detached") + cleanupUsb() + DataHolder.usbConnected.postValue(false) + } + + if (UsbManager.ACTION_USB_DEVICE_ATTACHED == intent.action) { + // Handle USB reconnection + Log.d(TAG, "USB device attached") + connectUsb(false) // Attempt reconnection + } else if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { + // Handle USB permission granted + device?.let { connectUsb(true) DataHolder.usbConnected.postValue(true) } } else { - onErrorReported("permission denied for device") - DataHolder.usbConnected.postValue(true) + Log.e(TAG, "USB permission denied") + DataHolder.usbConnected.postValue(false) } } - } } @@ -96,6 +104,15 @@ class UsbTerminalActivity : AppCompatActivity() { } } + fun getExecutedCommands(): List { + return executedCommands + } + + fun addExecutedCommand(command: String) { + if (!executedCommands.contains(command)) { + executedCommands.add(command) + } + } override fun attachBaseContext(newBase: Context?) { val languageCode = LanguageManager.getSavedLanguage(newBase!!) LanguageManager.setLocale(newBase, languageCode) @@ -106,21 +123,28 @@ class UsbTerminalActivity : AppCompatActivity() { super.onCreate(savedInstanceState) binding = ActivityUsbTerminalBinding.inflate(layoutInflater) setContentView(binding.root) -// setSupportActionBar(binding.myToolbar) - supportActionBar?.setDisplayHomeAsUpEnabled(true) + + val filter = IntentFilter() + filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED) + filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED) + registerReceiver(broadcastReceiver, filter) + doBoth() } + fun doBoth(){ 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) @@ -131,11 +155,14 @@ class UsbTerminalActivity : AppCompatActivity() { open fun connectUsb(permissionGranted: Boolean) { Log.d(TAG, "connectUsb() called, permission variable = $permissionGranted") + cleanupUsb() // Clean up before reinitializing + val manager = getSystemService(Context.USB_SERVICE) as UsbManager val availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager) if (availableDrivers.isEmpty()) { - onErrorReported("No Device is Connected") + onErrorReported("No Device Connected") + DataHolder.usbConnected.postValue(false) } else { mDriver = availableDrivers[0] mConnection = manager.openDevice(mDriver.device) @@ -143,13 +170,13 @@ class UsbTerminalActivity : AppCompatActivity() { requestUserPermission(manager, mDriver.device) } else { setupService() + DataHolder.usbConnected.postValue(true) } } } fun onErrorReported(msg: String) { Toast.makeText(this, msg, Toast.LENGTH_SHORT).show() - if (!isFinishing) onBackPressed() } private fun moveToNext() { @@ -193,12 +220,29 @@ class UsbTerminalActivity : AppCompatActivity() { return true } + override fun onDestroy() { super.onDestroy() + unregisterReceiver(broadcastReceiver) if (usbTerminalViewModel.isServiceConnected) { mService.disconnect() unbindService(connection) usbTerminalViewModel.isServiceConnected = false } + cleanupUsb() } + + + + private fun cleanupUsb() { + try { + mService.disconnect() + unbindService(connection) + } catch (e: Exception) { + Log.e(TAG, "Error during USB cleanup: ${e.message}") + } + mConnection?.close() + mConnection = null + } + } \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/presentation/usb_teminal/UsbTerminalFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/usb_teminal/UsbTerminalFragment.kt index 02f244d..4fd27be 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/usb_teminal/UsbTerminalFragment.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/usb_teminal/UsbTerminalFragment.kt @@ -13,43 +13,33 @@ package com.example.hpostesting.presentation.usb_teminal +// Fragment Class + import android.content.Context import android.content.SharedPreferences import android.os.Bundle -import android.os.Handler -import android.os.Looper -import android.text.InputType import android.text.method.ScrollingMovementMethod -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.inputmethod.EditorInfo -import android.widget.Toast import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.MutableLiveData -import com.example.hpostesting.data.constant.DataHolder import com.example.hpostesting.presentation.utils.UsbServiceListener -import com.google.firebase.crashlytics.ktx.crashlytics -import com.google.firebase.ktx.Firebase import `in`.sminnovations.hpostesting.databinding.FragmentUsbTerminalBinding - class UsbTerminalFragment : Fragment() { private lateinit var binding: FragmentUsbTerminalBinding private val usbTerminalViewModel: UsbTerminalViewModel by activityViewModels() private lateinit var sharedPreferences: SharedPreferences private var startListening = MutableLiveData(false) - private var resultData: String = "" - - private var fullCommand: String = "" - private var currentCommandIndex = 0 - private var isCommandInProgress = false - private val commandHistory = StringBuilder() + private var resultData = StringBuilder() + private var commandQueue: MutableList = mutableListOf() + private var currentCommand: String? = null + var executedCommands: MutableList = mutableListOf() override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View { binding = FragmentUsbTerminalBinding.inflate(inflater, container, false) sharedPreferences = requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE) @@ -58,163 +48,122 @@ class UsbTerminalFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + + val pastCommands = (activity as? UsbTerminalActivity)?.getExecutedCommands() ?: emptyList() + if (pastCommands.isNotEmpty()) { + resumeCommands(pastCommands) + } + initViews() - observeUsbConnection() - } - - private fun observeUsbConnection() { - DataHolder.usbConnected.observe(viewLifecycleOwner) { isConnected -> - if (isConnected) { - commandHistory.append("\n[Device Connected]") - listenToHemoCube() - if (isCommandInProgress && currentCommandIndex < fullCommand.length) { - (activity as UsbTerminalActivity).doBoth() - commandHistory.append("\n>> Continuing command: $fullCommand from position $currentCommandIndex") - updateMessageDisplay() - continueCommand() - } - } else { - commandHistory.append("\n[Device Disconnected]") - updateMessageDisplay() - isCommandInProgress = false - } - } - } - override fun onResume() { - super.onResume() - - if (DataHolder.usbConnected.value == true) { - listenToHemoCube() - } else { - commandHistory.append("\n[Waiting for Device Connection]") - updateMessageDisplay() - } - (activity as UsbTerminalActivity).connectUsb(false) - } - - override fun onPause() { - super.onPause() - isCommandInProgress = false - currentCommandIndex = 0 - fullCommand = "" - (activity as UsbTerminalActivity).connectUsb(false) } private fun initViews() { - with(binding) { - messageCommand.movementMethod = ScrollingMovementMethod() + binding.messageCommand.movementMethod = ScrollingMovementMethod() - etCommands.apply { - imeOptions = EditorInfo.IME_ACTION_DONE - setOnEditorActionListener { _, actionId, _ -> - if (actionId == EditorInfo.IME_ACTION_DONE) { - text?.toString()?.trim()?.let { command -> - if (command.isNotEmpty()) { - callCommand(command) - text?.clear() - } - } - true - } else false - } - } - - sendCommand.setOnClickListener { - etCommands.text?.toString()?.trim()?.let { command -> - if (command.isNotEmpty()) { - callCommand(command) - etCommands.text?.clear() - } - } - } + // Observe messages with proper formatting + usbTerminalViewModel.messages.observe(viewLifecycleOwner) { message -> + binding.messageCommand.text = message + scrollToBottom() } - commandHistory.clear() - commandHistory.append("[Session Started]") - updateMessageDisplay() + binding.sendCommand.setOnClickListener { + val commands = binding.etCommands.text.toString().trim() + if (commands.isNotEmpty()) { + // Process entire command string + addToQueue(listOf(commands)) + binding.etCommands.text.clear() + } + } + binding.button.setOnClickListener{ + + } listenToHemoCube() callCommand("I") } - private fun callCommand(command: String) { - commandHistory.append("\n>> $command") - updateMessageDisplay() - - fullCommand = command - currentCommandIndex = 0 - isCommandInProgress = true - - usbTerminalViewModel.progressBar.postValue(true) - continueCommand() + private fun scrollToBottom() { + binding.messageCommand.post { + val scrollAmount = binding.messageCommand.layout.getLineTop(binding.messageCommand.lineCount) - + binding.messageCommand.height + if (scrollAmount > 0) { + binding.messageCommand.scrollTo(0, scrollAmount) + } + } } - private fun continueCommand() { - if (currentCommandIndex >= fullCommand.length) { - // Command execution completed - isCommandInProgress = false - usbTerminalViewModel.progressBar.postValue(false) + + private fun addToQueue(commands: List) { + commandQueue.clear() + currentCommand = null + + commandQueue.addAll(commands) + + // Track commands in the activity + (activity as? UsbTerminalActivity)?.let { activity -> + commands.forEach { command -> + activity.addExecutedCommand(command) + } + } + + appendFormattedMessage("\nNew priority commands: ${commands.joinToString(" ")}") + processNextCommand() + } + + + + private fun appendFormattedMessage(message: String) { + resultData.append("\n").append(message) + usbTerminalViewModel.messages.postValue(resultData.toString()) + scrollToBottom() + } + + private fun processNextCommand() { + if (currentCommand == null && commandQueue.isNotEmpty()) { + currentCommand = commandQueue.removeAt(0) + callCommand(currentCommand!!) + } + } + + private fun callCommand(command: String) { + // Don't process if command was cleared + if (currentCommand == null) { return } - val currentChar = fullCommand[currentCommandIndex].toString() - commandHistory.append("\nExecuting command: $currentChar") - updateMessageDisplay() + usbTerminalViewModel.progressBar.postValue(true) + appendFormattedMessage("Sending command: $command \n") - // Send the command and wait for the full response sequence - (activity as UsbTerminalActivity).mService.sendAndListenToHemoCubeTxt( - currentChar, - object : UsbServiceListener { - private var responseStage = 0 // 0 for BS, 1 for BC + try { + (activity as UsbTerminalActivity).mService.sendAndListenToHemoCubeTxt( + command, + object : UsbServiceListener { + override fun onUsbRead(data: ByteArray?) { + // Keep empty as per original code + } - override fun onUsbRead(data: ByteArray?) { - data?.let { - val response = String(it).trim() - commandHistory.append("\nReceived response: $response") - updateMessageDisplay() + override fun onUsbError(e: Exception?) { + activity?.runOnUiThread { - // Check response sequence - when (responseStage) { - 0 -> if (response == "BS") { - responseStage = 1 // Wait for BC - } else { - handleWriteError() - } - 1 -> if (response == "BC") { - responseStage = 0 // Response complete for current command - currentCommandIndex++ // Move to the next character - continueCommand() // Start next command - } else { - handleWriteError() - } + appendFormattedMessage("Error sending command $command: ${e?.message ?: "Unknown error"}") + usbTerminalViewModel.progressBar.postValue(false) + currentCommand = null + + + processNextCommand() } } } - - override fun onUsbError(e: Exception?) { - Log.e("USB", "Error: ${e?.message}") - (activity as UsbTerminalActivity).doBoth() - Toast.makeText(context, "USB Write Error: ${e?.localizedMessage}", Toast.LENGTH_SHORT).show() - } - - private fun handleWriteError() { - activity?.runOnUiThread { - Toast.makeText(context, "Reconnecting USB...", Toast.LENGTH_SHORT).show() - } - (activity as UsbTerminalActivity).doBoth() - // Optionally reset commands - isCommandInProgress = false - currentCommandIndex = 0 - fullCommand = "" - } - - } - ) + ) + } catch (e: Exception) { + appendFormattedMessage("Error: ${e.localizedMessage}") + usbTerminalViewModel.progressBar.postValue(false) + currentCommand = null + processNextCommand() + } } - private fun listenToHemoCube() { - val fullReadOutput = StringBuilder() startListening.postValue(true) try { @@ -222,53 +171,58 @@ class UsbTerminalFragment : Fragment() { override fun onUsbRead(data: ByteArray?) { data?.let { val stringData = String(it) - fullReadOutput.append(stringData) - resultData += stringData - updateMessageDisplay() + resultData.append(stringData) + + activity?.runOnUiThread { + usbTerminalViewModel.messages.postValue(resultData.toString()) + scrollToBottom() + + // Check for command completion + if (currentCommand != null && stringData.contains("${currentCommand}C")) { + currentCommand = null + usbTerminalViewModel.progressBar.postValue(false) + + // If there are no more commands in queue, clear completion flags + if (commandQueue.isEmpty()) { + appendFormattedMessage("\nAll commands completed") + } else { + processNextCommand() + } + } + } } } override fun onUsbError(e: Exception?) { - val errorMessage = e!!.localizedMessage - commandHistory.append("\nError: $errorMessage") - updateMessageDisplay() - usbTerminalViewModel.progressBar.postValue(false) activity?.runOnUiThread { - Toast.makeText(context, "USB Error: ${e.message}", Toast.LENGTH_SHORT).show() + appendFormattedMessage("Error: ${e?.localizedMessage ?: "Unknown error"}") + usbTerminalViewModel.progressBar.postValue(false) + currentCommand = null + processNextCommand() } } }) } catch (e: Exception) { - commandHistory.append("\nError: ${e.localizedMessage}") - updateMessageDisplay() - usbTerminalViewModel.progressBar.postValue(false) - Firebase.crashlytics.recordException(e) + appendFormattedMessage("Error: ${e.localizedMessage}") } } - private fun updateMessageDisplay() { - val fullDisplay = commandHistory.toString() + "\n" + resultData - binding.messageCommand.text = fullDisplay - - binding.messageCommand.post { - val scrollAmount = binding.messageCommand.layout.getLineTop(binding.messageCommand.lineCount) - binding.messageCommand.height - if (scrollAmount > 0) { - binding.messageCommand.scrollTo(0, scrollAmount) + fun resumeCommands(pastCommands: List) { + appendFormattedMessage("Resuming execution...") + pastCommands.forEach { command -> + if (!commandQueue.contains(command)) { + commandQueue.add(command) } } + processNextCommand() } - private fun clearOutput() { - resultData = "" - commandHistory.clear() - commandHistory.append("[Output Cleared]") - updateMessageDisplay() - } override fun onDestroyView() { super.onDestroyView() - isCommandInProgress = false - currentCommandIndex = 0 - fullCommand = "" + startListening.value = false + resultData.clear() + commandQueue.clear() + currentCommand = null } } \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/presentation/usb_teminal/UsbTerminalViewModel.kt b/app/src/main/java/com/example/hpostesting/presentation/usb_teminal/UsbTerminalViewModel.kt index d949d24..30ca41f 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/usb_teminal/UsbTerminalViewModel.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/usb_teminal/UsbTerminalViewModel.kt @@ -23,8 +23,6 @@ import javax.inject.Inject @HiltViewModel class UsbTerminalViewModel @Inject constructor( - private val hemoCubeDao: HemoCubeDao, - private val repository: Repository, ) : ViewModel() { var isServiceConnected = false val progressBar = MutableLiveData(false) diff --git a/app/src/main/res/drawable/baseline_send_24.xml b/app/src/main/res/drawable/baseline_send_24.xml new file mode 100644 index 0000000..014a036 --- /dev/null +++ b/app/src/main/res/drawable/baseline_send_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/layout/fragment_usb_terminal.xml b/app/src/main/res/layout/fragment_usb_terminal.xml index e214e11..82abc1c 100644 --- a/app/src/main/res/layout/fragment_usb_terminal.xml +++ b/app/src/main/res/layout/fragment_usb_terminal.xml @@ -17,6 +17,12 @@ android:orientation="vertical" android:padding="16dp"> +