pulse-monitor/backups/backup_20251127/helpers.go

118 lines
2.9 KiB
Go

package main
import (
"fmt"
"io"
"time"
)
// LogTarget specifies where to write the log message
type LogTarget int
const (
Console LogTarget = iota
LogFile
Both
)
// LogLevel specifies the severity/type of log message
type LogLevel int
const (
Debug LogLevel = iota
Info
Warning
Error
)
// Global logger - set once during initialization
var globalLogger io.Writer = nil
// logMessage writes a formatted log message to the specified target(s)
// Debug messages are automatically filtered out when DEBUG_MODE is false
//
// Usage examples:
// logMessage(Console, Info, "SpO2=%d%%, HR=%d bpm", spo2, hr)
// logMessage(Both, Debug, "Before preprocess: %dx%d", cols, rows)
// logMessage(Both, Error, "Layout detection failed: %v", err)
// logMessage(Both, Warning, "Low confidence (#%d)", count)
func logMessage(target LogTarget, level LogLevel, format string, args ...interface{}) {
// Filter debug messages when not in debug mode
if level == Debug && !DEBUG_MODE {
return
}
// Format the message
message := fmt.Sprintf(format, args...)
// Single-letter prefix for fixed-width alignment
var levelChar string
switch level {
case Debug:
levelChar = "D"
case Info:
levelChar = "I"
case Warning:
levelChar = "W"
case Error:
levelChar = "E"
}
// Format with timestamp
timestamp := time.Now().Format("15:04:05.000")
formattedMessage := fmt.Sprintf("[%s] %s %s\n", timestamp, levelChar, message)
// Write to target(s)
switch target {
case Console:
fmt.Print(formattedMessage)
case LogFile:
if globalLogger != nil {
fmt.Fprint(globalLogger, formattedMessage)
}
case Both:
fmt.Print(formattedMessage)
if globalLogger != nil {
fmt.Fprint(globalLogger, formattedMessage)
}
}
}
// logf is DEPRECATED - use logMessage() instead
// Kept temporarily for reference, but no longer used in the codebase
/*
func logf(logger io.Writer, format string, args ...interface{}) {
if logger != nil {
fmt.Fprintf(logger, format, args...)
}
}
*/
// printTimingTable prints timing data in horizontal table format
// Prints header every 20 frames for readability
// Note: Acquire is separate (camera waiting time)
// Note: Total is wall-clock processing time (may not equal sum due to logging/overhead)
// Note: HASS is added in main loop after function returns
func printTimingTable(timing TimingData, showHeader bool) {
if !TIMING_MODE {
return
}
if showHeader {
fmt.Println("")
fmt.Println("Frame | Acquire | Thresh | Prep | Scale | OCR_SpO2 | OCR_HR | Valid | FileIO | HASS | Total")
fmt.Println("------|---------|--------|------|-------|----------|--------|-------|--------|------|-------")
}
fmt.Printf("#%-4d | %5dms | %5dms | %3dms | %4dms | %7dms | %5dms | %4dms | %5dms | %3dms | %4dms\n",
timing.FrameNum,
timing.Acquire,
timing.Threshold,
timing.Preprocess,
timing.Scale,
timing.OCR_SpO2,
timing.OCR_HR,
timing.Validation,
timing.FileIO,
timing.HASS,
timing.Total)
}