frigate/uptime-go/uptime/monitor.go

113 lines
2.8 KiB
Go
Raw Normal View History

package main
import (
2024-10-09 19:47:17 -04:00
"fmt"
"log/slog"
2024-10-09 19:47:17 -04:00
"strings"
"time"
)
import (
"frigate/uptime/notify"
2024-10-09 19:47:17 -04:00
"frigate/uptime/ping"
)
type CameraMonitor struct {
2024-10-09 19:47:17 -04:00
pingInterval time.Duration
pingTimeout time.Duration
consecutiveDownThreshold int
downtime map[string]int
notify.Notifier
}
func NewCameraMonitor(pingInterval time.Duration, pingTimeout time.Duration, consecutiveDownThreshold int, notifier notify.Notifier) CameraMonitor {
return CameraMonitor{
2024-10-09 19:47:17 -04:00
pingInterval: pingInterval,
pingTimeout: pingTimeout,
consecutiveDownThreshold: consecutiveDownThreshold,
2024-10-09 19:47:17 -04:00
downtime: make(map[string]int),
Notifier: notifier,
}
}
func (c CameraMonitor) onCameraUp(camera string) {
c.SendNotification(camera, true)
2024-10-09 19:47:17 -04:00
slog.Info(fmt.Sprintf("%s camera is back online!", camera))
}
func (c CameraMonitor) onCameraDown(camera string) {
c.SendNotification(camera, false)
2024-10-09 19:47:17 -04:00
slog.Info(fmt.Sprintf("%s camera is offline!", camera))
}
func (c CameraMonitor) onCameraPingResult(camera string, online bool) {
if online {
if c.downtime[camera] >= c.consecutiveDownThreshold {
c.onCameraUp(camera)
}
c.downtime[camera] = 0
} else {
c.downtime[camera] += 1
if c.downtime[camera] == c.consecutiveDownThreshold {
c.onCameraDown(camera)
}
}
}
func (c CameraMonitor) Run(cameras map[string]string) {
type pingResult struct {
camera string
2024-10-09 19:47:17 -04:00
online bool
}
var pingResultChannel = make(chan pingResult, 4)
for {
var unknownPingResultCameras = make(map[string]bool, len(cameras))
var startTime = time.Now()
var timeoutChannel = time.After(c.pingTimeout)
// Start all pings
for camera, host := range cameras {
go func() {
pingResultChannel <- pingResult{
camera: camera,
2024-10-09 19:47:17 -04:00
online: ping.VideoPing(host),
}
}()
unknownPingResultCameras[camera] = true
}
timeout:
// Await ping results or timeout
for range cameras {
select {
case cameraPingResult := <-pingResultChannel:
2024-10-09 19:47:17 -04:00
slog.Debug(cameraPingResult.camera, "online", cameraPingResult.online)
c.onCameraPingResult(cameraPingResult.camera, cameraPingResult.online)
delete(unknownPingResultCameras, cameraPingResult.camera) // Maintain set of cameras with unknown ping status
case <-timeoutChannel:
2024-10-09 19:47:17 -04:00
var b strings.Builder
for camera := range unknownPingResultCameras {
if b.Len() > 0 {
b.WriteString(", ")
}
b.WriteString(camera)
}
slog.Warn("Timed out waiting for ping result", "cameras", b.String())
break timeout
}
}
// Handle timed out camera pings
for camera := range unknownPingResultCameras {
c.onCameraPingResult(camera, false)
}
var sleepDuration = c.pingInterval - time.Since(startTime)
2024-10-09 19:47:17 -04:00
slog.Debug("Sleeping", "seconds", sleepDuration)
time.Sleep(sleepDuration)
}
}