|
|
@@ -384,7 +384,8 @@ func GetTotalRealtimePower(deviceIDs []string) (float64, error) {
|
|
|
total += val.Float64
|
|
|
}
|
|
|
}
|
|
|
- return total, nil
|
|
|
+ // Convert W to kW
|
|
|
+ return total / 1000.0, nil
|
|
|
}
|
|
|
|
|
|
// GetActiveDeviceCount returns the number of unique devices that reported data in the last duration
|
|
|
@@ -393,10 +394,10 @@ func GetActiveDeviceCount(duration time.Duration) (int64, error) {
|
|
|
return 0, fmt.Errorf("TDengine not initialized")
|
|
|
}
|
|
|
|
|
|
- startTime := time.Now().Add(-duration).Format("2006-01-02 15:04:05")
|
|
|
+ startTime := time.Now().Add(-duration).UnixMilli()
|
|
|
|
|
|
// Query distinct device_id from readings table in the last duration
|
|
|
- query := fmt.Sprintf(`SELECT COUNT(DISTINCT device_id) FROM readings WHERE ts > '%s'`, startTime)
|
|
|
+ query := fmt.Sprintf(`SELECT COUNT(DISTINCT device_id) FROM readings WHERE ts > %d`, startTime)
|
|
|
|
|
|
rows, err := TD.Query(query)
|
|
|
if err != nil {
|
|
|
@@ -415,6 +416,8 @@ func GetActiveDeviceCount(duration time.Duration) (int64, error) {
|
|
|
}
|
|
|
|
|
|
// GetDailyEnergyTrend returns daily energy consumption for the last N days
|
|
|
+// Implementation Note: Due to TDengine 3.0 limitation (GROUP BY and INTERVAL conflict),
|
|
|
+// we iterate through each day and query sum of spreads individually.
|
|
|
func GetDailyEnergyTrend(deviceIDs []string, days int) ([]float64, []string, error) {
|
|
|
if TD == nil {
|
|
|
return nil, nil, fmt.Errorf("TDengine not initialized")
|
|
|
@@ -425,10 +428,8 @@ func GetDailyEnergyTrend(deviceIDs []string, days int) ([]float64, []string, err
|
|
|
|
|
|
// Calculate time range
|
|
|
now := time.Now()
|
|
|
- // Start from N days ago at 00:00:00
|
|
|
+ // Start from N days ago
|
|
|
startDate := now.AddDate(0, 0, -days+1)
|
|
|
- startStr := startDate.Format("2006-01-02") + " 00:00:00"
|
|
|
- endStr := now.Format("2006-01-02 15:04:05")
|
|
|
|
|
|
var ids []string
|
|
|
for _, id := range deviceIDs {
|
|
|
@@ -436,57 +437,45 @@ func GetDailyEnergyTrend(deviceIDs []string, days int) ([]float64, []string, err
|
|
|
}
|
|
|
inClause := strings.Join(ids, ", ")
|
|
|
|
|
|
- // Query daily spread (consumption) for all devices
|
|
|
- // SPREAD(val) gives the diff between max and min in the interval
|
|
|
- // SUM(SPREAD(val)) is not directly supported in one go if we group by interval first?
|
|
|
- // TDengine: SELECT SUM(val) ... GROUP BY ... won't work on spread directly if spread is agg.
|
|
|
- // Correct approach: Calculate spread per device per day, then sum up in app?
|
|
|
- // OR: If we treat 'energy' as cumulative, we can just query MAX(val) - MIN(val) for the whole group? No, that mixes devices.
|
|
|
-
|
|
|
- // Best approach with TDengine:
|
|
|
- // Select time-bucketed spread for EACH device, then sum them up in Go.
|
|
|
- // Query: SELECT SPREAD(val) FROM readings WHERE metric='energy' AND ... INTERVAL(1d) GROUP BY device_id
|
|
|
-
|
|
|
- query := fmt.Sprintf(`SELECT _wstart, SPREAD(val) FROM readings
|
|
|
- WHERE metric='energy' AND device_id IN (%s) AND ts >= '%s' AND ts <= '%s'
|
|
|
- INTERVAL(1d) GROUP BY device_id`,
|
|
|
- inClause, startStr, endStr)
|
|
|
-
|
|
|
- rows, err := TD.Query(query)
|
|
|
- if err != nil {
|
|
|
- return nil, nil, err
|
|
|
- }
|
|
|
- defer rows.Close()
|
|
|
-
|
|
|
- // Map: DateString -> Total Energy
|
|
|
- dailyMap := make(map[string]float64)
|
|
|
-
|
|
|
- for rows.Next() {
|
|
|
- var ts time.Time
|
|
|
- var val sql.NullFloat64
|
|
|
- if err := rows.Scan(&ts, &val); err == nil && val.Valid {
|
|
|
- dateKey := ts.Format("2006-01-02")
|
|
|
- dailyMap[dateKey] += val.Float64
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Prepare result arrays (ensure continuous days)
|
|
|
trend := make([]float64, days)
|
|
|
dates := make([]string, days)
|
|
|
-
|
|
|
+
|
|
|
+ // Iterate each day
|
|
|
for i := 0; i < days; i++ {
|
|
|
d := startDate.AddDate(0, 0, i)
|
|
|
- dateKey := d.Format("2006-01-02")
|
|
|
+ dates[i] = d.Format("01-02")
|
|
|
|
|
|
- // For X-Axis labels (e.g., "Mon", "01-02")
|
|
|
- // Using MM-DD for clarity
|
|
|
- dates[i] = d.Format("01-02")
|
|
|
+ dayStart := d.Format("2006-01-02") + " 00:00:00"
|
|
|
+ // End of the day (or strictly next day 00:00:00)
|
|
|
+ dayEnd := d.AddDate(0, 0, 1).Format("2006-01-02") + " 00:00:00"
|
|
|
|
|
|
- if val, ok := dailyMap[dateKey]; ok {
|
|
|
- trend[i] = val
|
|
|
- } else {
|
|
|
- trend[i] = 0
|
|
|
+ // Query: Sum of spreads for all devices in this single day
|
|
|
+ // We use a subquery approach if possible, or just sum in app
|
|
|
+ // Query: SELECT SPREAD(val) FROM readings WHERE ... AND ts >= start AND ts < end GROUP BY device_id
|
|
|
+
|
|
|
+ query := fmt.Sprintf(`SELECT SPREAD(val) FROM readings
|
|
|
+ WHERE metric='energy' AND device_id IN (%s) AND ts >= '%s' AND ts < '%s'
|
|
|
+ GROUP BY device_id`,
|
|
|
+ inClause, dayStart, dayEnd)
|
|
|
+
|
|
|
+ // log.Printf("DEBUG: Querying Day %s: %s", dates[i], query)
|
|
|
+
|
|
|
+ rows, err := TD.Query(query)
|
|
|
+ if err != nil {
|
|
|
+ log.Printf("Error querying day %s: %v", dates[i], err)
|
|
|
+ trend[i] = 0 // Treat as 0 on error
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ var dayTotal float64
|
|
|
+ for rows.Next() {
|
|
|
+ var val sql.NullFloat64
|
|
|
+ if err := rows.Scan(&val); err == nil && val.Valid {
|
|
|
+ dayTotal += val.Float64
|
|
|
+ }
|
|
|
}
|
|
|
+ rows.Close()
|
|
|
+ trend[i] = dayTotal
|
|
|
}
|
|
|
|
|
|
return trend, dates, nil
|