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 {
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'

View File

@@ -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))
}

View File

@@ -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<String> = 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<String> {
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
}
}

View File

@@ -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<String> = mutableListOf()
private var currentCommand: String? = null
var executedCommands: MutableList<String> = 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<String>) {
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<String>) {
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
}
}

View File

@@ -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)

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:padding="16dp">
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Clar All Commands" />
<TextView
android:id="@+id/messageCommand"
android:layout_width="match_parent"