101 lines
3.5 KiB
Kotlin
101 lines
3.5 KiB
Kotlin
package com.example.esp32aldldashboard.logging
|
|
|
|
import android.content.ContentValues
|
|
import android.content.Context
|
|
import android.os.Build
|
|
import android.os.Environment
|
|
import android.provider.MediaStore
|
|
import com.example.esp32aldldashboard.parser.ALDLFrame
|
|
import java.io.OutputStream
|
|
import java.text.SimpleDateFormat
|
|
import java.util.Date
|
|
import java.util.Locale
|
|
|
|
class CsvLogger(private val context: Context) {
|
|
|
|
private var currentOutputStream: OutputStream? = null
|
|
|
|
fun startNewSession(isSimulation: Boolean): Boolean {
|
|
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())
|
|
val fileName = "ALDL_Log_${if(isSimulation) "SIM_" else ""}$timeStamp.csv"
|
|
|
|
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, "text/csv")
|
|
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + "/ALDLLogs")
|
|
}
|
|
|
|
val uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
|
|
if (uri != null) {
|
|
currentOutputStream = resolver.openOutputStream(uri)
|
|
}
|
|
} else {
|
|
// For older Android versions, we'd need WRITE_EXTERNAL_STORAGE and write to Environment.getExternalStoragePublicDirectory.
|
|
// We'll skip legacy support for this specific snippet to keep it concise, assuming target is modern Android.
|
|
return false
|
|
}
|
|
|
|
// Write CSV Header
|
|
val header = "Timestamp,Raw_Hex,RPM,Coolant_C,Coolant_F,MAP_kPa,MAP_Volts,TPS_Volts,O2_mV,Battery_V,Spark_Adv,IAC,BPW_ms,Speed_MPH,MAT_C,MAT_F,BLM,INT,EGR_Duty,Rich_Crosses,Closed_Loop,Rich\n"
|
|
currentOutputStream?.write(header.toByteArray())
|
|
return true
|
|
} catch (e: Exception) {
|
|
e.printStackTrace()
|
|
return false
|
|
}
|
|
}
|
|
|
|
fun logFrame(frame: ALDLFrame) {
|
|
val out = currentOutputStream ?: return
|
|
|
|
val hexString = frame.rawBytes.joinToString(" ") { String.format("%02X", it) }
|
|
val dateString = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US).format(Date(frame.timestamp))
|
|
|
|
val row = String.format(
|
|
Locale.US,
|
|
"%s,%s,%d,%.1f,%.1f,%.1f,%.3f,%.3f,%.1f,%.1f,%.1f,%d,%.2f,%d,%.1f,%.1f,%d,%d,%.1f,%d,%b,%b\n",
|
|
dateString,
|
|
hexString,
|
|
frame.engineSpeedRpm,
|
|
frame.coolantTempC,
|
|
frame.coolantTempF,
|
|
frame.mapKpa,
|
|
frame.mapVolts,
|
|
frame.tpsVolts,
|
|
frame.o2SensorMv,
|
|
frame.batteryVolts,
|
|
frame.sparkAdvance,
|
|
frame.iacPosition,
|
|
frame.bpwMs,
|
|
frame.vehicleSpeedMPH,
|
|
frame.matC,
|
|
frame.matF,
|
|
frame.blm,
|
|
frame.integrator,
|
|
frame.egrDutyCycle,
|
|
frame.richLeanCrosses,
|
|
frame.isClosedLoop,
|
|
frame.isRich
|
|
)
|
|
|
|
try {
|
|
out.write(row.toByteArray())
|
|
} catch (e: Exception) {
|
|
e.printStackTrace()
|
|
}
|
|
}
|
|
|
|
fun endSession() {
|
|
try {
|
|
currentOutputStream?.close()
|
|
} catch (e: Exception) {
|
|
e.printStackTrace()
|
|
} finally {
|
|
currentOutputStream = null
|
|
}
|
|
}
|
|
}
|