Annotated debug panel of to/from fields with hex form (#830)

This commit is contained in:
Mike Cumings 2024-02-02 18:55:41 -08:00 committed by GitHub
parent d75188f03c
commit e32a1dadea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 130 additions and 3 deletions

View file

@ -42,6 +42,19 @@ data class MeshLog(@PrimaryKey val uuid: String,
return null
}
val myNodeInfo: MeshProtos.MyNodeInfo?
get() {
if (message_type == "MyNodeInfo") {
val builder = MeshProtos.MyNodeInfo.newBuilder()
try {
TextFormat.getParser().merge(raw_message, builder)
return builder.build()
} catch (e: IOException) {
}
}
return null
}
val position: MeshProtos.Position?
get() {
return meshPacket?.run {

View file

@ -1,21 +1,25 @@
package com.geeksville.mesh.ui
import android.content.Context
import android.text.SpannedString
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.text.toSpannable
import androidx.recyclerview.widget.RecyclerView
import com.geeksville.mesh.R
import com.geeksville.mesh.database.entity.MeshLog
import java.text.DateFormat
import java.util.*
import java.util.Date
class DebugAdapter internal constructor(
context: Context
) : RecyclerView.Adapter<DebugAdapter.DebugViewHolder>() {
private val inflater: LayoutInflater = LayoutInflater.from(context)
private val colorAnnotation = ContextCompat.getColor(context, R.color.colorAnnotation)
private var logs = emptyList<MeshLog>()
private val timeFormat: DateFormat =
@ -35,11 +39,36 @@ class DebugAdapter internal constructor(
override fun onBindViewHolder(holder: DebugViewHolder, position: Int) {
val current = logs[position]
holder.logTypeView.text = current.message_type
holder.logRawMessage.text = current.raw_message
holder.logRawMessage.text = annotateMessage(current)
val date = Date(current.received_date)
holder.logDateReceivedView.text = timeFormat.format(date)
}
/**
* Enhance the raw message by visually distinguishing the annotations prior to when
* the data was added to the database.
*
* @see com.geeksville.mesh.ui.DebugFragment.annotateMeshLogs
*/
private fun annotateMessage(current: MeshLog): CharSequence {
val spannable = current.raw_message.toSpannable()
REGEX_ANNOTATED_NODE_ID.findAll(spannable).toList().reversed().forEach {
spannable.setSpan(
android.text.style.StyleSpan(android.graphics.Typeface.ITALIC),
it.range.first,
it.range.last + 1,
SpannedString.SPAN_EXCLUSIVE_EXCLUSIVE
)
spannable.setSpan(
android.text.style.ForegroundColorSpan(colorAnnotation),
it.range.first,
it.range.last + 1,
SpannedString.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
return spannable
}
internal fun setLogs(logs: List<MeshLog>) {
this.logs = logs
notifyDataSetChanged()
@ -47,4 +76,10 @@ class DebugAdapter internal constructor(
override fun getItemCount() = logs.size
private companion object {
/**
* Regex to match the node ID annotations in the MeshLog raw message text.
*/
val REGEX_ANNOTATED_NODE_ID = Regex("\\(![0-9a-fA-F]{8}\\)$", RegexOption.MULTILINE)
}
}

View file

@ -9,10 +9,16 @@ import androidx.fragment.app.activityViewModels
import androidx.lifecycle.asLiveData
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.geeksville.mesh.CoroutineDispatchers
import com.geeksville.mesh.R
import com.geeksville.mesh.database.entity.MeshLog
import com.geeksville.mesh.databinding.FragmentDebugBinding
import com.geeksville.mesh.model.UIViewModel
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import java.util.Locale
import javax.inject.Inject
@AndroidEntryPoint
class DebugFragment : Fragment() {
@ -24,6 +30,9 @@ class DebugFragment : Fragment() {
private val model: UIViewModel by activityViewModels()
@Inject
lateinit var dispatchers: CoroutineDispatchers
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
@ -47,7 +56,11 @@ class DebugFragment : Fragment() {
binding.closeButton.setOnClickListener {
parentFragmentManager.popBackStack()
}
model.meshLog.asLiveData().observe(viewLifecycleOwner) { logs ->
model.meshLog
.map(this::annotateMeshLogs)
.flowOn(dispatchers.default)
.asLiveData()
.observe(viewLifecycleOwner) { logs ->
logs?.let { adapter.setLogs(it) }
}
}
@ -56,4 +69,68 @@ class DebugFragment : Fragment() {
super.onDestroyView()
_binding = null
}
/**
* Transform the input list by enhancing the raw message with annotations.
*/
private fun annotateMeshLogs(logs: List<MeshLog>): List<MeshLog> {
return logs.map { meshLog ->
val annotated = when (meshLog.message_type) {
"Packet" -> {
meshLog.meshPacket?.let { packet ->
annotateRawMessage(meshLog.raw_message, packet.from, packet.to)
}
}
"NodeInfo" -> {
meshLog.nodeInfo?.let { nodeInfo ->
annotateRawMessage(meshLog.raw_message, nodeInfo.num)
}
}
"MyNodeInfo" -> {
meshLog.myNodeInfo?.let { nodeInfo ->
annotateRawMessage(meshLog.raw_message, nodeInfo.myNodeNum)
}
}
else -> null
}
if (annotated == null) {
meshLog
} else {
meshLog.copy(raw_message = annotated)
}
}
}
/**
* Annotate the raw message string with the node IDs provided, in hex, if they are present.
*/
private fun annotateRawMessage(rawMessage: String, vararg nodeIds: Int): String {
val msg = StringBuilder(rawMessage)
var mutated = false
nodeIds.forEach { nodeId ->
mutated = mutated or msg.annotateNodeId(nodeId)
}
return if (mutated) {
return msg.toString()
} else {
rawMessage
}
}
/**
* Look for a single node ID integer in the string and annotate it with the hex equivalent
* if found.
*/
private fun StringBuilder.annotateNodeId(nodeId: Int): Boolean {
val nodeIdStr = nodeId.toUInt().toString()
indexOf(nodeIdStr).takeIf { it >= 0 }?.let { idx ->
insert(idx + nodeIdStr.length, " (${nodeId.asNodeId()})")
return true
}
return false
}
private fun Int.asNodeId(): String {
return "!%08x".format(Locale.getDefault(), this)
}
}

View file

@ -10,4 +10,5 @@
<color name="colorMenuItem">#FFFFFF</color>
<color name="selectedColor">#67EA94</color>
<color name="unselectedColor">#AAAAAA</color>
<color name="colorAnnotation">#039BE5</color>
</resources>

View file

@ -12,4 +12,5 @@
<color name="unselectedColor">#212121</color>
<color name="buttonColor">#67EA94</color>
<color name="colourGrey">#535353</color>
<color name="colorAnnotation">#0288D1</color>
</resources>