Files
go-service/internal/pipe/reader.go

146 lines
3.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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. В консоль больше НЕ пишем (всё уже в логах)
}