Доработка. Журналирование

This commit is contained in:
Maxim
2026-06-09 11:00:20 +03:00
parent ac0e3944ab
commit 354156e557
7 changed files with 537 additions and 44 deletions

146
internal/pipe/reader.go Normal file
View File

@@ -0,0 +1,146 @@
package pipe
import (
"context"
"fmt"
"os"
"time"
"gpio-monitor/internal/adapter"
"gpio-monitor/internal/logger"
)
type PipeReader struct {
buf *adapter.RingBuffer
dataLogger *logger.RotatingLogger
humanLogger *logger.HumanLogger
monitor *logger.Monitor
eventLog *logger.EventLogger
lastAlert map[byte]time.Time // для предотвращения спама алертов
}
func NewPipeReader(
buf *adapter.RingBuffer,
dataLogger *logger.RotatingLogger,
humanLogger *logger.HumanLogger,
monitor *logger.Monitor,
eventLog *logger.EventLogger,
) *PipeReader {
return &PipeReader{
buf: buf,
dataLogger: dataLogger,
humanLogger: humanLogger,
monitor: monitor,
eventLog: eventLog,
lastAlert: make(map[byte]time.Time),
}
}
func (pr *PipeReader) Start(ctx context.Context, pipePath string) {
go func() {
buffer := make([]byte, 4096)
for {
select {
case <-ctx.Done():
return
default:
}
// Проверяем существует ли pipe
if _, err := os.Stat(pipePath); os.IsNotExist(err) {
if pr.eventLog != nil {
pr.eventLog.Event("PIPE_НЕ_НАЙДЕН")
}
time.Sleep(2 * time.Second)
continue
}
f, err := os.OpenFile(pipePath, os.O_RDONLY, 0)
if err != nil {
if pr.eventLog != nil {
pr.eventLog.Event("ОШИБКА_ОТКРЫТИЯ_PIPE")
}
time.Sleep(time.Second)
continue
}
if pr.eventLog != nil {
pr.eventLog.Event("PIPE_ПОДКЛЮЧЕН")
}
for {
n, err := f.Read(buffer)
if err != nil {
f.Close()
if pr.eventLog != nil {
pr.eventLog.Event("PIPE_ОТКЛЮЧЕН")
}
break
}
// Обрабатываем каждый байт
for i := 0; i < n; i++ {
b := buffer[i]
now := time.Now()
// 1. В RAM для UI
pr.buf.Write(b)
// 2. На диск для архива (бинарный)
if pr.dataLogger != nil {
pr.dataLogger.Write(logger.Sample{
Timestamp: now.UnixMicro(),
Value: b,
})
}
// 3. Human-readable лог (только когда есть данные)
if pr.humanLogger != nil {
humanLine := fmt.Sprintf("[%s] Значение GPIO: %d (0x%02X)\n",
now.Format("2006-01-02 15:04:05.000"),
b, b)
pr.humanLogger.Write(humanLine)
}
// 4. Алерт если значение > 10
if b > 10 {
pr.handleAlert(b, now)
}
// 5. Обновляем watchdog
if pr.monitor != nil {
pr.monitor.RecordWrite()
}
}
}
// Пауза перед переподключением
time.Sleep(1 * time.Second)
}
}()
}
func (pr *PipeReader) handleAlert(value byte, timestamp time.Time) {
// Anti-spam: не чаще 1 алерта в секунду для одного значения
if last, exists := pr.lastAlert[value]; exists {
if timestamp.Sub(last) < 1*time.Second {
return
}
}
pr.lastAlert[value] = timestamp
// 1. В event log (JSON)
if pr.eventLog != nil {
pr.eventLog.Event(fmt.Sprintf("ВЫСОКОЕ_ЗНАЧЕНИЕ_%d", value))
}
// 2. В human-readable лог с предупреждением (только на русском)
if pr.humanLogger != nil {
alertLine := fmt.Sprintf("[%s] ВНИМАНИЕ: Обнаружено высокое значение! GPIO = %d (>10)\n",
timestamp.Format("2006-01-02 15:04:05.000"), value)
pr.humanLogger.Write(alertLine)
}
// 3. В консоль больше НЕ пишем (всё уже в логах)
}