Files
qb/mqe/instant.go
2026-06-19 07:25:46 +03:00

227 lines
4.7 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 mqe
import (
"database/sql"
"fmt"
"time"
)
type CurrentValue struct {
MetricID uint32
Time int64 `json:"t"`
Value float64 `json:"v"`
}
func (s *MeasureQueryEngine) ListCurrentValues(metricIDs []uint32) (result []CurrentValue, err error) {
driver := s.db.Driver()
for _, metricID := range metricIDs {
var rows *sql.Rows
rows, err = driver.Query(`
SELECT tm, value
FROM f64
WHERE metricID=?
ORDER BY tm DESC
LIMIT 1`,
metricID)
if err != nil {
if err == sql.ErrNoRows {
err = nil
}
return
}
for rows.Next() {
var (
tm int64
value float64
)
err = rows.Scan(&tm, &value)
if err != nil {
return
}
result = append(result, CurrentValue{
MetricID: metricID,
Time: tm,
Value: value,
})
}
if err = rows.Err(); err != nil {
return
}
rows.Close()
}
return
}
// ListInstantMeasures - cписок показаний мгновенных метрик (Температура, Давление, Расход)
// за за интервал без группировки
func (s *MeasureQueryEngine) listInstantMeasures(req MetricMeasuresFilter) (_ []Measure, err error) {
rows, err := s.db.Driver().Query(`
SELECT tm, value
FROM f64
WHERE metricID=? AND tm BETWEEN ? AND ?
ORDER BY tm ASC`,
req.MetricID, req.Since, req.Until)
if err != nil {
if err == sql.ErrNoRows {
err = nil
}
return
}
defer rows.Close()
var result []Measure
for rows.Next() {
var (
tm int64
value float64
)
err = rows.Scan(&tm, &value)
if err != nil {
return
}
result = append(result, Measure{
Time: tm,
Values: []float64{
value,
},
})
}
if err = rows.Err(); err != nil {
return
}
return result, nil
}
type AggregatedInstantMeasuresIn struct {
MetricID int64 `json:"metricID"`
Since int64 `json:"since"`
Until int64 `json:"until"`
GroupBy GroupBy `json:"groupBy"`
Flags byte `json:"flags"` // из настроек по умолчанию либо из запроса
LastDayOfMonth int `json:"lastDayOfMonth"` // например, конец месяца 25 число
FirstHourOfDay int `json:"firstHourOfDay"` // например день начинается в 6:00
}
// Заполняет поля Period и Values (от 1 до 3 значений) в структуре AggregatedMeasure
func (s *MeasureQueryEngine) listAggregatedInstantMeasures(req AggregatedInstantMeasuresIn) (_ []AggregatedMeasure, err error) {
var (
groupByFormat string
parsePeriodLayout string
result []AggregatedMeasure
)
switch req.GroupBy {
case ByHour:
groupByFormat = "%Y%m%d%H"
parsePeriodLayout = hourPeriodLayout
case ByDay:
groupByFormat = "%Y%m%d"
parsePeriodLayout = dayPeriodLayout
case ByMonth:
groupByFormat = "%Y%m"
parsePeriodLayout = monthPeriodLayout
default:
// Защита от дурака
err = fmt.Errorf("unknown GroupBy: %s", req.GroupBy)
return
}
valuesQty := 0
values := ""
if (req.Flags & AggregateMin) == AggregateMin {
values += " MIN(value),"
valuesQty++
}
if (req.Flags & AggregateMax) == AggregateMax {
values += " MAX(value),"
valuesQty++
}
if (req.Flags & AggregateAvg) == AggregateAvg {
values += " AVG(value),"
valuesQty++
}
dt, err := getPeriodDateTime(req.GroupBy, req.LastDayOfMonth, req.FirstHourOfDay)
if err != nil {
return
}
query := fmt.Sprintf(`
SELECT%s DATE_FORMAT(%s, '%s') as periodStr
FROM f64
WHERE metricID=? AND tm BETWEEN ? AND ?
GROUP BY periodStr
ORDER BY periodStr ASC`,
values, dt, groupByFormat)
rows, err := s.db.Driver().Query(query, req.MetricID, req.Since, req.Until)
if err != nil {
if err == sql.ErrNoRows {
err = nil
}
return
}
for rows.Next() {
var (
m = AggregatedMeasure{
Values: make([]float64, valuesQty),
}
periodStr string
period time.Time
valueIdx int
dest []interface{}
)
if (req.Flags & AggregateMin) == AggregateMin {
dest = append(dest, &m.Values[valueIdx])
valueIdx++
}
if (req.Flags & AggregateMax) == AggregateMax {
dest = append(dest, &m.Values[valueIdx])
valueIdx++
}
if (req.Flags & AggregateAvg) == AggregateAvg {
dest = append(dest, &m.Values[valueIdx])
}
dest = append(dest, &periodStr)
err = rows.Scan(dest...)
if err != nil {
return
}
period, err = time.ParseInLocation(parsePeriodLayout, periodStr, s.location)
if err != nil {
err = fmt.Errorf("time.ParseInLocation: %s; layout=%q; str=%q",
err, parsePeriodLayout, periodStr)
return
}
m.Period = period.Unix()
result = append(result, m)
}
if err = rows.Err(); err != nil {
return
}
return result, nil
}