optimised to retry usb

This commit is contained in:
sathwikcs
2025-01-06 17:51:54 +05:30
parent 36dc7e68a2
commit 0179173860
7 changed files with 202 additions and 193 deletions

View File

@@ -63,6 +63,7 @@ android {
dependencies { dependencies {
implementation "com.google.dagger:hilt-android:2.46" implementation "com.google.dagger:hilt-android:2.46"
implementation 'androidx.activity:activity:1.8.0' 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" kapt "com.google.dagger:hilt-android-compiler:2.46"
implementation 'androidx.core:core-ktx:1.12.0' implementation 'androidx.core:core-ktx:1.12.0'

View File

@@ -116,6 +116,7 @@ class GalleryFragment : Fragment() {
binding.btnUsbTerminal.setOnClickListener { binding.btnUsbTerminal.setOnClickListener {
startActivity(Intent(requireContext(), UsbTerminalActivity::class.java)) startActivity(Intent(requireContext(), UsbTerminalActivity::class.java))
} }
binding.btnSubmit.setOnClickListener { binding.btnSubmit.setOnClickListener {
startActivity(Intent(requireContext(), HemocubeBufferCheckActivity::class.java)) startActivity(Intent(requireContext(), HemocubeBufferCheckActivity::class.java))
} }

View File

@@ -36,11 +36,7 @@ import androidx.core.content.ContextCompat
import androidx.core.view.get import androidx.core.view.get
import com.example.hpostesting.data.constant.Constants import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.DataHolder 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.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.example.hpostesting.util.UsbService
import com.hoho.android.usbserial.driver.UsbSerialDriver import com.hoho.android.usbserial.driver.UsbSerialDriver
import com.hoho.android.usbserial.driver.UsbSerialProber import com.hoho.android.usbserial.driver.UsbSerialProber
@@ -59,25 +55,37 @@ class UsbTerminalActivity : AppCompatActivity() {
private var mConnection: UsbDeviceConnection? = null private var mConnection: UsbDeviceConnection? = null
lateinit var mService: UsbService lateinit var mService: UsbService
private var deviceId = "" private var deviceId = ""
private var executedCommands: MutableList<String> = mutableListOf()
private var isResuming = false
private val TAG = "Calibration" private val TAG = "Calibration"
private val broadcastReceiver = object : BroadcastReceiver() { private val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
synchronized(this) { synchronized(this) {
val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE) val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { if (UsbManager.ACTION_USB_DEVICE_DETACHED == intent.action) {
device?.apply { // 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) connectUsb(true)
DataHolder.usbConnected.postValue(true) DataHolder.usbConnected.postValue(true)
} }
} else { } else {
onErrorReported("permission denied for device") Log.e(TAG, "USB permission denied")
DataHolder.usbConnected.postValue(true) DataHolder.usbConnected.postValue(false)
} }
} }
} }
} }
@@ -96,6 +104,15 @@ class UsbTerminalActivity : AppCompatActivity() {
} }
} }
fun getExecutedCommands(): List<String> {
return executedCommands
}
fun addExecutedCommand(command: String) {
if (!executedCommands.contains(command)) {
executedCommands.add(command)
}
}
override fun attachBaseContext(newBase: Context?) { override fun attachBaseContext(newBase: Context?) {
val languageCode = LanguageManager.getSavedLanguage(newBase!!) val languageCode = LanguageManager.getSavedLanguage(newBase!!)
LanguageManager.setLocale(newBase, languageCode) LanguageManager.setLocale(newBase, languageCode)
@@ -106,21 +123,28 @@ class UsbTerminalActivity : AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityUsbTerminalBinding.inflate(layoutInflater) binding = ActivityUsbTerminalBinding.inflate(layoutInflater)
setContentView(binding.root) 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() doBoth()
} }
fun doBoth(){ fun doBoth(){
setupListener() setupListener()
connectUsb(false) connectUsb(false)
} }
private fun setupListener() { private fun setupListener() {
DataHolder.usbConnected.observe(this) { DataHolder.usbConnected.observe(this) {
Log.d("USB OBSERVE", "HemoCube called -> $it") Log.d("USB OBSERVE", "HemoCube called -> $it")
if (it) { if (it) {
myMenu?.get(0)?.icon = myMenu?.get(0)?.icon =
ContextCompat.getDrawable(this, R.drawable.ic_baseline_usb_24) ContextCompat.getDrawable(this, R.drawable.ic_baseline_usb_24)
} else { } else {
myMenu?.get(0)?.icon = myMenu?.get(0)?.icon =
ContextCompat.getDrawable(this, R.drawable.ic_baseline_usb_off_24) ContextCompat.getDrawable(this, R.drawable.ic_baseline_usb_off_24)
@@ -131,11 +155,14 @@ class UsbTerminalActivity : AppCompatActivity() {
open fun connectUsb(permissionGranted: Boolean) { open fun connectUsb(permissionGranted: Boolean) {
Log.d(TAG, "connectUsb() called, permission variable = $permissionGranted") Log.d(TAG, "connectUsb() called, permission variable = $permissionGranted")
cleanupUsb() // Clean up before reinitializing
val manager = getSystemService(Context.USB_SERVICE) as UsbManager val manager = getSystemService(Context.USB_SERVICE) as UsbManager
val availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager) val availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager)
if (availableDrivers.isEmpty()) { if (availableDrivers.isEmpty()) {
onErrorReported("No Device is Connected") onErrorReported("No Device Connected")
DataHolder.usbConnected.postValue(false)
} else { } else {
mDriver = availableDrivers[0] mDriver = availableDrivers[0]
mConnection = manager.openDevice(mDriver.device) mConnection = manager.openDevice(mDriver.device)
@@ -143,13 +170,13 @@ class UsbTerminalActivity : AppCompatActivity() {
requestUserPermission(manager, mDriver.device) requestUserPermission(manager, mDriver.device)
} else { } else {
setupService() setupService()
DataHolder.usbConnected.postValue(true)
} }
} }
} }
fun onErrorReported(msg: String) { fun onErrorReported(msg: String) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show() Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
if (!isFinishing) onBackPressed()
} }
private fun moveToNext() { private fun moveToNext() {
@@ -193,12 +220,29 @@ class UsbTerminalActivity : AppCompatActivity() {
return true return true
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
unregisterReceiver(broadcastReceiver)
if (usbTerminalViewModel.isServiceConnected) { if (usbTerminalViewModel.isServiceConnected) {
mService.disconnect() mService.disconnect()
unbindService(connection) unbindService(connection)
usbTerminalViewModel.isServiceConnected = false 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
}
} }

View File

@@ -13,43 +13,33 @@
package com.example.hpostesting.presentation.usb_teminal package com.example.hpostesting.presentation.usb_teminal
// Fragment Class
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.text.InputType
import android.text.method.ScrollingMovementMethod import android.text.method.ScrollingMovementMethod
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.widget.Toast
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.example.hpostesting.data.constant.DataHolder
import com.example.hpostesting.presentation.utils.UsbServiceListener 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 import `in`.sminnovations.hpostesting.databinding.FragmentUsbTerminalBinding
class UsbTerminalFragment : Fragment() { class UsbTerminalFragment : Fragment() {
private lateinit var binding: FragmentUsbTerminalBinding private lateinit var binding: FragmentUsbTerminalBinding
private val usbTerminalViewModel: UsbTerminalViewModel by activityViewModels() private val usbTerminalViewModel: UsbTerminalViewModel by activityViewModels()
private lateinit var sharedPreferences: SharedPreferences private lateinit var sharedPreferences: SharedPreferences
private var startListening = MutableLiveData(false) private var startListening = MutableLiveData(false)
private var resultData: String = "" private var resultData = StringBuilder()
private var commandQueue: MutableList<String> = mutableListOf()
private var fullCommand: String = "" private var currentCommand: String? = null
private var currentCommandIndex = 0 var executedCommands: MutableList<String> = mutableListOf()
private var isCommandInProgress = false
private val commandHistory = StringBuilder()
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?,
): View { ): View {
binding = FragmentUsbTerminalBinding.inflate(inflater, container, false) binding = FragmentUsbTerminalBinding.inflate(inflater, container, false)
sharedPreferences = requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE) sharedPreferences = requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
@@ -58,163 +48,122 @@ class UsbTerminalFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val pastCommands = (activity as? UsbTerminalActivity)?.getExecutedCommands() ?: emptyList()
if (pastCommands.isNotEmpty()) {
resumeCommands(pastCommands)
}
initViews() 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() { private fun initViews() {
with(binding) { binding.messageCommand.movementMethod = ScrollingMovementMethod()
messageCommand.movementMethod = ScrollingMovementMethod()
etCommands.apply { // Observe messages with proper formatting
imeOptions = EditorInfo.IME_ACTION_DONE usbTerminalViewModel.messages.observe(viewLifecycleOwner) { message ->
setOnEditorActionListener { _, actionId, _ -> binding.messageCommand.text = message
if (actionId == EditorInfo.IME_ACTION_DONE) { scrollToBottom()
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()
}
}
}
} }
commandHistory.clear() binding.sendCommand.setOnClickListener {
commandHistory.append("[Session Started]") val commands = binding.etCommands.text.toString().trim()
updateMessageDisplay() if (commands.isNotEmpty()) {
// Process entire command string
addToQueue(listOf(commands))
binding.etCommands.text.clear()
}
}
binding.button.setOnClickListener{
}
listenToHemoCube() listenToHemoCube()
callCommand("I") callCommand("I")
} }
private fun callCommand(command: String) { private fun scrollToBottom() {
commandHistory.append("\n>> $command") binding.messageCommand.post {
updateMessageDisplay() val scrollAmount = binding.messageCommand.layout.getLineTop(binding.messageCommand.lineCount) -
binding.messageCommand.height
fullCommand = command if (scrollAmount > 0) {
currentCommandIndex = 0 binding.messageCommand.scrollTo(0, scrollAmount)
isCommandInProgress = true }
}
usbTerminalViewModel.progressBar.postValue(true)
continueCommand()
} }
private fun continueCommand() {
if (currentCommandIndex >= fullCommand.length) { private fun addToQueue(commands: List<String>) {
// Command execution completed commandQueue.clear()
isCommandInProgress = false currentCommand = null
usbTerminalViewModel.progressBar.postValue(false)
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 return
} }
val currentChar = fullCommand[currentCommandIndex].toString() usbTerminalViewModel.progressBar.postValue(true)
commandHistory.append("\nExecuting command: $currentChar") appendFormattedMessage("Sending command: $command \n")
updateMessageDisplay()
// Send the command and wait for the full response sequence try {
(activity as UsbTerminalActivity).mService.sendAndListenToHemoCubeTxt( (activity as UsbTerminalActivity).mService.sendAndListenToHemoCubeTxt(
currentChar, command,
object : UsbServiceListener { object : UsbServiceListener {
private var responseStage = 0 // 0 for BS, 1 for BC override fun onUsbRead(data: ByteArray?) {
// Keep empty as per original code
}
override fun onUsbRead(data: ByteArray?) { override fun onUsbError(e: Exception?) {
data?.let { activity?.runOnUiThread {
val response = String(it).trim()
commandHistory.append("\nReceived response: $response")
updateMessageDisplay()
// Check response sequence appendFormattedMessage("Error sending command $command: ${e?.message ?: "Unknown error"}")
when (responseStage) { usbTerminalViewModel.progressBar.postValue(false)
0 -> if (response == "BS") { currentCommand = null
responseStage = 1 // Wait for BC
} else {
handleWriteError() processNextCommand()
}
1 -> if (response == "BC") {
responseStage = 0 // Response complete for current command
currentCommandIndex++ // Move to the next character
continueCommand() // Start next command
} else {
handleWriteError()
}
} }
} }
} }
)
override fun onUsbError(e: Exception?) { } catch (e: Exception) {
Log.e("USB", "Error: ${e?.message}") appendFormattedMessage("Error: ${e.localizedMessage}")
(activity as UsbTerminalActivity).doBoth() usbTerminalViewModel.progressBar.postValue(false)
Toast.makeText(context, "USB Write Error: ${e?.localizedMessage}", Toast.LENGTH_SHORT).show() currentCommand = null
} processNextCommand()
}
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 = ""
}
}
)
} }
private fun listenToHemoCube() { private fun listenToHemoCube() {
val fullReadOutput = StringBuilder()
startListening.postValue(true) startListening.postValue(true)
try { try {
@@ -222,53 +171,58 @@ class UsbTerminalFragment : Fragment() {
override fun onUsbRead(data: ByteArray?) { override fun onUsbRead(data: ByteArray?) {
data?.let { data?.let {
val stringData = String(it) val stringData = String(it)
fullReadOutput.append(stringData) resultData.append(stringData)
resultData += stringData
updateMessageDisplay() 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?) { override fun onUsbError(e: Exception?) {
val errorMessage = e!!.localizedMessage
commandHistory.append("\nError: $errorMessage")
updateMessageDisplay()
usbTerminalViewModel.progressBar.postValue(false)
activity?.runOnUiThread { 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) { } catch (e: Exception) {
commandHistory.append("\nError: ${e.localizedMessage}") appendFormattedMessage("Error: ${e.localizedMessage}")
updateMessageDisplay()
usbTerminalViewModel.progressBar.postValue(false)
Firebase.crashlytics.recordException(e)
} }
} }
private fun updateMessageDisplay() { fun resumeCommands(pastCommands: List<String>) {
val fullDisplay = commandHistory.toString() + "\n" + resultData appendFormattedMessage("Resuming execution...")
binding.messageCommand.text = fullDisplay pastCommands.forEach { command ->
if (!commandQueue.contains(command)) {
binding.messageCommand.post { commandQueue.add(command)
val scrollAmount = binding.messageCommand.layout.getLineTop(binding.messageCommand.lineCount) - binding.messageCommand.height
if (scrollAmount > 0) {
binding.messageCommand.scrollTo(0, scrollAmount)
} }
} }
processNextCommand()
} }
private fun clearOutput() {
resultData = ""
commandHistory.clear()
commandHistory.append("[Output Cleared]")
updateMessageDisplay()
}
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
isCommandInProgress = false startListening.value = false
currentCommandIndex = 0 resultData.clear()
fullCommand = "" commandQueue.clear()
currentCommand = null
} }
} }

View File

@@ -23,8 +23,6 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class UsbTerminalViewModel @Inject constructor( class UsbTerminalViewModel @Inject constructor(
private val hemoCubeDao: HemoCubeDao,
private val repository: Repository,
) : ViewModel() { ) : ViewModel() {
var isServiceConnected = false var isServiceConnected = false
val progressBar = MutableLiveData(false) val progressBar = MutableLiveData(false)

View File

@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
</vector>

View File

@@ -17,6 +17,12 @@
android:orientation="vertical" android:orientation="vertical"
android:padding="16dp"> android:padding="16dp">
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Clar All Commands" />
<TextView <TextView
android:id="@+id/messageCommand" android:id="@+id/messageCommand"
android:layout_width="match_parent" android:layout_width="match_parent"