graphicek/server/services/alerts_service.go

282 lines
6.8 KiB
Go
Raw Normal View History

2024-03-29 09:55:51 +01:00
package services
import (
"basic-sensor-receiver/integrations"
2024-03-29 20:49:08 +01:00
"basic-sensor-receiver/models"
2024-03-29 09:55:51 +01:00
"encoding/json"
"time"
)
type AlertsService struct {
ctx *Context
}
2024-03-29 20:49:08 +01:00
func evaluateSensorValueCondition(condition *models.AlertConditionSensorValue, value *sensorValue) bool {
2024-03-29 09:55:51 +01:00
switch condition.Condition {
case "less":
return value.Value < condition.Value
case "more":
return value.Value > condition.Value
case "equal":
return value.Value == condition.Value
}
return false
}
func (s *AlertsService) EvaluateAlerts() error {
alerts, err := s.GetList()
if err != nil {
return err
}
for _, alert := range alerts {
err := s.EvaluateAlert(alert)
if err != nil {
return err
}
}
return nil
}
2024-03-31 09:50:09 +02:00
func unitToSeconds(unit string) float64 {
2024-03-29 20:49:08 +01:00
switch unit {
case "s":
return 1
case "m":
return 60
case "h":
return 3600
case "d":
return 86400
}
return 0
}
func (s *AlertsService) EvaluateAlert(alert *models.AlertItem) error {
2024-03-29 09:55:51 +01:00
condition := map[string]interface{}{}
err := json.Unmarshal([]byte(alert.Condition), &condition)
if err != nil {
return err
}
newStatus := alert.LastStatus
2024-03-29 20:49:08 +01:00
lastValue := float64(0)
2024-03-29 09:55:51 +01:00
conditionMet := false
sensorId := int64(-1)
2024-03-29 20:49:08 +01:00
sensorValueCondition := models.AlertConditionSensorValue{}
sensorLastContactCondition := models.AlertConditionSensorLastContact{}
2024-03-29 09:55:51 +01:00
switch condition["type"].(string) {
case "sensor_value":
{
2024-03-29 20:49:08 +01:00
sensorValueCondition = models.AlertConditionSensorValue{
2024-03-31 09:50:09 +02:00
SensorId: int64(condition["sensorId"].(float64)),
2024-03-29 09:55:51 +01:00
Condition: condition["condition"].(string),
Value: condition["value"].(float64),
}
2024-03-29 20:49:08 +01:00
value, err := s.ctx.Services.SensorValues.GetLatest(sensorValueCondition.SensorId, time.Now().Unix())
lastValue = value.Value
sensorId = int64(sensorValueCondition.SensorId)
2024-03-29 09:55:51 +01:00
if err != nil {
return err
}
2024-03-29 20:49:08 +01:00
conditionMet = evaluateSensorValueCondition(&sensorValueCondition, value)
2024-03-29 09:55:51 +01:00
break
}
2024-03-29 20:49:08 +01:00
case "sensor_last_contact":
{
sensorLastContactCondition := models.AlertConditionSensorLastContact{
2024-03-31 09:50:09 +02:00
SensorId: int64(condition["sensorId"].(float64)),
Value: condition["value"].(float64),
2024-03-29 20:49:08 +01:00
ValueUnit: condition["valueUnit"].(string),
}
value, err := s.ctx.Services.SensorValues.GetLatest(sensorLastContactCondition.SensorId, time.Now().Unix())
lastValue = float64(value.Timestamp)
2024-03-29 09:55:51 +01:00
2024-03-29 20:49:08 +01:00
if err != nil {
return err
}
2024-03-29 09:55:51 +01:00
2024-03-31 09:50:09 +02:00
conditionInSec := int64(sensorLastContactCondition.Value * unitToSeconds(sensorLastContactCondition.ValueUnit))
2024-03-29 09:55:51 +01:00
2024-03-29 20:49:08 +01:00
conditionMet = time.Now().Unix()-value.Timestamp > conditionInSec
break
}
2024-03-29 09:55:51 +01:00
}
if conditionMet {
2024-03-31 09:50:09 +02:00
if alert.LastStatus == "good" {
newStatus = "pending"
} else if alert.LastStatus == "pending" {
if time.Now().Unix()-alert.LastStatusAt >= alert.TriggerInterval {
2024-03-29 09:55:51 +01:00
newStatus = "alerting"
}
}
2024-03-31 09:50:09 +02:00
} else {
if alert.LastStatus == "alerting" {
newStatus = "pending"
} else if alert.LastStatus == "pending" {
if time.Now().Unix()-alert.LastStatusAt >= alert.TriggerInterval {
newStatus = "good"
}
}
2024-03-29 09:55:51 +01:00
}
if newStatus != alert.LastStatus {
s.ctx.DB.Exec("UPDATE alerts SET last_status = ?, last_status_at = ? WHERE id = ?", newStatus, time.Now().Unix(), alert.Id)
2024-03-31 09:50:09 +02:00
if newStatus == "alerting" {
sensor, err := s.ctx.Services.Sensors.GetById(sensorId)
if err != nil {
return err
}
2024-03-29 09:55:51 +01:00
2024-03-31 09:50:09 +02:00
contactPoint, err := s.ctx.Services.ContactPoints.GetById(alert.ContactPointId)
if err != nil {
return err
}
2024-03-29 09:55:51 +01:00
2024-03-31 09:50:09 +02:00
dispatchService, err := contactPoint.getService(s.ctx)
if err != nil {
return err
}
2024-03-29 09:55:51 +01:00
2024-03-31 09:50:09 +02:00
err = dispatchService.ProcessEvent(&integrations.ContactPointEvent{
Type: integrations.ContactPointEventAlertTriggered,
AlertTriggeredEvent: &integrations.ContactPointAlertTriggeredEvent{
Alert: alert,
Sensor: sensor,
SensorValueCondition: &sensorValueCondition,
SensorLastContactCondition: &sensorLastContactCondition,
LastValue: lastValue,
},
}, contactPoint.TypeConfig)
2024-03-29 09:55:51 +01:00
2024-03-31 09:50:09 +02:00
if err != nil {
return err
}
2024-03-29 09:55:51 +01:00
}
}
return nil
}
2024-03-29 20:49:08 +01:00
func (s *AlertsService) GetList() ([]*models.AlertItem, error) {
2024-03-31 09:50:09 +02:00
rows, err := s.ctx.DB.Query("SELECT id, contact_point_id, name, condition, trigger_interval, last_status, last_status_at FROM alerts ORDER BY name ASC")
2024-03-29 09:55:51 +01:00
if err != nil {
return nil, err
}
defer rows.Close()
2024-03-29 20:49:08 +01:00
alerts := []*models.AlertItem{}
2024-03-29 09:55:51 +01:00
for rows.Next() {
2024-03-29 20:49:08 +01:00
alert := models.AlertItem{}
2024-03-29 09:55:51 +01:00
2024-03-31 09:50:09 +02:00
err := rows.Scan(&alert.Id, &alert.ContactPointId, &alert.Name, &alert.Condition, &alert.TriggerInterval, &alert.LastStatus, &alert.LastStatusAt)
2024-03-29 09:55:51 +01:00
if err != nil {
return nil, err
}
alerts = append(alerts, &alert)
}
err = rows.Err()
if err != nil {
return nil, err
}
return alerts, nil
}
2024-03-29 20:49:08 +01:00
func (s *AlertsService) GetById(id int64) (*models.AlertItem, error) {
alert := models.AlertItem{}
2024-03-29 09:55:51 +01:00
2024-03-31 09:50:09 +02:00
row := s.ctx.DB.QueryRow("SELECT id, contact_point_id, name, condition, trigger_interval, last_status, last_status_at FROM alerts WHERE id = ?", id)
err := row.Scan(&alert.Id, &alert.ContactPointId, &alert.Name, &alert.Condition, &alert.TriggerInterval, &alert.LastStatus, &alert.LastStatusAt)
2024-03-29 09:55:51 +01:00
if err != nil {
return nil, err
}
return &alert, nil
}
2024-03-31 09:50:09 +02:00
func (s *AlertsService) Create(contactPointId int64, name string, condition string, triggerInterval int64, customMessage string) (*models.AlertItem, error) {
2024-03-29 20:49:08 +01:00
alert := models.AlertItem{
2024-03-29 09:55:51 +01:00
ContactPointId: contactPointId,
Name: name,
Condition: condition,
TriggerInterval: triggerInterval,
2024-03-31 09:50:09 +02:00
CustomMessage: customMessage,
2024-03-29 09:55:51 +01:00
LastStatus: "good",
LastStatusAt: 0,
}
res, err := s.ctx.DB.Exec(
2024-03-31 09:50:09 +02:00
"INSERT INTO alerts (contact_point_id, name, condition, trigger_interval, last_status, last_status_at, custom_message) VALUES (?, ?, ?, ?, ?, ?, ?)",
alert.ContactPointId, alert.Name, alert.Condition, alert.TriggerInterval, alert.LastStatus, alert.LastStatusAt, alert.CustomMessage,
2024-03-29 09:55:51 +01:00
)
if err != nil {
return nil, err
}
alert.Id, err = res.LastInsertId()
if err != nil {
return nil, err
}
return &alert, nil
}
func (s *AlertsService) DeleteById(id int64) error {
_, err := s.ctx.DB.Exec("DELETE FROM alerts WHERE id = ?", id)
if err != nil {
return err
}
return nil
}
2024-03-31 09:50:09 +02:00
func (s *AlertsService) Update(id int64, contactPointId int64, name string, condition string, triggerInterval int64, customMessage string) (*models.AlertItem, error) {
2024-03-29 20:49:08 +01:00
alert := models.AlertItem{
2024-03-29 09:55:51 +01:00
Id: id,
ContactPointId: contactPointId,
Name: name,
Condition: condition,
TriggerInterval: triggerInterval,
2024-03-31 09:50:09 +02:00
CustomMessage: customMessage,
2024-03-29 09:55:51 +01:00
}
_, err := s.ctx.DB.Exec(
2024-03-31 09:50:09 +02:00
"UPDATE alerts SET contact_point_id = ?, name = ?, condition = ?, trigger_interval = ?, custom_message = ? WHERE id = ?",
alert.ContactPointId, alert.Name, alert.Condition, alert.TriggerInterval, alert.CustomMessage, alert.Id,
2024-03-29 09:55:51 +01:00
)
if err != nil {
return nil, err
}
return &alert, nil
}