feat: implement telemetry persistence, data visualization, and improved bluetooth stream parsing with frame statistics (#9)
Build and Release APK / build-and-release (push) Successful in 10m7s
Build and Release APK / build-and-release (push) Successful in 10m7s
Reviewed-on: #9 Co-authored-by: Gandalf <gordon@i3omb.com> Co-committed-by: Gandalf <gordon@i3omb.com>
This commit was merged in pull request #9.
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
package com.example.esp32aldldashboard.logging
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.provider.MediaStore
|
||||
import java.io.OutputStream
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Logs raw binary ALDL data streams for debugging purposes.
|
||||
* Records full 27-byte frames (AA 55 header + 25-byte payload) to Downloads/ALDLLogs/
|
||||
*/
|
||||
class RawStreamLogger(private val context: Context) {
|
||||
|
||||
private var currentOutputStream: OutputStream? = null
|
||||
private var currentUri: Uri? = null
|
||||
private var isRecording = false
|
||||
|
||||
/**
|
||||
* Starts a new raw recording session.
|
||||
* @return true if recording started successfully, false otherwise
|
||||
*/
|
||||
fun startRecording(): Boolean {
|
||||
if (isRecording) return true
|
||||
|
||||
val timeStamp: String = SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.US).format(Date())
|
||||
val fileName = "raw_$timeStamp.bin"
|
||||
|
||||
return try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
val resolver = context.contentResolver
|
||||
val contentValues = ContentValues().apply {
|
||||
put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
|
||||
put(MediaStore.MediaColumns.MIME_TYPE, "application/octet-stream")
|
||||
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + "/ALDLLogs")
|
||||
}
|
||||
|
||||
val uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
|
||||
if (uri != null) {
|
||||
currentUri = uri
|
||||
currentOutputStream = resolver.openOutputStream(uri)
|
||||
isRecording = true
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
// Legacy support not implemented for this debug feature
|
||||
false
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a single 27-byte frame (including AA 55 header).
|
||||
* @param frame The complete 27-byte frame to log
|
||||
*/
|
||||
fun logFrame(frame: ByteArray) {
|
||||
if (!isRecording || frame.size != 27) return
|
||||
|
||||
try {
|
||||
currentOutputStream?.write(frame)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the current recording session and closes the file.
|
||||
*/
|
||||
fun stopRecording() {
|
||||
if (!isRecording) return
|
||||
|
||||
try {
|
||||
currentOutputStream?.flush()
|
||||
currentOutputStream?.close()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
currentOutputStream = null
|
||||
currentUri = null
|
||||
isRecording = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a recording session is currently active.
|
||||
*/
|
||||
fun isRecording(): Boolean = isRecording
|
||||
}
|
||||
Reference in New Issue
Block a user