118 lines
2.9 KiB
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)
|
|
}
|