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 }